@tearleads/client-sdk 0.1.54 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/containers/shared/types.d.ts +1 -2
- package/data/containers/shared/types.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/container-state/types.js.map +1 -1
- package/workflows/container-contents/remoteHydration/types.d.ts +5 -4
- package/workflows/container-contents/remoteHydration/types.js.map +1 -1
- package/workflows/containers/child/runtimeCurrentGroupShare.js +5 -8
- package/workflows/containers/child/runtimeCurrentGroupShare.js.map +1 -1
- package/workflows/containers/child/sharePrincipalPolicy.d.ts +3 -3
- package/workflows/containers/child/sharePrincipalPolicy.js +1 -1
- package/workflows/containers/child/sharePrincipalPolicy.js.map +1 -1
- package/workflows/organizations/currentOrganizationMutationRetention.js +1 -1
- package/workflows/organizations/currentOrganizationMutationRetention.js.map +1 -1
- package/workflows/organizations/currentPrincipalMutationLease.js +5 -4
- package/workflows/organizations/currentPrincipalMutationLease.js.map +1 -1
- package/workflows/organizations/deleteOrganizationGroup.js +1 -1
- package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
- package/workflows/organizations/groupCreation.js +1 -1
- package/workflows/organizations/groupCreation.js.map +1 -1
- package/workflows/organizations/groupNameUniqueness.js.map +1 -1
- package/workflows/organizations/groupPolicyMutationContext.js +1 -1
- package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
- package/workflows/organizations/organizationGroupNamePolicies.js +19 -5
- package/workflows/organizations/organizationGroupNamePolicies.js.map +1 -1
- package/workflows/organizations/principalPolicyApi.d.ts +3 -3
- package/workflows/organizations/principalPolicyApi.js.map +1 -1
- package/workflows/organizations/runtimeCurrentGroupMutation.js +4 -4
- package/workflows/organizations/runtimeCurrentGroupMutation.js.map +1 -1
- package/workflows/organizations/runtimeCurrentOrganizationMutation.js +2 -2
- package/workflows/organizations/runtimeCurrentOrganizationMutation.js.map +1 -1
- package/workflows/principals/currentPrincipalPolicyReader.d.ts +12 -0
- package/workflows/principals/currentPrincipalPolicyReader.js +2 -0
- package/workflows/principals/currentPrincipalPolicyReader.js.map +1 -0
- package/workflows/principals/externalAdminPolicy.d.ts +2 -1
- package/workflows/principals/externalAdminPolicy.js +3 -3
- package/workflows/principals/externalAdminPolicy.js.map +1 -1
- package/workflows/principals/policyCache.d.ts +3 -2
- package/workflows/principals/policyCache.js +9 -9
- package/workflows/principals/policyCache.js.map +1 -1
- package/workflows/principals/principalHistoryRecoveryTypes.d.ts +3 -2
- package/workflows/principals/principalHistoryRecoveryTypes.js.map +1 -1
- package/workflows/principals/principalPolicyHistoryReader.d.ts +20 -0
- package/workflows/principals/principalPolicyHistoryReader.js +13 -0
- package/workflows/principals/principalPolicyHistoryReader.js.map +1 -0
- package/workflows/principals/principalPolicyOrganizationScope.js +1 -1
- package/workflows/principals/principalPolicyOrganizationScope.js.map +1 -1
- package/workflows/principals/principalRecoveryDirectory.js +6 -6
- package/workflows/principals/principalRecoveryDirectory.js.map +1 -1
- package/workflows/principals/principalRecoveryDiscoveryPage.js +4 -4
- package/workflows/principals/principalRecoveryDiscoveryPage.js.map +1 -1
- package/workflows/principals/publicPrincipalHistoryTypes.d.ts +2 -2
- package/workflows/principals/publicPrincipalHistoryTypes.js.map +1 -1
- package/workflows/principals/recoverPrincipalPolicyHistory.js +1 -1
- package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/recoverPublicPrincipalHistory.js +1 -1
- package/workflows/principals/recoverPublicPrincipalHistory.js.map +1 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +1 -1
- package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
- package/workflows/principals/runtimePolicyRecovery.js +4 -3
- package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
- package/workflows/principals/runtimeProjectionPolicyRecovery.js +4 -3
- package/workflows/principals/runtimeProjectionPolicyRecovery.js.map +1 -1
- package/workflows/registration/provisionedPrincipalHistory.js +1 -1
- package/workflows/registration/provisionedPrincipalHistory.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupNameUniqueness.js","sourceRoot":"","sources":["groupNameUniqueness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../data/keyingProjectionVerification/error\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"../principals/policyVerification\";\nimport { GroupMetadataUnreadableError } from \"./groupMetadataAccess\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport {\n canonicalGroupNameKey,\n type GroupPolicyNameReader,\n readGroupPolicyPayloadName,\n} from \"./principalPolicyRequest\";\n\nexport interface DirectoryGroupWalkInput {\n readonly readEncryptedName?: GroupPolicyNameReader | undefined;\n readonly apiClient: {\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n };\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly execSql: ExecSql;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}\n\ntype DirectoryGroupHead = OrganizationAuthorityDescriptor[\"groupHeads\"][number];\n\ninterface VerifiedDirectoryGroup {\n readonly bundle: PrincipalPolicyBundleResponse;\n /** Null when the signed name does not decrypt for any member. */\n readonly name: string | null;\n readonly nameKey: string | null;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\n/**\n * Loads one directory group at its committed head: from the local retained\n * bundles when they already hold that head, and from the API otherwise.\n */\nasync function loadDirectoryGroupBundle(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n) {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n head.principalId,\n );\n const cached = await loadPrincipalPolicyBundleForReference(\n input.execSql,\n head,\n localCheckpoint,\n );\n const bundle =\n cached ??\n (await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n head.principalId,\n ));\n if (!bundle) {\n throw new Error(\n `Organization directory group ${head.principalId} could not be loaded`,\n );\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n head,\n )\n ) {\n // Head drift is reachable by a concurrent admin mutation, so it is a\n // plain error to retry on, not an incident (as in the share path). The\n // typed errors stay for signature and projection failures.\n throw new Error(\n \"Organization directory group policy does not match the signed organization directory\",\n );\n }\n return { bundle, localCheckpoint };\n}\n\n/**\n * A bundle served from local retention was verified when it was retained, and\n * is verified again here on purpose: the re-check costs local CPU only, and it\n * keeps the name a group vouches for independent of what any earlier caller\n * trusted at retention time.\n */\nexport async function verifyDirectoryGroup(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n): Promise<VerifiedDirectoryGroup> {\n const { bundle, localCheckpoint } = await loadDirectoryGroupBundle(\n input,\n head,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(\n `Organization directory group signer key could not be loaded (${signerPublicKeys.error})`,\n );\n }\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: head,\n loadExternalAuthority: async () => input.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Organization directory group verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, input.descriptor);\n const name = await readDirectoryGroupName(input, bundle);\n return {\n bundle,\n name,\n nameKey: name === null ? null : canonicalGroupNameKey(name),\n policy: verified.value,\n };\n}\n\n/**\n * A verified group whose name no member can open has no name to select by. The\n * walk isolates it rather than failing every name in the organization, but it\n * still records the incident: only a dishonest admin signs such a name.\n */\nexport async function readDirectoryGroupName(\n input: Pick<\n DirectoryGroupWalkInput,\n \"readEncryptedName\" | \"reportSecurityIncident\" | \"organizationId\"\n >,\n bundle: Parameters<typeof readGroupPolicyPayloadName>[0],\n): Promise<string | null> {\n try {\n return await readGroupPolicyPayloadName(bundle, input.readEncryptedName);\n } catch (error) {\n if (!(error instanceof GroupMetadataUnreadableError)) throw error;\n await input.reportSecurityIncident(\n new KeyingVerificationError(\n \"invalid_shape\",\n \"Signed group name does not open under the cited metadata key\",\n ),\n {\n evidenceHashes: { stateHash: bundle.currentState.stateHash },\n objectId: bundle.currentState.principalId,\n objectKind: \"principal\",\n operation: \"organization.groupName\",\n organizationId: input.organizationId,\n },\n );\n return null;\n }\n}\n\n/**\n * Signed group names are the only authenticated label a member can choose a\n * group by, and the API does not make them unique within an organization. So\n * uniqueness is enforced here, where a name enters: before an admin signs a\n * new group, every group committed in the signed organization directory\n * (the reserved Admins and Members groups included) is loaded, verified\n * against its directory head, and its committed name compared by canonical\n * key. A share then needs only the target's verified name. A compromised\n * server cannot mint a signed group, so it cannot create the duplicate this\n * check refuses.\n *\n * The walk is fail-closed: one group that cannot be loaded or verified blocks\n * the creation, since an unverifiable group could carry any name. A verified\n * group whose name does not decrypt is skipped: no member can open that name,\n * so it cannot collide with one. It runs the\n * loads in parallel and retains every verified bundle, so a later creation\n * finds unchanged groups locally and fetches only those whose head moved.\n */\nconst DIRECTORY_WALK_CONCURRENCY = 4;\n\n/** Runs `work` over `items` with at most `limit` in flight, keeping order. */\nasync function mapWithConcurrency<T, R>(\n items: ReadonlyArray<T>,\n limit: number,\n work: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async () => {\n for (;;) {\n const index = next++;\n const item = items[index];\n if (index >= items.length || item === undefined) return;\n results[index] = await work(item);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n\nexport async function assertGroupNameUniqueInDirectory(\n input: DirectoryGroupWalkInput & { readonly name: string },\n): Promise<void> {\n const nameKey = canonicalGroupNameKey(input.name);\n // Each verified bundle is retained as soon as it verifies, so a failure\n // later in the walk (or a taken name) still leaves the cache warm for the\n // retry. Retention runs in a transaction on the shared connection, so the\n // writes are chained one after another while the loads stay concurrent.\n const retainedAt = new Date().toISOString();\n let retention: Promise<void> = Promise.resolve();\n const verifyAndRetain = async (\n head: DirectoryGroupHead,\n ): Promise<VerifiedDirectoryGroup> => {\n const group = await verifyDirectoryGroup(input, head);\n retention = retention.then(() =>\n retainVerifiedPrincipalPolicyBundle({\n bundle: group.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: group.policy,\n updatedAt: retainedAt,\n }),\n );\n await retention;\n return group;\n };\n const groups = await mapWithConcurrency(\n input.descriptor.groupHeads,\n DIRECTORY_WALK_CONCURRENCY,\n verifyAndRetain,\n );\n // A taken name is user input to correct, not evidence of tampering, so this\n // is a plain error: group creation runs under security-incident reporting,\n // which records every KeyingVerificationError.\n if (groups.some((group) => group.nameKey === nameKey)) {\n throw new Error(\n \"Another signed group in this organization already carries this name\",\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"groupNameUniqueness.js","sourceRoot":"","sources":["groupNameUniqueness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../data/keyingProjectionVerification/error\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { CurrentPrincipalPolicyReader } from \"../principals/currentPrincipalPolicyReader\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"../principals/policyVerification\";\nimport { GroupMetadataUnreadableError } from \"./groupMetadataAccess\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport {\n canonicalGroupNameKey,\n type GroupPolicyNameReader,\n readGroupPolicyPayloadName,\n} from \"./principalPolicyRequest\";\n\nexport interface DirectoryGroupWalkInput {\n readonly readEncryptedName?: GroupPolicyNameReader | undefined;\n readonly apiClient: CurrentPrincipalPolicyReader;\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly execSql: ExecSql;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}\n\ntype DirectoryGroupHead = OrganizationAuthorityDescriptor[\"groupHeads\"][number];\n\ninterface VerifiedDirectoryGroup {\n readonly bundle: PrincipalPolicyBundleResponse;\n /** Null when the signed name does not decrypt for any member. */\n readonly name: string | null;\n readonly nameKey: string | null;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\n/**\n * Loads one directory group at its committed head: from the local retained\n * bundles when they already hold that head, and from the API otherwise.\n */\nasync function loadDirectoryGroupBundle(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n) {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n head.principalId,\n );\n const cached = await loadPrincipalPolicyBundleForReference(\n input.execSql,\n head,\n localCheckpoint,\n );\n const bundle =\n cached ??\n (await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n head.principalId,\n ));\n if (!bundle) {\n throw new Error(\n `Organization directory group ${head.principalId} could not be loaded`,\n );\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n head,\n )\n ) {\n // Head drift is reachable by a concurrent admin mutation, so it is a\n // plain error to retry on, not an incident (as in the share path). The\n // typed errors stay for signature and projection failures.\n throw new Error(\n \"Organization directory group policy does not match the signed organization directory\",\n );\n }\n return { bundle, localCheckpoint };\n}\n\n/**\n * A bundle served from local retention was verified when it was retained, and\n * is verified again here on purpose: the re-check costs local CPU only, and it\n * keeps the name a group vouches for independent of what any earlier caller\n * trusted at retention time.\n */\nexport async function verifyDirectoryGroup(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n): Promise<VerifiedDirectoryGroup> {\n const { bundle, localCheckpoint } = await loadDirectoryGroupBundle(\n input,\n head,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(\n `Organization directory group signer key could not be loaded (${signerPublicKeys.error})`,\n );\n }\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: head,\n loadExternalAuthority: async () => input.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Organization directory group verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, input.descriptor);\n const name = await readDirectoryGroupName(input, bundle);\n return {\n bundle,\n name,\n nameKey: name === null ? null : canonicalGroupNameKey(name),\n policy: verified.value,\n };\n}\n\n/**\n * A verified group whose name no member can open has no name to select by. The\n * walk isolates it rather than failing every name in the organization, but it\n * still records the incident: only a dishonest admin signs such a name.\n */\nexport async function readDirectoryGroupName(\n input: Pick<\n DirectoryGroupWalkInput,\n \"readEncryptedName\" | \"reportSecurityIncident\" | \"organizationId\"\n >,\n bundle: Parameters<typeof readGroupPolicyPayloadName>[0],\n): Promise<string | null> {\n try {\n return await readGroupPolicyPayloadName(bundle, input.readEncryptedName);\n } catch (error) {\n if (!(error instanceof GroupMetadataUnreadableError)) throw error;\n await input.reportSecurityIncident(\n new KeyingVerificationError(\n \"invalid_shape\",\n \"Signed group name does not open under the cited metadata key\",\n ),\n {\n evidenceHashes: { stateHash: bundle.currentState.stateHash },\n objectId: bundle.currentState.principalId,\n objectKind: \"principal\",\n operation: \"organization.groupName\",\n organizationId: input.organizationId,\n },\n );\n return null;\n }\n}\n\n/**\n * Signed group names are the only authenticated label a member can choose a\n * group by, and the API does not make them unique within an organization. So\n * uniqueness is enforced here, where a name enters: before an admin signs a\n * new group, every group committed in the signed organization directory\n * (the reserved Admins and Members groups included) is loaded, verified\n * against its directory head, and its committed name compared by canonical\n * key. A share then needs only the target's verified name. A compromised\n * server cannot mint a signed group, so it cannot create the duplicate this\n * check refuses.\n *\n * The walk is fail-closed: one group that cannot be loaded or verified blocks\n * the creation, since an unverifiable group could carry any name. A verified\n * group whose name does not decrypt is skipped: no member can open that name,\n * so it cannot collide with one. It runs the\n * loads in parallel and retains every verified bundle, so a later creation\n * finds unchanged groups locally and fetches only those whose head moved.\n */\nconst DIRECTORY_WALK_CONCURRENCY = 4;\n\n/** Runs `work` over `items` with at most `limit` in flight, keeping order. */\nasync function mapWithConcurrency<T, R>(\n items: ReadonlyArray<T>,\n limit: number,\n work: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async () => {\n for (;;) {\n const index = next++;\n const item = items[index];\n if (index >= items.length || item === undefined) return;\n results[index] = await work(item);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n\nexport async function assertGroupNameUniqueInDirectory(\n input: DirectoryGroupWalkInput & { readonly name: string },\n): Promise<void> {\n const nameKey = canonicalGroupNameKey(input.name);\n // Each verified bundle is retained as soon as it verifies, so a failure\n // later in the walk (or a taken name) still leaves the cache warm for the\n // retry. Retention runs in a transaction on the shared connection, so the\n // writes are chained one after another while the loads stay concurrent.\n const retainedAt = new Date().toISOString();\n let retention: Promise<void> = Promise.resolve();\n const verifyAndRetain = async (\n head: DirectoryGroupHead,\n ): Promise<VerifiedDirectoryGroup> => {\n const group = await verifyDirectoryGroup(input, head);\n retention = retention.then(() =>\n retainVerifiedPrincipalPolicyBundle({\n bundle: group.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: group.policy,\n updatedAt: retainedAt,\n }),\n );\n await retention;\n return group;\n };\n const groups = await mapWithConcurrency(\n input.descriptor.groupHeads,\n DIRECTORY_WALK_CONCURRENCY,\n verifyAndRetain,\n );\n // A taken name is user input to correct, not evidence of tampering, so this\n // is a plain error: group creation runs under security-incident reporting,\n // which records every KeyingVerificationError.\n if (groups.some((group) => group.nameKey === nameKey)) {\n throw new Error(\n \"Another signed group in this organization already carries this name\",\n );\n }\n}\n"]}
|
|
@@ -44,8 +44,8 @@ export async function cacheGroupPolicy(input) {
|
|
|
44
44
|
export async function loadGroupPolicyMutationContext(input) {
|
|
45
45
|
await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
|
|
46
46
|
const adminPolicy = await loadOrganizationExternalAdminPolicy({
|
|
47
|
+
currentPolicyReader: input.apiClient,
|
|
47
48
|
execSql: input.execSql,
|
|
48
|
-
getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
|
|
49
49
|
organizationId: input.organizationId,
|
|
50
50
|
resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
|
|
51
51
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupPolicyMutationContext.js","sourceRoot":"","sources":["groupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n PrincipalPolicyCheckpoint,\n PrincipalPolicyExternalAuthority,\n ReferencedPrincipalHead,\n SigningKeyPair,\n} from \"@tearleads/crypto\";\nimport type {\n ContainerMutationRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n CurrentPrincipalMemberEnvelopesResponse,\n PrincipalPolicyBundleResponse,\n PrincipalPolicyMutationResponse,\n} from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { retainLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../principals/externalAdminPolicy\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\nimport {\n acknowledgeGroupPolicyState,\n assertGroupPolicyEnvelopesMatchAcknowledgement,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { FullGroupMembershipMutationInput } from \"./groupPolicyMutationEvidence\";\nimport {\n assertPrincipalPolicyCurrentStateMatchesHead,\n groupPolicyMutationHead,\n} from \"./groupPolicyMutationHead\";\nimport {\n collectGroupPolicySignerPublicKeys,\n prepareGroupPolicyVerification,\n verifyGroupPolicy,\n verifyGroupPolicyWithExternalOrganizationAdmins,\n} from \"./groupPolicyVerification\";\nimport type {\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nimport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\nexport type {\n OrganizationPrincipalPolicyApi,\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nexport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\ninterface LoadedGroupPolicyMutationContext\n extends FullGroupMembershipMutationInput {\n readonly adminGroupId: string;\n readonly adminPolicyBundle: PrincipalPolicyBundleResponse;\n readonly currentOrgAdminUserIds: readonly string[];\n readonly organizationDescriptor: VerifiedExternalAdminPolicy[\"descriptor\"];\n readonly organizationPolicyBundle: PrincipalPolicyBundleResponse;\n readonly memberGroupId: string;\n}\n\nexport async function cacheGroupPolicy(input: {\n readonly acknowledgedMemberEnvelopes?:\n | CurrentPrincipalMemberEnvelopesResponse\n | undefined;\n readonly apiClient: PrincipalPolicyReadApi;\n readonly externalAuthority?: PrincipalPolicyExternalAuthority | undefined;\n readonly execSql: ExecSql;\n readonly expectedCurrentHead?: ReferencedPrincipalHead | undefined;\n readonly groupId: string;\n readonly localPolicyCheckpoint?: PrincipalPolicyCheckpoint | null;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse> {\n const bundle = await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n\n if (!bundle) {\n throw new Error(\"Updated group policy could not be loaded\");\n }\n\n const signerPublicKeys = await collectGroupPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const verified = await verifyGroupPolicy({\n currentPolicy: bundle,\n ...(input.externalAuthority\n ? { externalAuthority: input.externalAuthority }\n : {}),\n localPolicyCheckpoint: input.localPolicyCheckpoint ?? null,\n signerPublicKeys,\n });\n if (input.expectedCurrentHead) {\n assertPrincipalPolicyCurrentStateMatchesHead(\n bundle.currentState,\n input.expectedCurrentHead,\n );\n }\n if (input.acknowledgedMemberEnvelopes) {\n assertGroupPolicyEnvelopesMatchAcknowledgement(\n input.acknowledgedMemberEnvelopes,\n bundle.currentMemberEnvelopes,\n );\n }\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [{ bundle, policy: verified }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return bundle;\n}\n\nexport async function loadGroupPolicyMutationContext(input: {\n readonly apiClient: PrincipalPolicyReadApi;\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<LoadedGroupPolicyMutationContext> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n const adminPolicy = await loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!adminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const expectedGroupHead = requireOrganizationGroupHead(\n adminPolicy.descriptor,\n input.groupId,\n );\n const currentPolicy =\n input.groupId === adminPolicy.adminGroupId\n ? adminPolicy.adminBundle\n : await input.apiClient.getCurrentPrincipalPolicy(\"group\", input.groupId);\n if (!currentPolicy) {\n throw new Error(\"Group policy could not be loaded\");\n }\n assertPrincipalPolicyCurrentStateMatchesHead(\n currentPolicy.currentState,\n expectedGroupHead,\n );\n const verification = await prepareGroupPolicyVerification({\n currentPolicy,\n execSql: input.execSql,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const isOrganizationAdminsGroup = adminPolicy.adminGroupId === input.groupId;\n const currentOrgAdminUserIds = adminPolicy.signerUserIds;\n const externalAuthority = adminPolicy.externalAuthority;\n if (!currentOrgAdminUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority is required\");\n }\n const verified = isOrganizationAdminsGroup\n ? await verifyGroupPolicy({\n currentPolicy,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n })\n : await verifyGroupPolicyWithExternalOrganizationAdmins({\n currentPolicy,\n loadExternalAuthority: async () => externalAuthority,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n });\n assertGroupMetadataBinding(currentPolicy, adminPolicy.descriptor);\n requireSignerCanManageGroup(\n currentPolicy,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [\n ...externalAdminPolicyPersistenceEntries(adminPolicy).filter(\n (entry) =>\n entry.policy.principalType !== verified.principalType ||\n entry.policy.principalId !== verified.principalId,\n ),\n { bundle: currentPolicy, policy: verified },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n });\n\n return {\n adminGroupId: adminPolicy.adminGroupId,\n adminPolicyBundle: adminPolicy.adminBundle,\n currentPolicy,\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup,\n memberGroupId: adminPolicy.memberGroupId,\n organizationDescriptor: adminPolicy.descriptor,\n organizationPolicyBundle: adminPolicy.bundle,\n ...verification,\n localPolicyCheckpoint: verified.checkpoint,\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n };\n}\n\nexport async function commitGroupPolicyMutation(input: {\n readonly retiredContainerIds?: (() => readonly string[]) | undefined;\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly groupId: string;\n readonly organizationId: string;\n readonly organizationPolicy: PrincipalPolicyBundleResponse;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n /**\n * Re-sign the batch with the descendant rekeys a refusal named woven in; the\n * result replaces `containerMutations` and the commit is retried once.\n */\n readonly carryDescendantRekeys?:\n | ((\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>)\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse> {\n const stored = await submitGroupPolicyCommit(input);\n if (!stored) {\n throw new Error(\"Group policy update failed\");\n }\n const storedPolicy = stored.groupPolicy;\n const acknowledgedPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy.currentState,\n });\n const groupBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy,\n });\n const organizationHead = await groupPolicyMutationHead(\n input.organizationRequest,\n );\n const acknowledgedOrganization = await acknowledgeGroupPolicyState({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy,\n });\n await retainLocallyAcknowledgedPrincipalPolicyBundles({\n retiredContainers: {\n containerIds: input.retiredContainerIds?.() ?? [],\n organizationId: input.organizationId,\n policy: acknowledgedPolicy,\n },\n entries: [\n { bundle: groupBundle, policy: acknowledgedPolicy },\n {\n bundle: organizationBundle,\n policy: acknowledgedOrganization,\n },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return groupBundle;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"groupPolicyMutationContext.js","sourceRoot":"","sources":["groupPolicyMutationContext.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n PrincipalPolicyCheckpoint,\n PrincipalPolicyExternalAuthority,\n ReferencedPrincipalHead,\n SigningKeyPair,\n} from \"@tearleads/crypto\";\nimport type {\n ContainerMutationRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n CurrentPrincipalMemberEnvelopesResponse,\n PrincipalPolicyBundleResponse,\n PrincipalPolicyMutationResponse,\n} from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { retainLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport { requireOrganizationGroupHead } from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../principals/externalAdminPolicy\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport { requireSignerCanManageGroup } from \"./groupMutationAuthorization\";\nimport {\n acknowledgeGroupPolicyState,\n assertGroupPolicyEnvelopesMatchAcknowledgement,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { FullGroupMembershipMutationInput } from \"./groupPolicyMutationEvidence\";\nimport {\n assertPrincipalPolicyCurrentStateMatchesHead,\n groupPolicyMutationHead,\n} from \"./groupPolicyMutationHead\";\nimport {\n collectGroupPolicySignerPublicKeys,\n prepareGroupPolicyVerification,\n verifyGroupPolicy,\n verifyGroupPolicyWithExternalOrganizationAdmins,\n} from \"./groupPolicyVerification\";\nimport type {\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nimport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\nexport type {\n OrganizationPrincipalPolicyApi,\n PrincipalPolicyReadApi,\n PrincipalPolicyReadWriteApi,\n} from \"./principalPolicyApi\";\nexport { submitGroupPolicyCommit } from \"./submitGroupPolicyCommit\";\n\ninterface LoadedGroupPolicyMutationContext\n extends FullGroupMembershipMutationInput {\n readonly adminGroupId: string;\n readonly adminPolicyBundle: PrincipalPolicyBundleResponse;\n readonly currentOrgAdminUserIds: readonly string[];\n readonly organizationDescriptor: VerifiedExternalAdminPolicy[\"descriptor\"];\n readonly organizationPolicyBundle: PrincipalPolicyBundleResponse;\n readonly memberGroupId: string;\n}\n\nexport async function cacheGroupPolicy(input: {\n readonly acknowledgedMemberEnvelopes?:\n | CurrentPrincipalMemberEnvelopesResponse\n | undefined;\n readonly apiClient: PrincipalPolicyReadApi;\n readonly externalAuthority?: PrincipalPolicyExternalAuthority | undefined;\n readonly execSql: ExecSql;\n readonly expectedCurrentHead?: ReferencedPrincipalHead | undefined;\n readonly groupId: string;\n readonly localPolicyCheckpoint?: PrincipalPolicyCheckpoint | null;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse> {\n const bundle = await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n\n if (!bundle) {\n throw new Error(\"Updated group policy could not be loaded\");\n }\n\n const signerPublicKeys = await collectGroupPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const verified = await verifyGroupPolicy({\n currentPolicy: bundle,\n ...(input.externalAuthority\n ? { externalAuthority: input.externalAuthority }\n : {}),\n localPolicyCheckpoint: input.localPolicyCheckpoint ?? null,\n signerPublicKeys,\n });\n if (input.expectedCurrentHead) {\n assertPrincipalPolicyCurrentStateMatchesHead(\n bundle.currentState,\n input.expectedCurrentHead,\n );\n }\n if (input.acknowledgedMemberEnvelopes) {\n assertGroupPolicyEnvelopesMatchAcknowledgement(\n input.acknowledgedMemberEnvelopes,\n bundle.currentMemberEnvelopes,\n );\n }\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [{ bundle, policy: verified }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return bundle;\n}\n\nexport async function loadGroupPolicyMutationContext(input: {\n readonly apiClient: PrincipalPolicyReadApi;\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n}): Promise<LoadedGroupPolicyMutationContext> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n const adminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!adminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const expectedGroupHead = requireOrganizationGroupHead(\n adminPolicy.descriptor,\n input.groupId,\n );\n const currentPolicy =\n input.groupId === adminPolicy.adminGroupId\n ? adminPolicy.adminBundle\n : await input.apiClient.getCurrentPrincipalPolicy(\"group\", input.groupId);\n if (!currentPolicy) {\n throw new Error(\"Group policy could not be loaded\");\n }\n assertPrincipalPolicyCurrentStateMatchesHead(\n currentPolicy.currentState,\n expectedGroupHead,\n );\n const verification = await prepareGroupPolicyVerification({\n currentPolicy,\n execSql: input.execSql,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n const isOrganizationAdminsGroup = adminPolicy.adminGroupId === input.groupId;\n const currentOrgAdminUserIds = adminPolicy.signerUserIds;\n const externalAuthority = adminPolicy.externalAuthority;\n if (!currentOrgAdminUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority is required\");\n }\n const verified = isOrganizationAdminsGroup\n ? await verifyGroupPolicy({\n currentPolicy,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n })\n : await verifyGroupPolicyWithExternalOrganizationAdmins({\n currentPolicy,\n loadExternalAuthority: async () => externalAuthority,\n localPolicyCheckpoint: verification.localPolicyCheckpoint,\n signerPublicKeys: verification.currentPolicySignerPublicKeys,\n });\n assertGroupMetadataBinding(currentPolicy, adminPolicy.descriptor);\n requireSignerCanManageGroup(\n currentPolicy,\n currentOrgAdminUserIds,\n input.signerUserId,\n );\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: [\n ...externalAdminPolicyPersistenceEntries(adminPolicy).filter(\n (entry) =>\n entry.policy.principalType !== verified.principalType ||\n entry.policy.principalId !== verified.principalId,\n ),\n { bundle: currentPolicy, policy: verified },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n });\n\n return {\n adminGroupId: adminPolicy.adminGroupId,\n adminPolicyBundle: adminPolicy.adminBundle,\n currentPolicy,\n currentOrgAdminUserIds,\n externalAuthority,\n isOrganizationAdminsGroup,\n memberGroupId: adminPolicy.memberGroupId,\n organizationDescriptor: adminPolicy.descriptor,\n organizationPolicyBundle: adminPolicy.bundle,\n ...verification,\n localPolicyCheckpoint: verified.checkpoint,\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n };\n}\n\nexport async function commitGroupPolicyMutation(input: {\n readonly retiredContainerIds?: (() => readonly string[]) | undefined;\n readonly apiClient: PrincipalPolicyReadWriteApi;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly groupId: string;\n readonly organizationId: string;\n readonly organizationPolicy: PrincipalPolicyBundleResponse;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n /**\n * Re-sign the batch with the descendant rekeys a refusal named woven in; the\n * result replaces `containerMutations` and the commit is retried once.\n */\n readonly carryDescendantRekeys?:\n | ((\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>)\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse> {\n const stored = await submitGroupPolicyCommit(input);\n if (!stored) {\n throw new Error(\"Group policy update failed\");\n }\n const storedPolicy = stored.groupPolicy;\n const acknowledgedPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy.currentState,\n });\n const groupBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.currentPolicy,\n expectedHead: input.expectedHead,\n request: input.request,\n response: storedPolicy,\n });\n const organizationHead = await groupPolicyMutationHead(\n input.organizationRequest,\n );\n const acknowledgedOrganization = await acknowledgeGroupPolicyState({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: input.organizationPolicy,\n expectedHead: organizationHead,\n request: input.organizationRequest,\n response: stored.organizationPolicy,\n });\n await retainLocallyAcknowledgedPrincipalPolicyBundles({\n retiredContainers: {\n containerIds: input.retiredContainerIds?.() ?? [],\n organizationId: input.organizationId,\n policy: acknowledgedPolicy,\n },\n entries: [\n { bundle: groupBundle, policy: acknowledgedPolicy },\n {\n bundle: organizationBundle,\n policy: acknowledgedOrganization,\n },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return groupBundle;\n}\n"]}
|
|
@@ -21,14 +21,28 @@ export async function loadGroupNameDirectoryAuthority(input) {
|
|
|
21
21
|
return null;
|
|
22
22
|
return loadOrganizationExternalAdminPolicy({
|
|
23
23
|
...input,
|
|
24
|
+
currentPolicyReader: directoryPinnedPolicyReader({
|
|
25
|
+
load,
|
|
26
|
+
organization,
|
|
27
|
+
organizationId: input.organizationId,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Serves the organization bundle already read and each directory group at the
|
|
33
|
+
* head that organization's directory pins, so the authority check reads the
|
|
34
|
+
* heads the projection cited rather than whatever the server holds now.
|
|
35
|
+
*/
|
|
36
|
+
function directoryPinnedPolicyReader(input) {
|
|
37
|
+
return {
|
|
24
38
|
getCurrentPrincipalPolicy: async (type, id) => {
|
|
25
39
|
if (type === "organization")
|
|
26
|
-
return id === input.organizationId ? organization : null;
|
|
27
|
-
//
|
|
28
|
-
const descriptor = parseOrganizationAuthorityDescriptor(organization.currentPayload.ciphertext);
|
|
40
|
+
return id === input.organizationId ? input.organization : null;
|
|
41
|
+
// Reached only after the organization signature and scope checks.
|
|
42
|
+
const descriptor = parseOrganizationAuthorityDescriptor(input.organization.currentPayload.ciphertext);
|
|
29
43
|
const head = descriptor.groupHeads.find((head) => head.principalId === id);
|
|
30
|
-
return head ? load(head) : null;
|
|
44
|
+
return head ? input.load(head) : null;
|
|
31
45
|
},
|
|
32
|
-
}
|
|
46
|
+
};
|
|
33
47
|
}
|
|
34
48
|
//# sourceMappingURL=organizationGroupNamePolicies.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationGroupNamePolicies.js","sourceRoot":"","sources":["organizationGroupNamePolicies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"organizationGroupNamePolicies.js","sourceRoot":"","sources":["organizationGroupNamePolicies.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,qCAAqC,EAAE,MAAM,4DAA4D,CAAC;AACnH,OAAO,EACL,oCAAoC,EACpC,6BAA6B,GAC9B,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,kCAAkC,EAAE,MAAM,mDAAmD,CAAC;AAEvG,OAAO,EAAE,mCAAmC,EAAE,MAAM,mCAAmC,CAAC;AAWxF,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,+BAA+B,CAAC,KAAY;IAChE,MAAM,IAAI,GAAG,KAAK,EAAE,SAAkC,EAAE,EAAE;QACxD,MAAM,UAAU,GAAG,MAAM,6BAA6B,CACpD,KAAK,CAAC,OAAO,EACb,SAAS,CAAC,aAAa,EACvB,SAAS,CAAC,WAAW,CACtB,CAAC;QACF,MAAM,MAAM,GACV,CAAC,MAAM,qCAAqC,CAC1C,KAAK,CAAC,OAAO,EACb,SAAS,EACT,UAAU,CACX,CAAC;YACF,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,yBAAyB,CAC9C,SAAS,CAAC,aAAa,EACvB,SAAS,CAAC,WAAW,CACtB,CAAC,CAAC;QACL,IACE,MAAM;YACN,CAAC,6BAA6B,CAC5B,kCAAkC,CAAC,MAAM,CAAC,EAC1C,SAAS,CACV;YAED,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;QACJ,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,YAAY,GAAG,KAAK,CAAC,2BAA2B;QACpD,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC;QAC/C,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,yBAAyB,CAC7C,cAAc,EACd,KAAK,CAAC,cAAc,CACrB,CAAC;IACN,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO,mCAAmC,CAAC;QACzC,GAAG,KAAK;QACR,mBAAmB,EAAE,2BAA2B,CAAC;YAC/C,IAAI;YACJ,YAAY;YACZ,cAAc,EAAE,KAAK,CAAC,cAAc;SACrC,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,2BAA2B,CAAC,KAMpC;IACC,OAAO;QACL,yBAAyB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;YAC5C,IAAI,IAAI,KAAK,cAAc;gBACzB,OAAO,EAAE,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;YACjE,kEAAkE;YAClE,MAAM,UAAU,GAAG,oCAAoC,CACrD,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,UAAU,CAC7C,CAAC;YACF,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,KAAK,EAAE,CAClC,CAAC;YACF,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport {\n parseOrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { CurrentPrincipalPolicyReader } from \"../principals/currentPrincipalPolicyReader\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport type { DirectoryGroupWalkInput } from \"./groupNameUniqueness\";\n\ntype Input = Pick<\n DirectoryGroupWalkInput,\n \"apiClient\" | \"execSql\" | \"organizationId\" | \"resolveTrustedUserIdentity\"\n> & {\n readonly organizationPolicyReference?: ReferencedPrincipalHead | null;\n readonly stillCurrent: () => boolean;\n};\n\n/** Cached directory policies are reusable only at the exact projected head. */\nexport async function loadGroupNameDirectoryAuthority(input: Input) {\n const load = async (reference: ReferencedPrincipalHead) => {\n const checkpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n reference.principalType,\n reference.principalId,\n );\n const bundle =\n (await loadPrincipalPolicyBundleForReference(\n input.execSql,\n reference,\n checkpoint,\n )) ??\n (await input.apiClient.getCurrentPrincipalPolicy(\n reference.principalType,\n reference.principalId,\n ));\n if (\n bundle &&\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n reference,\n )\n )\n throw new Error(\n \"Group name authority does not match the projected directory head\",\n );\n return bundle;\n };\n const organization = input.organizationPolicyReference\n ? await load(input.organizationPolicyReference)\n : await input.apiClient.getCurrentPrincipalPolicy(\n \"organization\",\n input.organizationId,\n );\n if (!organization) return null;\n return loadOrganizationExternalAdminPolicy({\n ...input,\n currentPolicyReader: directoryPinnedPolicyReader({\n load,\n organization,\n organizationId: input.organizationId,\n }),\n });\n}\n\n/**\n * Serves the organization bundle already read and each directory group at the\n * head that organization's directory pins, so the authority check reads the\n * heads the projection cited rather than whatever the server holds now.\n */\nfunction directoryPinnedPolicyReader(input: {\n readonly load: (\n reference: ReferencedPrincipalHead,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n readonly organization: PrincipalPolicyBundleResponse;\n readonly organizationId: string;\n}): CurrentPrincipalPolicyReader {\n return {\n getCurrentPrincipalPolicy: async (type, id) => {\n if (type === \"organization\")\n return id === input.organizationId ? input.organization : null;\n // Reached only after the organization signature and scope checks.\n const descriptor = parseOrganizationAuthorityDescriptor(\n input.organization.currentPayload.ciphertext,\n );\n const head = descriptor.groupHeads.find(\n (head) => head.principalId === id,\n );\n return head ? input.load(head) : null;\n },\n };\n}\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { RequestFailureKind } from "../../internal/api-client/index.js";
|
|
2
2
|
import type { CommitOrganizationGroupPolicyRequest, CreateOrganizationGroupWithPolicyRequest } from "../../internal/validators/request/index.js";
|
|
3
|
-
import type { CommitOrganizationGroupPolicyResponse, CreateOrganizationGroupResponse
|
|
4
|
-
|
|
3
|
+
import type { CommitOrganizationGroupPolicyResponse, CreateOrganizationGroupResponse } from "../../internal/validators/response/index.js";
|
|
4
|
+
import type { CurrentPrincipalPolicyReader } from "../principals/currentPrincipalPolicyReader.js";
|
|
5
|
+
export interface PrincipalPolicyReadApi extends CurrentPrincipalPolicyReader {
|
|
5
6
|
/** Resolve authored work before reading the state for a new mutation. */
|
|
6
7
|
readonly recoverPendingPrincipalMutation?: ((organizationId: string) => Promise<void>) | undefined;
|
|
7
|
-
getCurrentPrincipalPolicy: (principalType: "group" | "organization", principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
|
|
8
8
|
}
|
|
9
9
|
export interface PrincipalPolicyReadWriteApi extends PrincipalPolicyReadApi {
|
|
10
10
|
commitOrganizationGroupPolicy: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest) => Promise<CommitOrganizationGroupPolicyResponse | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalPolicyApi.js","sourceRoot":"","sources":["principalPolicyApi.ts"],"names":[],"mappings":"","sourcesContent":["import type { RequestFailureKind } from \"@tearleads/api-client\";\nimport type {\n CommitOrganizationGroupPolicyRequest,\n CreateOrganizationGroupWithPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n CreateOrganizationGroupResponse,\n
|
|
1
|
+
{"version":3,"file":"principalPolicyApi.js","sourceRoot":"","sources":["principalPolicyApi.ts"],"names":[],"mappings":"","sourcesContent":["import type { RequestFailureKind } from \"@tearleads/api-client\";\nimport type {\n CommitOrganizationGroupPolicyRequest,\n CreateOrganizationGroupWithPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n CreateOrganizationGroupResponse,\n} from \"@tearleads/validators/response\";\nimport type { CurrentPrincipalPolicyReader } from \"../principals/currentPrincipalPolicyReader\";\n\nexport interface PrincipalPolicyReadApi extends CurrentPrincipalPolicyReader {\n /** Resolve authored work before reading the state for a new mutation. */\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n}\n\nexport interface PrincipalPolicyReadWriteApi extends PrincipalPolicyReadApi {\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n /**\n * Optional status-bearing variant. A rematerialized rotation the server\n * refuses for stranding a granted path names the descendant rekeys the batch\n * must carry; without this the refusal is a plain failure.\n */\n commitOrganizationGroupPolicyResult?: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n options?: { readonly reportErrors?: boolean | undefined },\n ) => Promise<\n | {\n readonly ok: true;\n readonly data: CommitOrganizationGroupPolicyResponse;\n }\n | {\n readonly ok: false;\n readonly kind?: RequestFailureKind | undefined;\n readonly message?: string | undefined;\n readonly code?: string | undefined;\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n }\n >;\n}\n\nexport interface OrganizationPrincipalPolicyApi extends PrincipalPolicyReadApi {\n createOrganizationGroup: (\n organizationId: string,\n input: CreateOrganizationGroupWithPolicyRequest,\n ) => Promise<CreateOrganizationGroupResponse | null>;\n}\n"]}
|
|
@@ -13,7 +13,7 @@ export function createRuntimeCurrentGroupMutation(runtime) {
|
|
|
13
13
|
return undefined;
|
|
14
14
|
return async (input, work) => {
|
|
15
15
|
input = { ...input };
|
|
16
|
-
return lease(input.stillCurrent, async ({ protection, stillCurrent, resolveCurrentPolicy,
|
|
16
|
+
return lease(input.stillCurrent, async ({ protection, stillCurrent, resolveCurrentPolicy, historyReader, directoryRecovery, }) => {
|
|
17
17
|
const loadGroup = async (groupId) => {
|
|
18
18
|
const context = await loadCurrentGroupPolicyMutationContext({
|
|
19
19
|
...input,
|
|
@@ -23,7 +23,7 @@ export function createRuntimeCurrentGroupMutation(runtime) {
|
|
|
23
23
|
recoverPendingPrincipalMutation: (organizationId) => runtime.apiClient.recoverPendingPrincipalMutation(organizationId),
|
|
24
24
|
stillCurrent,
|
|
25
25
|
});
|
|
26
|
-
const retention = await createCurrentMutationRetention(runtime, { ...input, groupId }, context, protection,
|
|
26
|
+
const retention = await createCurrentMutationRetention(runtime, { ...input, groupId }, context, protection, historyReader, directoryRecovery(context.organizationCurrent.current));
|
|
27
27
|
return {
|
|
28
28
|
...context,
|
|
29
29
|
...retention,
|
|
@@ -44,9 +44,9 @@ export function createRuntimeCurrentGroupMutation(runtime) {
|
|
|
44
44
|
});
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
async function createCurrentMutationRetention(runtime, input, context, protection,
|
|
47
|
+
async function createCurrentMutationRetention(runtime, input, context, protection, historyReader, directoryRecovery) {
|
|
48
48
|
const common = {
|
|
49
|
-
|
|
49
|
+
historyReader,
|
|
50
50
|
resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,
|
|
51
51
|
};
|
|
52
52
|
const groupRecovery = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeCurrentGroupMutation.js","sourceRoot":"","sources":["runtimeCurrentGroupMutation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { PrincipalPolicyHistoryProgressOptions } from \"@tearleads/crypto\";\nimport type { PutPrincipalPolicyRequest } from \"@tearleads/validators/request\";\nimport type { CommitOrganizationGroupPolicyResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { CurrentPolicyReferenceResolver } from \"../../data/principals/currentPolicyReferenceResolver\";\nimport { scopedGroupHistoryProtection } from \"../../data/principals/principalHistoryScopeProtection\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { PrincipalPolicyRecoveryRuntime } from \"../principals/runtimePolicyRecovery\";\nimport { captureAcknowledgedPrincipalPredecessor } from \"./acknowledgedPrincipalPredecessor\";\nimport { resolveCurrentGroupMutationReferences } from \"./currentGroupMutationReferences\";\nimport { loadCurrentGroupPolicyMutationContext } from \"./currentGroupPolicyMutationContext\";\nimport { createCurrentPrincipalMutationLease } from \"./currentPrincipalMutationLease\";\nimport type { PrincipalMutationRecoveryApi } from \"./principalMutationJournalManagement\";\nimport {\n type AcknowledgedPrincipalCurrentInput,\n retainAcknowledgedPrincipalCurrents,\n} from \"./retainAcknowledgedPrincipalCurrents\";\n\ntype MutationRuntime = PrincipalPolicyRecoveryRuntime & {\n readonly apiClient: PrincipalPolicyRecoveryRuntime[\"apiClient\"] &\n Pick<PrincipalMutationRecoveryApi, \"recoverPendingPrincipalMutation\">;\n};\n\ntype CurrentMutationContext = Awaited<\n ReturnType<typeof loadCurrentGroupPolicyMutationContext>\n>;\n\nexport type RuntimeCurrentGroupMutationContext = CurrentMutationContext & {\n readonly resolveAuthoredPolicyReferences: CurrentPolicyReferenceResolver;\n readonly loadGroup: (\n groupId: string,\n ) => Promise<RuntimeCurrentGroupMutationContext>;\n readonly groupRetention: Pick<\n AcknowledgedPrincipalCurrentInput,\n \"recovery\" | \"predecessor\"\n >;\n readonly retainAcknowledged: (\n receipt: CurrentMutationReceipt,\n ) => Promise<void>;\n};\n\ninterface CurrentMutationReceipt {\n readonly additionalGroups?:\n | readonly (AcknowledgedPrincipalCurrentInput & {\n readonly retiredContainerIds?: readonly string[] | undefined;\n })[]\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n readonly response: CommitOrganizationGroupPolicyResponse;\n readonly retiredContainerIds?: readonly string[] | undefined;\n}\n\ninterface MutationInput {\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly stillCurrent: () => boolean;\n}\n\n/** Keep private prefix custody inside the entire mutation and its acknowledgement. */\nexport function createRuntimeCurrentGroupMutation(runtime: MutationRuntime) {\n const lease = createCurrentPrincipalMutationLease(runtime);\n if (!lease) return undefined;\n return async <T>(\n input: MutationInput,\n work: (context: RuntimeCurrentGroupMutationContext) => Promise<T>,\n ): Promise<T> => {\n input = { ...input };\n return lease(\n input.stillCurrent,\n async ({\n protection,\n stillCurrent,\n resolveCurrentPolicy,\n readPages,\n directoryRecovery,\n }) => {\n const loadGroup = async (\n groupId: string,\n ): Promise<RuntimeCurrentGroupMutationContext> => {\n const context = await loadCurrentGroupPolicyMutationContext({\n ...input,\n groupId,\n execSql: runtime.infra.execSql,\n resolveCurrentPolicy,\n recoverPendingPrincipalMutation: (organizationId) =>\n runtime.apiClient.recoverPendingPrincipalMutation(organizationId),\n stillCurrent,\n });\n const retention = await createCurrentMutationRetention(\n runtime,\n { ...input, groupId },\n context,\n protection,\n readPages,\n directoryRecovery(context.organizationCurrent.current),\n );\n return {\n ...context,\n ...retention,\n loadGroup,\n resolveAuthoredPolicyReferences: (current, references) =>\n resolveCurrentGroupMutationReferences({\n current,\n predecessor: context.verifiedCurrentPolicy,\n references,\n readPredecessorReference: context.readPredecessorReference,\n stillCurrent: context.stillCurrent,\n }),\n };\n };\n const context = await loadGroup(input.groupId);\n const result = await work(context);\n assertProjectionVerificationCurrent(context.stillCurrent);\n return result;\n },\n );\n };\n}\n\nasync function createCurrentMutationRetention(\n runtime: MutationRuntime,\n input: MutationInput,\n context: CurrentMutationContext,\n protection: PrincipalPolicyHistoryProgressOptions,\n readPages: ApiClient[\"getPrincipalPolicyPages\"],\n directoryRecovery: AcknowledgedPrincipalCurrentInput[\"recovery\"],\n) {\n const common = {\n apiClient: { getPrincipalPolicyPages: readPages },\n resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,\n };\n const groupRecovery: AcknowledgedPrincipalCurrentInput[\"recovery\"] = {\n ...common,\n expectedHead: principalPolicyReferenceFromBundle(context.currentPolicy),\n protection: context.isOrganizationAdminsGroup\n ? protection\n : scopedGroupHistoryProtection(\n protection,\n input.organizationId,\n context.adminGroupId,\n ),\n historyVerification: context.isOrganizationAdminsGroup\n ? \"direct-admins\"\n : \"standard\",\n ...(context.isOrganizationAdminsGroup\n ? {}\n : { loadExternalAuthority: async () => context.externalAuthority }),\n };\n const capture = (recovery: AcknowledgedPrincipalCurrentInput[\"recovery\"]) =>\n captureAcknowledgedPrincipalPredecessor({\n ...recovery,\n execSql: runtime.infra.execSql,\n organizationId: input.organizationId,\n stillCurrent: context.stillCurrent,\n });\n const groupPredecessor = await capture(groupRecovery);\n const directoryPredecessor = await capture(directoryRecovery);\n const retainAcknowledged = async (\n receipt: CurrentMutationReceipt,\n ): Promise<void> => {\n assertProjectionVerificationCurrent(context.stillCurrent);\n await retainAcknowledgedPrincipalCurrents({\n execSql: runtime.infra.execSql,\n organizationId: input.organizationId,\n entries: [\n ...(receipt.additionalGroups ?? []),\n {\n recovery: groupRecovery,\n predecessor: groupPredecessor,\n request: receipt.request,\n response: receipt.response.groupPolicy,\n },\n {\n recovery: directoryRecovery,\n predecessor: directoryPredecessor,\n request: receipt.organizationRequest,\n response: receipt.response.organizationPolicy,\n },\n ],\n retirements: [\n ...(receipt.additionalGroups ?? []).map((entry) => ({\n principalId: entry.request.state.principalId,\n principalType: \"group\" as const,\n containerIds: entry.retiredContainerIds ?? [],\n })),\n {\n principalId: input.groupId,\n principalType: \"group\",\n containerIds: receipt.retiredContainerIds ?? [],\n },\n ],\n stillCurrent: context.stillCurrent,\n });\n };\n return {\n groupRetention: { recovery: groupRecovery, predecessor: groupPredecessor },\n retainAcknowledged,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtimeCurrentGroupMutation.js","sourceRoot":"","sources":["runtimeCurrentGroupMutation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PrincipalPolicyHistoryProgressOptions } from \"@tearleads/crypto\";\nimport type { PutPrincipalPolicyRequest } from \"@tearleads/validators/request\";\nimport type { CommitOrganizationGroupPolicyResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { CurrentPolicyReferenceResolver } from \"../../data/principals/currentPolicyReferenceResolver\";\nimport { scopedGroupHistoryProtection } from \"../../data/principals/principalHistoryScopeProtection\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { PrincipalPolicyPageReader } from \"../principals/principalPolicyHistoryReader\";\nimport type { PrincipalPolicyRecoveryRuntime } from \"../principals/runtimePolicyRecovery\";\nimport { captureAcknowledgedPrincipalPredecessor } from \"./acknowledgedPrincipalPredecessor\";\nimport { resolveCurrentGroupMutationReferences } from \"./currentGroupMutationReferences\";\nimport { loadCurrentGroupPolicyMutationContext } from \"./currentGroupPolicyMutationContext\";\nimport { createCurrentPrincipalMutationLease } from \"./currentPrincipalMutationLease\";\nimport type { PrincipalMutationRecoveryApi } from \"./principalMutationJournalManagement\";\nimport {\n type AcknowledgedPrincipalCurrentInput,\n retainAcknowledgedPrincipalCurrents,\n} from \"./retainAcknowledgedPrincipalCurrents\";\n\ntype MutationRuntime = PrincipalPolicyRecoveryRuntime & {\n readonly apiClient: PrincipalPolicyRecoveryRuntime[\"apiClient\"] &\n Pick<PrincipalMutationRecoveryApi, \"recoverPendingPrincipalMutation\">;\n};\n\ntype CurrentMutationContext = Awaited<\n ReturnType<typeof loadCurrentGroupPolicyMutationContext>\n>;\n\nexport type RuntimeCurrentGroupMutationContext = CurrentMutationContext & {\n readonly resolveAuthoredPolicyReferences: CurrentPolicyReferenceResolver;\n readonly loadGroup: (\n groupId: string,\n ) => Promise<RuntimeCurrentGroupMutationContext>;\n readonly groupRetention: Pick<\n AcknowledgedPrincipalCurrentInput,\n \"recovery\" | \"predecessor\"\n >;\n readonly retainAcknowledged: (\n receipt: CurrentMutationReceipt,\n ) => Promise<void>;\n};\n\ninterface CurrentMutationReceipt {\n readonly additionalGroups?:\n | readonly (AcknowledgedPrincipalCurrentInput & {\n readonly retiredContainerIds?: readonly string[] | undefined;\n })[]\n | undefined;\n readonly request: PutPrincipalPolicyRequest;\n readonly organizationRequest: PutPrincipalPolicyRequest;\n readonly response: CommitOrganizationGroupPolicyResponse;\n readonly retiredContainerIds?: readonly string[] | undefined;\n}\n\ninterface MutationInput {\n readonly groupId: string;\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly stillCurrent: () => boolean;\n}\n\n/** Keep private prefix custody inside the entire mutation and its acknowledgement. */\nexport function createRuntimeCurrentGroupMutation(runtime: MutationRuntime) {\n const lease = createCurrentPrincipalMutationLease(runtime);\n if (!lease) return undefined;\n return async <T>(\n input: MutationInput,\n work: (context: RuntimeCurrentGroupMutationContext) => Promise<T>,\n ): Promise<T> => {\n input = { ...input };\n return lease(\n input.stillCurrent,\n async ({\n protection,\n stillCurrent,\n resolveCurrentPolicy,\n historyReader,\n directoryRecovery,\n }) => {\n const loadGroup = async (\n groupId: string,\n ): Promise<RuntimeCurrentGroupMutationContext> => {\n const context = await loadCurrentGroupPolicyMutationContext({\n ...input,\n groupId,\n execSql: runtime.infra.execSql,\n resolveCurrentPolicy,\n recoverPendingPrincipalMutation: (organizationId) =>\n runtime.apiClient.recoverPendingPrincipalMutation(organizationId),\n stillCurrent,\n });\n const retention = await createCurrentMutationRetention(\n runtime,\n { ...input, groupId },\n context,\n protection,\n historyReader,\n directoryRecovery(context.organizationCurrent.current),\n );\n return {\n ...context,\n ...retention,\n loadGroup,\n resolveAuthoredPolicyReferences: (current, references) =>\n resolveCurrentGroupMutationReferences({\n current,\n predecessor: context.verifiedCurrentPolicy,\n references,\n readPredecessorReference: context.readPredecessorReference,\n stillCurrent: context.stillCurrent,\n }),\n };\n };\n const context = await loadGroup(input.groupId);\n const result = await work(context);\n assertProjectionVerificationCurrent(context.stillCurrent);\n return result;\n },\n );\n };\n}\n\nasync function createCurrentMutationRetention(\n runtime: MutationRuntime,\n input: MutationInput,\n context: CurrentMutationContext,\n protection: PrincipalPolicyHistoryProgressOptions,\n historyReader: PrincipalPolicyPageReader,\n directoryRecovery: AcknowledgedPrincipalCurrentInput[\"recovery\"],\n) {\n const common = {\n historyReader,\n resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,\n };\n const groupRecovery: AcknowledgedPrincipalCurrentInput[\"recovery\"] = {\n ...common,\n expectedHead: principalPolicyReferenceFromBundle(context.currentPolicy),\n protection: context.isOrganizationAdminsGroup\n ? protection\n : scopedGroupHistoryProtection(\n protection,\n input.organizationId,\n context.adminGroupId,\n ),\n historyVerification: context.isOrganizationAdminsGroup\n ? \"direct-admins\"\n : \"standard\",\n ...(context.isOrganizationAdminsGroup\n ? {}\n : { loadExternalAuthority: async () => context.externalAuthority }),\n };\n const capture = (recovery: AcknowledgedPrincipalCurrentInput[\"recovery\"]) =>\n captureAcknowledgedPrincipalPredecessor({\n ...recovery,\n execSql: runtime.infra.execSql,\n organizationId: input.organizationId,\n stillCurrent: context.stillCurrent,\n });\n const groupPredecessor = await capture(groupRecovery);\n const directoryPredecessor = await capture(directoryRecovery);\n const retainAcknowledged = async (\n receipt: CurrentMutationReceipt,\n ): Promise<void> => {\n assertProjectionVerificationCurrent(context.stillCurrent);\n await retainAcknowledgedPrincipalCurrents({\n execSql: runtime.infra.execSql,\n organizationId: input.organizationId,\n entries: [\n ...(receipt.additionalGroups ?? []),\n {\n recovery: groupRecovery,\n predecessor: groupPredecessor,\n request: receipt.request,\n response: receipt.response.groupPolicy,\n },\n {\n recovery: directoryRecovery,\n predecessor: directoryPredecessor,\n request: receipt.organizationRequest,\n response: receipt.response.organizationPolicy,\n },\n ],\n retirements: [\n ...(receipt.additionalGroups ?? []).map((entry) => ({\n principalId: entry.request.state.principalId,\n principalType: \"group\" as const,\n containerIds: entry.retiredContainerIds ?? [],\n })),\n {\n principalId: input.groupId,\n principalType: \"group\",\n containerIds: receipt.retiredContainerIds ?? [],\n },\n ],\n stillCurrent: context.stillCurrent,\n });\n };\n return {\n groupRetention: { recovery: groupRecovery, predecessor: groupPredecessor },\n retainAcknowledged,\n };\n}\n"]}
|
|
@@ -10,7 +10,7 @@ export function createRuntimeCurrentOrganizationMutation(runtime) {
|
|
|
10
10
|
return undefined;
|
|
11
11
|
return async (input, work) => {
|
|
12
12
|
const owned = { ...input };
|
|
13
|
-
return lease(owned.stillCurrent, async ({ protection,
|
|
13
|
+
return lease(owned.stillCurrent, async ({ protection, historyReader, stillCurrent, resolveCurrentPolicy, directoryRecovery, }) => {
|
|
14
14
|
const { authority } = await loadCurrentOrganizationMutationAuthority({
|
|
15
15
|
execSql: runtime.infra.execSql,
|
|
16
16
|
organizationId: owned.organizationId,
|
|
@@ -34,7 +34,7 @@ export function createRuntimeCurrentOrganizationMutation(runtime) {
|
|
|
34
34
|
runtime,
|
|
35
35
|
authority,
|
|
36
36
|
protection,
|
|
37
|
-
|
|
37
|
+
historyReader,
|
|
38
38
|
organizationId: owned.organizationId,
|
|
39
39
|
directoryRecovery: directoryRecovery(authority.directory.current),
|
|
40
40
|
stillCurrent: current,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtimeCurrentOrganizationMutation.js","sourceRoot":"","sources":["runtimeCurrentOrganizationMutation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,kCAAkC,EAAE,MAAM,2DAA2D,CAAC;AAG/G,OAAO,EAAE,wCAAwC,EAAE,MAAM,wCAAwC,CAAC;AAClG,OAAO,EAAE,0CAA0C,EAAE,MAAM,wCAAwC,CAAC;AACpG,OAAO,EAAE,mCAAmC,EAAE,MAAM,iCAAiC,CAAC;AAmBtF,mFAAmF;AACnF,MAAM,UAAU,wCAAwC,CAAC,OAAgB;IACvE,MAAM,KAAK,GAAG,mCAAmC,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,KAAK,EACV,KAAY,EACZ,IAAiE,EACrD,EAAE;QACd,MAAM,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CACV,KAAK,CAAC,YAAY,EAClB,KAAK,EAAE,EACL,UAAU,EACV,
|
|
1
|
+
{"version":3,"file":"runtimeCurrentOrganizationMutation.js","sourceRoot":"","sources":["runtimeCurrentOrganizationMutation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,kCAAkC,EAAE,MAAM,2DAA2D,CAAC;AAG/G,OAAO,EAAE,wCAAwC,EAAE,MAAM,wCAAwC,CAAC;AAClG,OAAO,EAAE,0CAA0C,EAAE,MAAM,wCAAwC,CAAC;AACpG,OAAO,EAAE,mCAAmC,EAAE,MAAM,iCAAiC,CAAC;AAmBtF,mFAAmF;AACnF,MAAM,UAAU,wCAAwC,CAAC,OAAgB;IACvE,MAAM,KAAK,GAAG,mCAAmC,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,KAAK,EACV,KAAY,EACZ,IAAiE,EACrD,EAAE;QACd,MAAM,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CACV,KAAK,CAAC,YAAY,EAClB,KAAK,EAAE,EACL,UAAU,EACV,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,GAClB,EAAE,EAAE;YACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,wCAAwC,CAAC;gBACnE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;gBAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,+BAA+B,EAAE,CAAC,cAAc,EAAE,EAAE,CAClD,OAAO,CAAC,SAAS,CAAC,+BAA+B,CAAC,cAAc,CAAC;gBACnE,oBAAoB;gBACpB,YAAY;aACb,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC;YACjE,MAAM,kCAAkC,CAAC;gBACvC,MAAM,EAAE,EAAE;gBACV,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;gBAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,QAAQ,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC/D,YAAY,EAAE,OAAO;aACtB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC;gBACxB,GAAG,SAAS;gBACZ,YAAY,EAAE,OAAO;gBACrB,GAAG,CAAC,MAAM,0CAA0C,CAAC;oBACnD,OAAO;oBACP,SAAS;oBACT,UAAU;oBACV,aAAa;oBACb,cAAc,EAAE,KAAK,CAAC,cAAc;oBACpC,iBAAiB,EAAE,iBAAiB,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC;oBACjE,YAAY,EAAE,OAAO;iBACtB,CAAC,CAAC;aACJ,CAAC,CAAC;YACH,mCAAmC,CAAC,OAAO,CAAC,CAAC;YAC7C,OAAO,MAAM,CAAC;QAChB,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport type { PrincipalPolicyRecoveryRuntime } from \"../principals/runtimePolicyRecovery\";\nimport type { loadCurrentOrganizationAuthority } from \"./currentOrganizationAuthority\";\nimport { loadCurrentOrganizationMutationAuthority } from \"./currentOrganizationMutationAuthority\";\nimport { createCurrentOrganizationMutationRetention } from \"./currentOrganizationMutationRetention\";\nimport { createCurrentPrincipalMutationLease } from \"./currentPrincipalMutationLease\";\nimport type { PrincipalMutationRecoveryApi } from \"./principalMutationJournalManagement\";\n\ntype Runtime = PrincipalPolicyRecoveryRuntime & {\n readonly apiClient: PrincipalPolicyRecoveryRuntime[\"apiClient\"] &\n Pick<PrincipalMutationRecoveryApi, \"recoverPendingPrincipalMutation\">;\n};\n\ninterface Input {\n readonly organizationId: string;\n readonly signerUserId: string;\n readonly stillCurrent: () => boolean;\n}\n\ntype Authority = Awaited<ReturnType<typeof loadCurrentOrganizationAuthority>>;\n\nexport type CurrentOrganizationMutationContext = Authority &\n Awaited<ReturnType<typeof createCurrentOrganizationMutationRetention>>;\n\n/** Keep directory authoring and exact acknowledgement inside one private lease. */\nexport function createRuntimeCurrentOrganizationMutation(runtime: Runtime) {\n const lease = createCurrentPrincipalMutationLease(runtime);\n if (!lease) return undefined;\n return async <T>(\n input: Input,\n work: (context: CurrentOrganizationMutationContext) => Promise<T>,\n ): Promise<T> => {\n const owned = { ...input };\n return lease(\n owned.stillCurrent,\n async ({\n protection,\n historyReader,\n stillCurrent,\n resolveCurrentPolicy,\n directoryRecovery,\n }) => {\n const { authority } = await loadCurrentOrganizationMutationAuthority({\n execSql: runtime.infra.execSql,\n organizationId: owned.organizationId,\n signerUserId: owned.signerUserId,\n recoverPendingPrincipalMutation: (organizationId) =>\n runtime.apiClient.recoverPendingPrincipalMutation(organizationId),\n resolveCurrentPolicy,\n stillCurrent,\n });\n const current = () => stillCurrent() && authority.stillCurrent();\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: runtime.infra.execSql,\n organizationId: owned.organizationId,\n policies: [authority.directory.policy, authority.admins.policy],\n stillCurrent: current,\n });\n const result = await work({\n ...authority,\n stillCurrent: current,\n ...(await createCurrentOrganizationMutationRetention({\n runtime,\n authority,\n protection,\n historyReader,\n organizationId: owned.organizationId,\n directoryRecovery: directoryRecovery(authority.directory.current),\n stillCurrent: current,\n })),\n });\n assertProjectionVerificationCurrent(current);\n return result;\n },\n );\n };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ManagedPrincipalKind } from "../../internal/crypto/index.js";
|
|
2
|
+
import type { PrincipalPolicyBundleResponse } from "../../internal/validators/response/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where a workflow reads a principal's current signed policy. The API client is
|
|
5
|
+
* the production reader and satisfies this port as it is; a workflow that has
|
|
6
|
+
* already verified a directory can serve its pinned heads instead. A bundle is
|
|
7
|
+
* untrusted until the caller checks it against a signed head or checkpoint,
|
|
8
|
+
* whichever reader supplied it.
|
|
9
|
+
*/
|
|
10
|
+
export interface CurrentPrincipalPolicyReader {
|
|
11
|
+
readonly getCurrentPrincipalPolicy: (principalType: ManagedPrincipalKind, principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"currentPrincipalPolicyReader.js","sourceRoot":"","sources":["currentPrincipalPolicyReader.ts"],"names":[],"mappings":"","sourcesContent":["import type { ManagedPrincipalKind } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\n\n/**\n * Where a workflow reads a principal's current signed policy. The API client is\n * the production reader and satisfies this port as it is; a workflow that has\n * already verified a directory can serve its pinned heads instead. A bundle is\n * untrusted until the caller checks it against a signed head or checkpoint,\n * whichever reader supplied it.\n */\nexport interface CurrentPrincipalPolicyReader {\n readonly getCurrentPrincipalPolicy: (\n principalType: ManagedPrincipalKind,\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import type { PrincipalPolicyBundleResponse } from "../../internal/validators/re
|
|
|
3
3
|
import { type OrganizationAuthorityDescriptor } from "../../data/principals/organizationAuthorityDescriptor.js";
|
|
4
4
|
import type { ExecSql } from "../../data/sqlite/sqlSchema.js";
|
|
5
5
|
import type { TrustedUserIdentityResolver } from "../../data/trustedUserIdentity/index.js";
|
|
6
|
+
import type { CurrentPrincipalPolicyReader } from "./currentPrincipalPolicyReader.js";
|
|
6
7
|
export interface VerifiedExternalAdminPolicy {
|
|
7
8
|
readonly adminBundle: PrincipalPolicyBundleResponse;
|
|
8
9
|
readonly adminGroupId: string;
|
|
@@ -19,8 +20,8 @@ export declare function externalAdminPolicyPersistenceEntries(authority: Verifie
|
|
|
19
20
|
readonly policy: VerifiedPrincipalPolicy;
|
|
20
21
|
}>;
|
|
21
22
|
interface ExternalAdminPolicyInput {
|
|
23
|
+
readonly currentPolicyReader: CurrentPrincipalPolicyReader;
|
|
22
24
|
readonly execSql: ExecSql;
|
|
23
|
-
readonly getCurrentPrincipalPolicy: (principalType: "group" | "organization", principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
|
|
24
25
|
readonly organizationId: string | null | undefined;
|
|
25
26
|
readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;
|
|
26
27
|
readonly stillCurrent?: (() => boolean) | undefined;
|
|
@@ -70,7 +70,7 @@ function parseScopedAuthorityDescriptor(bundle, organizationId) {
|
|
|
70
70
|
class AdminsHeadAdvanced extends Error {
|
|
71
71
|
}
|
|
72
72
|
async function loadVerifiedAdminsPolicy(input) {
|
|
73
|
-
const bundle = await input.getCurrentPrincipalPolicy("group", input.adminGroupId);
|
|
73
|
+
const bundle = await input.currentPolicyReader.getCurrentPrincipalPolicy("group", input.adminGroupId);
|
|
74
74
|
if (!bundle) {
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
@@ -106,7 +106,7 @@ async function loadVerifiedAdminsPolicy(input) {
|
|
|
106
106
|
return { bundle, policy: verified.value };
|
|
107
107
|
}
|
|
108
108
|
async function loadExternalAdminPolicyOnce(input, organizationId, mayRefetchDirectory) {
|
|
109
|
-
const bundle = await input.getCurrentPrincipalPolicy("organization", organizationId);
|
|
109
|
+
const bundle = await input.currentPolicyReader.getCurrentPrincipalPolicy("organization", organizationId);
|
|
110
110
|
if (!bundle) {
|
|
111
111
|
return null;
|
|
112
112
|
}
|
|
@@ -125,7 +125,7 @@ async function loadExternalAdminPolicyOnce(input, organizationId, mayRefetchDire
|
|
|
125
125
|
mayRefetchDirectory,
|
|
126
126
|
expectedHead: requireOrganizationGroupHead(descriptor, descriptor.adminGroupId),
|
|
127
127
|
execSql: input.execSql,
|
|
128
|
-
|
|
128
|
+
currentPolicyReader: input.currentPolicyReader,
|
|
129
129
|
resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
|
|
130
130
|
});
|
|
131
131
|
if (!admin) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"externalAdminPolicy.js","sourceRoot":"","sources":["externalAdminPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n verifyPrincipalPolicyBundle,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n parseOrganizationAuthorityDescriptor,\n previousStatesIncludeHead,\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n organizationAdminExternalAuthority,\n organizationAdminSignerUserIds,\n principalPolicyReferenceFromBundle,\n verifyOrganizationAdminPolicy,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"./policyVerification\";\n\nexport interface VerifiedExternalAdminPolicy {\n readonly adminBundle: PrincipalPolicyBundleResponse;\n readonly adminGroupId: string;\n readonly adminPolicy: VerifiedPrincipalPolicy;\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly memberGroupId: string;\n readonly policy: VerifiedPrincipalPolicy;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly signerUserIds: readonly string[];\n}\n\nexport function externalAdminPolicyPersistenceEntries(\n authority: VerifiedExternalAdminPolicy,\n): ReadonlyArray<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n}> {\n return [\n { bundle: authority.bundle, policy: authority.policy },\n { bundle: authority.adminBundle, policy: authority.adminPolicy },\n ];\n}\n\nfunction assertAdminsPolicyShape(policy: VerifiedPrincipalPolicy): void {\n if (\n (policy.history ?? [{ projection: policy.projection }]).some(\n (entry) =>\n entry.projection.length === 0 ||\n entry.projection.some((member) => member.role !== \"admin\"),\n )\n ) {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"reserved Admins policy must contain only direct admin users\",\n );\n }\n}\n\n/**\n * A signer the caller cannot resolve is a cache miss, but a resolved signer\n * whose trusted key differs from the one the policy signed with is a\n * verification failure, as on every other policy path (#2365 finding 23).\n */\nfunction signerKeyLoadFailure(\n error: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): null {\n if (error === \"fingerprint-mismatch\") {\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"external admin policy signer key fingerprint mismatch\",\n );\n }\n return null;\n}\n\nasync function verifyOrganizationPolicy(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<VerifiedPrincipalPolicy | null> {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle: input.bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return signerKeyLoadFailure(signerPublicKeys.error);\n }\n const verified = await verifyOrganizationAdminPolicy({\n bundle: input.bundle,\n localCheckpoint,\n organizationId: input.organizationId,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n return verified.value;\n}\n\nfunction parseScopedAuthorityDescriptor(\n bundle: PrincipalPolicyBundleResponse,\n organizationId: string,\n): OrganizationAuthorityDescriptor {\n let descriptor: OrganizationAuthorityDescriptor;\n try {\n descriptor = parseOrganizationAuthorityDescriptor(\n bundle.currentPayload.ciphertext,\n );\n } catch {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"organization authority descriptor is invalid\",\n );\n }\n if (descriptor.organizationId !== organizationId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"organization authority descriptor scope does not match\",\n );\n }\n return descriptor;\n}\n\n/**\n * The organization and its Admins group are separate reads, so an honest\n * Admins commit between them serves a chain that extends the head the\n * directory cites: a stale directory, not tampering (#2365 finding 22). The\n * claim is checked before any signer is trusted, so it is unverified and buys\n * only one organization refetch; a disagreement after that is an incident.\n */\nclass AdminsHeadAdvanced extends Error {}\n\nasync function loadVerifiedAdminsPolicy(input: {\n readonly adminGroupId: string;\n readonly expectedHead: ReturnType<typeof requireOrganizationGroupHead>;\n readonly mayRefetchDirectory: boolean;\n readonly execSql: ExecSql;\n readonly getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n} | null> {\n const bundle = await input.getCurrentPrincipalPolicy(\n \"group\",\n input.adminGroupId,\n );\n if (!bundle) {\n return null;\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedHead,\n )\n ) {\n if (\n input.mayRefetchDirectory &&\n previousStatesIncludeHead(bundle, input.expectedHead)\n ) {\n throw new AdminsHeadAdvanced();\n }\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n \"reserved Admins policy does not match the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.adminGroupId,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return signerKeyLoadFailure(signerPublicKeys.error);\n }\n const verified = await verifyPrincipalPolicyBundle({\n bundle,\n expectedReference: input.expectedHead,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n if (\n verified.value.principalType !== \"group\" ||\n verified.value.principalId !== input.adminGroupId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"reserved Admins policy target does not match\",\n );\n }\n assertAdminsPolicyShape(verified.value);\n return { bundle, policy: verified.value };\n}\n\ninterface ExternalAdminPolicyInput {\n readonly execSql: ExecSql;\n readonly getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n readonly organizationId: string | null | undefined;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nasync function loadExternalAdminPolicyOnce(\n input: ExternalAdminPolicyInput,\n organizationId: string,\n mayRefetchDirectory: boolean,\n): Promise<VerifiedExternalAdminPolicy | null> {\n const bundle = await input.getCurrentPrincipalPolicy(\n \"organization\",\n organizationId,\n );\n if (!bundle) {\n return null;\n }\n const policy = await verifyOrganizationPolicy({\n bundle,\n execSql: input.execSql,\n organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!policy) {\n return null;\n }\n const descriptor = parseScopedAuthorityDescriptor(bundle, organizationId);\n const admin = await loadVerifiedAdminsPolicy({\n adminGroupId: descriptor.adminGroupId,\n mayRefetchDirectory,\n expectedHead: requireOrganizationGroupHead(\n descriptor,\n descriptor.adminGroupId,\n ),\n execSql: input.execSql,\n getCurrentPrincipalPolicy: input.getCurrentPrincipalPolicy,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!admin) {\n return null;\n }\n\n const verified: VerifiedExternalAdminPolicy = {\n adminBundle: admin.bundle,\n adminGroupId: descriptor.adminGroupId,\n adminPolicy: admin.policy,\n bundle,\n descriptor,\n externalAuthority: organizationAdminExternalAuthority(admin.policy),\n memberGroupId: descriptor.memberGroupId,\n policy,\n signerUserIds: organizationAdminSignerUserIds(admin.policy),\n };\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: externalAdminPolicyPersistenceEntries(verified),\n execSql: input.execSql,\n organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return verified;\n}\n\nexport async function loadOrganizationExternalAdminPolicy(\n input: ExternalAdminPolicyInput,\n): Promise<VerifiedExternalAdminPolicy | null> {\n const { organizationId } = input;\n if (!organizationId) {\n return null;\n }\n try {\n try {\n return await loadExternalAdminPolicyOnce(input, organizationId, true);\n } catch (error) {\n if (!(error instanceof AdminsHeadAdvanced)) throw error;\n return await loadExternalAdminPolicyOnce(input, organizationId, false);\n }\n } catch (error) {\n if (error instanceof KeyingVerificationError) {\n throw error;\n }\n return null;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"externalAdminPolicy.js","sourceRoot":"","sources":["externalAdminPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n verifyPrincipalPolicyBundle,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { persistVerifiedPrincipalPolicyBundlesAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n parseOrganizationAuthorityDescriptor,\n previousStatesIncludeHead,\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n organizationAdminExternalAuthority,\n organizationAdminSignerUserIds,\n principalPolicyReferenceFromBundle,\n verifyOrganizationAdminPolicy,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { CurrentPrincipalPolicyReader } from \"./currentPrincipalPolicyReader\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"./policyVerification\";\n\nexport interface VerifiedExternalAdminPolicy {\n readonly adminBundle: PrincipalPolicyBundleResponse;\n readonly adminGroupId: string;\n readonly adminPolicy: VerifiedPrincipalPolicy;\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly memberGroupId: string;\n readonly policy: VerifiedPrincipalPolicy;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly signerUserIds: readonly string[];\n}\n\nexport function externalAdminPolicyPersistenceEntries(\n authority: VerifiedExternalAdminPolicy,\n): ReadonlyArray<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n}> {\n return [\n { bundle: authority.bundle, policy: authority.policy },\n { bundle: authority.adminBundle, policy: authority.adminPolicy },\n ];\n}\n\nfunction assertAdminsPolicyShape(policy: VerifiedPrincipalPolicy): void {\n if (\n (policy.history ?? [{ projection: policy.projection }]).some(\n (entry) =>\n entry.projection.length === 0 ||\n entry.projection.some((member) => member.role !== \"admin\"),\n )\n ) {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"reserved Admins policy must contain only direct admin users\",\n );\n }\n}\n\n/**\n * A signer the caller cannot resolve is a cache miss, but a resolved signer\n * whose trusted key differs from the one the policy signed with is a\n * verification failure, as on every other policy path (#2365 finding 23).\n */\nfunction signerKeyLoadFailure(\n error: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): null {\n if (error === \"fingerprint-mismatch\") {\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"external admin policy signer key fingerprint mismatch\",\n );\n }\n return null;\n}\n\nasync function verifyOrganizationPolicy(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<VerifiedPrincipalPolicy | null> {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle: input.bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return signerKeyLoadFailure(signerPublicKeys.error);\n }\n const verified = await verifyOrganizationAdminPolicy({\n bundle: input.bundle,\n localCheckpoint,\n organizationId: input.organizationId,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n return verified.value;\n}\n\nfunction parseScopedAuthorityDescriptor(\n bundle: PrincipalPolicyBundleResponse,\n organizationId: string,\n): OrganizationAuthorityDescriptor {\n let descriptor: OrganizationAuthorityDescriptor;\n try {\n descriptor = parseOrganizationAuthorityDescriptor(\n bundle.currentPayload.ciphertext,\n );\n } catch {\n throw new KeyingVerificationError(\n \"invalid_shape\",\n \"organization authority descriptor is invalid\",\n );\n }\n if (descriptor.organizationId !== organizationId) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"organization authority descriptor scope does not match\",\n );\n }\n return descriptor;\n}\n\n/**\n * The organization and its Admins group are separate reads, so an honest\n * Admins commit between them serves a chain that extends the head the\n * directory cites: a stale directory, not tampering (#2365 finding 22). The\n * claim is checked before any signer is trusted, so it is unverified and buys\n * only one organization refetch; a disagreement after that is an incident.\n */\nclass AdminsHeadAdvanced extends Error {}\n\nasync function loadVerifiedAdminsPolicy(input: {\n readonly adminGroupId: string;\n readonly expectedHead: ReturnType<typeof requireOrganizationGroupHead>;\n readonly mayRefetchDirectory: boolean;\n readonly execSql: ExecSql;\n readonly currentPolicyReader: CurrentPrincipalPolicyReader;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): Promise<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n} | null> {\n const bundle = await input.currentPolicyReader.getCurrentPrincipalPolicy(\n \"group\",\n input.adminGroupId,\n );\n if (!bundle) {\n return null;\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedHead,\n )\n ) {\n if (\n input.mayRefetchDirectory &&\n previousStatesIncludeHead(bundle, input.expectedHead)\n ) {\n throw new AdminsHeadAdvanced();\n }\n throw new KeyingVerificationError(\n \"hash_mismatch\",\n \"reserved Admins policy does not match the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.adminGroupId,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n return signerKeyLoadFailure(signerPublicKeys.error);\n }\n const verified = await verifyPrincipalPolicyBundle({\n bundle,\n expectedReference: input.expectedHead,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throw verified.error;\n }\n if (\n verified.value.principalType !== \"group\" ||\n verified.value.principalId !== input.adminGroupId\n ) {\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"reserved Admins policy target does not match\",\n );\n }\n assertAdminsPolicyShape(verified.value);\n return { bundle, policy: verified.value };\n}\n\ninterface ExternalAdminPolicyInput {\n readonly currentPolicyReader: CurrentPrincipalPolicyReader;\n readonly execSql: ExecSql;\n readonly organizationId: string | null | undefined;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}\n\nasync function loadExternalAdminPolicyOnce(\n input: ExternalAdminPolicyInput,\n organizationId: string,\n mayRefetchDirectory: boolean,\n): Promise<VerifiedExternalAdminPolicy | null> {\n const bundle = await input.currentPolicyReader.getCurrentPrincipalPolicy(\n \"organization\",\n organizationId,\n );\n if (!bundle) {\n return null;\n }\n const policy = await verifyOrganizationPolicy({\n bundle,\n execSql: input.execSql,\n organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!policy) {\n return null;\n }\n const descriptor = parseScopedAuthorityDescriptor(bundle, organizationId);\n const admin = await loadVerifiedAdminsPolicy({\n adminGroupId: descriptor.adminGroupId,\n mayRefetchDirectory,\n expectedHead: requireOrganizationGroupHead(\n descriptor,\n descriptor.adminGroupId,\n ),\n execSql: input.execSql,\n currentPolicyReader: input.currentPolicyReader,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!admin) {\n return null;\n }\n\n const verified: VerifiedExternalAdminPolicy = {\n adminBundle: admin.bundle,\n adminGroupId: descriptor.adminGroupId,\n adminPolicy: admin.policy,\n bundle,\n descriptor,\n externalAuthority: organizationAdminExternalAuthority(admin.policy),\n memberGroupId: descriptor.memberGroupId,\n policy,\n signerUserIds: organizationAdminSignerUserIds(admin.policy),\n };\n await persistVerifiedPrincipalPolicyBundlesAtomically({\n entries: externalAdminPolicyPersistenceEntries(verified),\n execSql: input.execSql,\n organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: new Date().toISOString(),\n });\n return verified;\n}\n\nexport async function loadOrganizationExternalAdminPolicy(\n input: ExternalAdminPolicyInput,\n): Promise<VerifiedExternalAdminPolicy | null> {\n const { organizationId } = input;\n if (!organizationId) {\n return null;\n }\n try {\n try {\n return await loadExternalAdminPolicyOnce(input, organizationId, true);\n } catch (error) {\n if (!(error instanceof AdminsHeadAdvanced)) throw error;\n return await loadExternalAdminPolicyOnce(input, organizationId, false);\n }\n } catch (error) {\n if (error instanceof KeyingVerificationError) {\n throw error;\n }\n return null;\n }\n}\n"]}
|
|
@@ -2,9 +2,10 @@ import type { PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse }
|
|
|
2
2
|
import type { SecurityIncidentReporter } from "../../data/securityIncidents.js";
|
|
3
3
|
import type { ExecSql } from "../../data/sqlite/sqlSchema.js";
|
|
4
4
|
import type { TrustedUserIdentityResolver } from "../../data/trustedUserIdentity/index.js";
|
|
5
|
+
import type { CurrentPrincipalPolicyReader } from "./currentPrincipalPolicyReader.js";
|
|
5
6
|
export interface CacheReferencedPrincipalPoliciesOptions {
|
|
7
|
+
currentPolicyReader: CurrentPrincipalPolicyReader;
|
|
6
8
|
execSql: ExecSql;
|
|
7
|
-
getCurrentPrincipalPolicy: (principalType: "group" | "organization", principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
|
|
8
9
|
log?: (message: string) => void;
|
|
9
10
|
organizationId: string;
|
|
10
11
|
reportSecurityIncident: SecurityIncidentReporter;
|
|
@@ -16,4 +17,4 @@ export interface CachePrincipalPolicyBundlesOptions extends Omit<CacheReferenced
|
|
|
16
17
|
bundles: ReadonlyArray<PrincipalPolicyBundleResponse> | undefined;
|
|
17
18
|
}
|
|
18
19
|
export declare function cacheReferencedPrincipalPolicies(input: CacheReferencedPrincipalPoliciesOptions): Promise<void>;
|
|
19
|
-
export declare function cachePrincipalPolicyBundles({ bundles,
|
|
20
|
+
export declare function cachePrincipalPolicyBundles({ bundles, currentPolicyReader, execSql, log, organizationId, reportSecurityIncident, resolveTrustedUserIdentity, stillCurrent, }: CachePrincipalPolicyBundlesOptions): Promise<void>;
|