@tearleads/client-sdk 0.1.100 → 0.1.102
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/client/containerContents/documentDiscovery.js +2 -2
- package/client/containerContents/documentDiscovery.js.map +1 -1
- package/data/containers/shared/mutationFailures.js.map +1 -1
- package/data/containers/shared/types.d.ts +3 -0
- package/data/containers/shared/types.js.map +1 -1
- package/data/persistence/container-contents/containerAccessDiscovery.js +28 -0
- package/data/persistence/container-contents/containerAccessDiscovery.js.map +1 -0
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js +22 -17
- package/data/persistence/container-contents/containerMetadataMutationPersistence.js.map +1 -1
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.d.ts +1 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js +1 -0
- package/data/persistence/documents/documentDiscoveryEvidencePersistence.js.map +1 -1
- package/internal/api-client/ApiClient.d.ts +2 -0
- package/internal/api-client/ApiClient.js +13 -11
- package/internal/api-client/ApiClient.js.map +1 -1
- package/package.json +1 -1
- package/workflows/container-contents/documentDiscovery/documentDiscoveryApply.js +6 -3
- package/workflows/container-contents/documentDiscovery/documentDiscoveryApply.js.map +1 -1
- package/workflows/container-contents/documentDiscovery/documentDiscoveryTypes.d.ts +4 -3
- package/workflows/container-contents/documentDiscovery/documentDiscoveryTypes.js.map +1 -1
- package/workflows/container-contents/documentDiscovery/index.js +15 -9
- package/workflows/container-contents/documentDiscovery/index.js.map +1 -1
- package/workflows/container-contents/syncPause/discoveryGuard.js +12 -11
- package/workflows/container-contents/syncPause/discoveryGuard.js.map +1 -1
- package/workflows/containers/child/share/index.d.ts +14 -4
- package/workflows/containers/child/share/index.js +93 -30
- package/workflows/containers/child/share/index.js.map +1 -1
- package/workflows/containers/rematerialization/principalContainerRematerialization.js +5 -19
- package/workflows/containers/rematerialization/principalContainerRematerialization.js.map +1 -1
- package/workflows/containers/rematerialization/principalContainerRematerializationPlanning.js +38 -0
- package/workflows/containers/rematerialization/principalContainerRematerializationPlanning.js.map +1 -1
- package/workflows/organizations/groupShare/sharePrincipalPolicy.d.ts +1 -0
- package/workflows/organizations/groupShare/sharePrincipalPolicy.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalContainerRematerialization.js","sourceRoot":"","sources":["principalContainerRematerialization.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ContainerDirectGrant,\n PrincipalContainerGrant,\n} from \"@tearleads/crypto\";\nimport type { ContainerMutationRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerMutationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { MAX_ROTATION_CONTAINER_REKEYS } from \"@tearleads/validators/util\";\nimport { rememberVerifiedContainerHeads } from \"../../../data/containers/shared/heldContainerHeads\";\nimport type { AuthoredContainerMutationHead } from \"../../../data/containers/shared/mutationAcknowledgement\";\nimport { acknowledgeContainerMutationBatch } from \"../../../data/containers/shared/mutationAcknowledgement\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../../data/containers/shared/projection\";\nimport {\n type PrincipalPolicyCache,\n verifyContainerWriterProjection,\n} from \"../../../data/keyingProjectionVerification\";\nimport { createProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification/userKeyResolver\";\nimport { scheduleHeldDescendantRecitations } from \"../child/recite\";\nimport { buildMaterializedContainerRekeyPlan } from \"../child/rekey\";\nimport { rebaseOnDeepestAncestor } from \"../child/rekey/carriedDescendantRekeys\";\nimport {\n containerWriterProjectionFromRotationPlan,\n containerWriterProjectionFromSharePlan,\n isSpeculativeContainerWriterProjection,\n} from \"../child/rekey/rekeyProjection\";\nimport { buildMaterializedContainerRevokePlan } from \"../child/revoke\";\nimport { selectReplacementPrincipalPolicyReferences } from \"../child/rotation/replacementPrincipalPolicyReferences\";\nimport { buildMaterializedContainerSharePlan } from \"../child/share/shareMaterialization\";\nimport { withPrecedingRematerialization } from \"./precedingPrincipalRematerialization\";\nimport {\n addPlan,\n type BatchPlanning,\n carryLevel,\n type PrincipalContainerRematerializationInput,\n type PrincipalRematerializationPredecessor,\n rerootOnAuthoredPaths,\n staleLevelsAbove,\n} from \"./principalContainerRematerializationPlanning\";\nimport {\n loadRematerializationTargets,\n loadServedProjection,\n type MaterializedPrincipalContainerMutationPlan,\n type PlannedRematerialization,\n type RematerializationTarget,\n seededPrincipalPolicyCache,\n} from \"./principalContainerRematerializationTargets\";\n\nfunction matchingGroupGrant(input: {\n readonly directGrants: readonly ContainerDirectGrant[];\n readonly groupId: string;\n}): ContainerDirectGrant | null {\n return (\n input.directGrants.find(\n (grant) =>\n grant.subjectType === \"group\" && grant.subjectId === input.groupId,\n ) ?? null\n );\n}\n\nfunction referencedGroupKeyEpoch(input: {\n readonly groupId: string;\n readonly referencedPrincipalHeads: ReturnType<\n typeof readContainerState\n >[\"referencedPrincipalHeads\"];\n}): number | null {\n return (\n input.referencedPrincipalHeads.find(\n (head) =>\n head.principalType === \"group\" && head.principalId === input.groupId,\n )?.keyEpoch ?? null\n );\n}\n\nasync function loadGrantedContainerContext(\n input: PrincipalContainerRematerializationInput,\n grantRow: PrincipalContainerGrant,\n projection: ContainerWriterProjectionResponse,\n principalPolicyCache: PrincipalPolicyCache,\n) {\n // A projection re-rooted on a rotation this batch has yet to commit must\n // not pin anything: its heads are speculative until acknowledged, and a\n // pin past the group's served head would refuse the other served paths.\n await verifyContainerWriterProjection({\n execSql: input.execSql,\n persistVerificationCheckpoints:\n !isSpeculativeContainerWriterProjection(projection),\n principalPolicyCache,\n projection,\n resolveUserKey: createProjectionUserKeyResolver({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n }),\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const state = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n const grant = matchingGroupGrant({\n directGrants: state.directGrants,\n groupId: input.groupId,\n });\n if (grant && grant.accessLevel !== grantRow.accessLevel) {\n const nextGrant = input.nextPolicy.grants.find(\n (candidate) => candidate.containerId === grantRow.containerId,\n );\n if (!nextGrant || nextGrant.accessLevel !== grantRow.accessLevel) {\n throw new Error(\n `Container ${grantRow.containerId} does not contain the expected group grant`,\n );\n }\n }\n const referencedKeyEpoch = referencedGroupKeyEpoch({\n groupId: input.groupId,\n referencedPrincipalHeads: state.referencedPrincipalHeads,\n });\n if (grant && referencedKeyEpoch === null) {\n throw new Error(\n `Container ${grantRow.containerId} is missing its group reference`,\n );\n }\n return { grant, projection, referencedKeyEpoch };\n}\n\nexport async function buildPrincipalContainerRematerializationBatch(\n input: PrincipalContainerRematerializationInput,\n): Promise<ContainerMutationRequest[]> {\n return (\n await buildPrincipalContainerRematerializationPlans(input)\n ).entries.map((entry) => entry.planned.plan.request);\n}\n\nexport interface PreparedPrincipalContainerRematerializationBatch {\n readonly preceding?: PrincipalRematerializationPredecessor | undefined;\n readonly retiredContainerIds: readonly string[];\n readonly acknowledge: (\n responses: readonly ContainerMutationResponse[],\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<void>;\n /**\n * Re-sign the batch with the descendant rekeys the server named for its\n * rotations woven in, parent-first, each against the path the batch will\n * leave behind. A named container the batch already rotates is re-planned\n * under what is carried above it, so no container rotates twice. Returns the\n * whole request list, which replaces `requests`; `acknowledge` then expects\n * the responses in that order.\n */\n readonly carry: (\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>;\n readonly plans: readonly MaterializedPrincipalContainerMutationPlan[];\n readonly requests: readonly ContainerMutationRequest[];\n}\n\nfunction authoredMutationHead(\n planned: MaterializedPrincipalContainerMutationPlan,\n): AuthoredContainerMutationHead {\n return planned.plan;\n}\n\nasync function buildPrincipalContainerRematerializationPlan(input: {\n readonly grantRow: PrincipalContainerGrant;\n /** Keys the batch minted above this container; see `BatchPlanning`. */\n readonly knownContainerKeks: ReadonlyMap<string, Uint8Array>;\n /** Holds the policy this batch commits, which rebased paths already cite. */\n readonly principalPolicyCache: PrincipalPolicyCache;\n /** The served projection, re-rooted on any rotation planned above it. */\n readonly projection: ContainerWriterProjectionResponse;\n readonly rematerialization: PrincipalContainerRematerializationInput;\n readonly resolveProjectionUserKey: ReturnType<\n typeof createProjectionUserKeyResolver\n >;\n}): Promise<MaterializedPrincipalContainerMutationPlan> {\n const { grantRow } = input;\n const rematerialization = {\n ...input.rematerialization,\n nextPolicy: await selectReplacementPrincipalPolicyReferences({\n policy: input.rematerialization.nextPolicy,\n projection: input.projection,\n principalPolicyCache: input.principalPolicyCache,\n resolveAuthoredPolicyReferences:\n input.rematerialization.resolveAuthoredPolicyReferences,\n }),\n };\n const { grant, projection, referencedKeyEpoch } =\n await loadGrantedContainerContext(\n rematerialization,\n grantRow,\n input.projection,\n input.principalPolicyCache,\n );\n const nextGrant = rematerialization.nextPolicy.grants.find(\n (candidate) => candidate.containerId === grantRow.containerId,\n );\n const author = {\n ...rematerialization.author,\n organizationId: projection.organizationId,\n };\n const sharedInput = {\n author,\n execSql: rematerialization.execSql,\n knownContainerKeks: input.knownContainerKeks,\n // Planning against a path this batch has yet to commit pins nothing; the\n // acknowledgement advances every head at once. The rekey planner applies\n // this rule itself; revoke and grant planning take it from here.\n persistVerificationCheckpoints:\n !isSpeculativeContainerWriterProjection(projection),\n previousProjection: projection,\n principalPolicyCache: input.principalPolicyCache,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: rematerialization.stillCurrent,\n targetSecretKey: rematerialization.targetSecretKey,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n };\n if (grantRow.containerId === rematerialization.revokedContainerId) {\n if (!grant) {\n throw new Error(\n `Container ${grantRow.containerId} does not contain the revoked group grant`,\n );\n }\n return buildMaterializedContainerRevokePlan({\n ...sharedInput,\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n revokedSubject: {\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n if (!grant || grant.accessLevel !== nextGrant?.accessLevel) {\n if (!nextGrant) {\n throw new Error(\n `Container ${grantRow.containerId} is absent from the next group grant set`,\n );\n }\n return buildMaterializedContainerSharePlan({\n ...sharedInput,\n accessLevel: nextGrant.accessLevel,\n recipient: {\n principalPolicy: rematerialization.nextPolicy,\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n if (referencedKeyEpoch === rematerialization.nextPolicy.keyEpoch) {\n return buildMaterializedContainerSharePlan({\n ...sharedInput,\n accessLevel: grant.accessLevel,\n recipient: {\n principalPolicy: rematerialization.nextPolicy,\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n return buildMaterializedContainerRekeyPlan({\n ...sharedInput,\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n });\n}\n\n/**\n * Plan one target against the paths already planned above it. A grant's\n * rematerialization extends the served head; a rotation's, and every carried\n * rekey, extends the epoch the batch mints above it.\n */\nasync function planRematerializationTarget(\n batch: BatchPlanning,\n target: RematerializationTarget,\n): Promise<void> {\n const { rematerialization } = batch;\n const rebased = rebaseOnDeepestAncestor(\n target.projection,\n batch.rotatedAbove,\n );\n const stale = rebased\n ? staleLevelsAbove(\n rebased.projection,\n rebased.ancestor.containerKeks.at(-1)?.containerId ?? \"\",\n )\n : [];\n for (const containerId of stale) {\n await carryLevel(\n batch,\n await loadServedProjection(rematerialization.apiClient, containerId),\n );\n }\n if (!target.grantRow) {\n await carryLevel(batch, target.projection);\n return;\n }\n // Each level carried above re-roots the target one step deeper.\n const previousProjection = await rerootOnAuthoredPaths(\n batch,\n target.projection,\n );\n const planned = await buildPrincipalContainerRematerializationPlan({\n grantRow: target.grantRow,\n knownContainerKeks: batch.knownContainerKeks,\n principalPolicyCache: batch.principalPolicyCache,\n projection: previousProjection,\n rematerialization,\n resolveProjectionUserKey: batch.resolveProjectionUserKey,\n });\n const nextProjection =\n \"keyring\" in planned.plan\n ? await containerWriterProjectionFromRotationPlan({\n plan: planned.plan,\n previousProjection,\n })\n : await containerWriterProjectionFromSharePlan({\n plan: planned.plan,\n previousProjection,\n });\n addPlan(batch, {\n carried: false,\n planned,\n rotated: \"keyring\" in planned.plan ? nextProjection : null,\n nextProjection,\n });\n}\n\n/**\n * Sign the batch parent-first. `carriedContainerIds` are the descendant rekeys\n * a refused attempt was told to carry; woven in by depth, each sits under the\n * rotation it rides, and a named container the batch rematerializes anyway is\n * re-planned there rather than rotated twice.\n */\nasync function buildPrincipalContainerRematerializationPlans(\n input: PrincipalContainerRematerializationInput,\n carriedContainerIds: readonly string[] = [],\n): Promise<{\n entries: PlannedRematerialization[];\n retiredContainerIds: readonly string[];\n preceding: PrincipalRematerializationPredecessor;\n}> {\n input = withPrecedingRematerialization(input);\n if (\n input.revokedContainerId &&\n !input.grants.some(\n (grant) => grant.containerId === input.revokedContainerId,\n )\n ) {\n throw new Error(\"Revoked container is not granted to the group\");\n }\n const { targets, retiredContainerIds } = await loadRematerializationTargets({\n apiClient: input.apiClient,\n carriedContainerIds,\n grants: input.grants,\n });\n const batch: BatchPlanning = {\n knownContainerKeks: new Map(input.preceding?.knownContainerKeks),\n plans: [],\n // A rotation planned above cites the group at the head this batch commits,\n // which no store holds yet; the cache is what lets a rebased path verify.\n principalPolicyCache: new Map([\n ...(input.preceding?.policies ?? []).flatMap((policy) => [\n ...seededPrincipalPolicyCache(policy),\n ]),\n ...seededPrincipalPolicyCache(input.nextPolicy),\n ]),\n rematerialization: input,\n resolveProjectionUserKey: createProjectionUserKeyResolver({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n }),\n rotatedAbove: [],\n authoredAbove: [],\n };\n for (const target of targets) {\n await planRematerializationTarget(batch, target);\n }\n return {\n entries: batch.plans,\n retiredContainerIds,\n preceding: {\n policies: [...(input.preceding?.policies ?? []), input.nextPolicy],\n projections: [\n ...(input.preceding?.projections ?? []),\n ...batch.plans.flatMap((entry) =>\n entry.nextProjection ? [entry.nextProjection] : [],\n ),\n ],\n knownContainerKeks: batch.knownContainerKeks,\n },\n };\n}\n\n/**\n * The server names a level it found pinned to a retired epoch. A batch answers\n * with a rotation of it: a carried rekey, or its own rematerialization when\n * that is a rekey or revoke. A rematerialized grant rotates nothing, so a\n * batch that both grants a container and must re-key it cannot be signed;\n * no policy change the SDK builds combines the two.\n */\nfunction assertNamedContainersRotated(\n plans: readonly PlannedRematerialization[],\n requiredContainerIds: readonly string[],\n): void {\n const rotatedIds = new Set(\n plans.flatMap((entry) =>\n entry.rotated ? [entry.planned.plan.containerId] : [],\n ),\n );\n const unrotated = requiredContainerIds\n .slice(0, MAX_ROTATION_CONTAINER_REKEYS)\n .find((containerId) => !rotatedIds.has(containerId));\n if (unrotated !== undefined) {\n throw new Error(\n `Container ${unrotated} needs a rekey this policy change only grants`,\n );\n }\n}\n\nexport async function preparePrincipalContainerRematerializationBatch(\n input: PrincipalContainerRematerializationInput,\n): Promise<PreparedPrincipalContainerRematerializationBatch> {\n let { entries, retiredContainerIds, preceding } =\n await buildPrincipalContainerRematerializationPlans(input);\n const requests = () => entries.map((entry) => entry.planned.plan.request);\n return {\n get preceding() {\n return preceding;\n },\n get retiredContainerIds() {\n return retiredContainerIds;\n },\n get plans() {\n return entries.map((entry) => entry.planned);\n },\n get requests() {\n return requests();\n },\n carry: async (requiredContainerIds) => {\n // The refused attempt rolled back whole, so every served head still\n // stands; the batch is signed again from them with the named levels in.\n const replanned = await buildPrincipalContainerRematerializationPlans(\n input,\n requiredContainerIds,\n );\n assertNamedContainersRotated(replanned.entries, requiredContainerIds);\n entries = replanned.entries;\n retiredContainerIds = replanned.retiredContainerIds;\n preceding = replanned.preceding;\n return requests();\n },\n acknowledge: async (responses, stillCurrent) => {\n const isCurrent = () =>\n input.stillCurrent?.() !== false && stillCurrent?.() !== false;\n const heads = entries.map((entry) => authoredMutationHead(entry.planned));\n const acknowledged = await acknowledgeContainerMutationBatch({\n execSql: input.execSql,\n plans: heads,\n responses,\n stillCurrent: isCurrent,\n });\n if (!acknowledged || !isCurrent()) return;\n const plansByOrganization = new Map<\n string,\n AuthoredContainerMutationHead[]\n >();\n for (const head of heads) {\n const organizationId = head.state.organizationId;\n const group = plansByOrganization.get(organizationId) ?? [];\n group.push(head);\n plansByOrganization.set(organizationId, group);\n }\n for (const [organizationId, organizationPlans] of plansByOrganization) {\n try {\n rememberVerifiedContainerHeads({\n organizationId,\n execSql: input.execSql,\n heads: [],\n policies: [input.nextPolicy],\n });\n } catch {\n // Cache failure must not invalidate the durably acknowledged batch.\n continue;\n }\n scheduleHeldDescendantRecitations({\n apiClient: input.apiClient,\n author: { ...input.author, organizationId },\n execSql: input.execSql,\n plans: organizationPlans,\n stillCurrent: input.recitationStillCurrent ?? isCurrent,\n reportSecurityIncident: input.reportSecurityIncident,\n });\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalContainerRematerialization.js","sourceRoot":"","sources":["principalContainerRematerialization.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ContainerDirectGrant,\n PrincipalContainerGrant,\n} from \"@tearleads/crypto\";\nimport type { ContainerMutationRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerMutationResponse,\n ContainerWriterProjectionResponse,\n} from \"@tearleads/validators/response\";\nimport { rememberVerifiedContainerHeads } from \"../../../data/containers/shared/heldContainerHeads\";\nimport type { AuthoredContainerMutationHead } from \"../../../data/containers/shared/mutationAcknowledgement\";\nimport { acknowledgeContainerMutationBatch } from \"../../../data/containers/shared/mutationAcknowledgement\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../../data/containers/shared/projection\";\nimport {\n type PrincipalPolicyCache,\n verifyContainerWriterProjection,\n} from \"../../../data/keyingProjectionVerification\";\nimport { createProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification/userKeyResolver\";\nimport { scheduleHeldDescendantRecitations } from \"../child/recite\";\nimport { buildMaterializedContainerRekeyPlan } from \"../child/rekey\";\nimport { rebaseOnDeepestAncestor } from \"../child/rekey/carriedDescendantRekeys\";\nimport {\n containerWriterProjectionFromRotationPlan,\n containerWriterProjectionFromSharePlan,\n isSpeculativeContainerWriterProjection,\n} from \"../child/rekey/rekeyProjection\";\nimport { buildMaterializedContainerRevokePlan } from \"../child/revoke\";\nimport { selectReplacementPrincipalPolicyReferences } from \"../child/rotation/replacementPrincipalPolicyReferences\";\nimport { buildMaterializedContainerSharePlan } from \"../child/share/shareMaterialization\";\nimport { withPrecedingRematerialization } from \"./precedingPrincipalRematerialization\";\nimport {\n addPlan,\n assertNamedContainersRotated,\n type BatchPlanning,\n carryLevel,\n type PrincipalContainerRematerializationInput,\n type PrincipalRematerializationPredecessor,\n planOnceMoreAfterRollback,\n rerootOnAuthoredPaths,\n staleLevelsAbove,\n} from \"./principalContainerRematerializationPlanning\";\nimport {\n loadRematerializationTargets,\n loadServedProjection,\n type MaterializedPrincipalContainerMutationPlan,\n type PlannedRematerialization,\n type RematerializationTarget,\n seededPrincipalPolicyCache,\n} from \"./principalContainerRematerializationTargets\";\n\nfunction matchingGroupGrant(input: {\n readonly directGrants: readonly ContainerDirectGrant[];\n readonly groupId: string;\n}): ContainerDirectGrant | null {\n return (\n input.directGrants.find(\n (grant) =>\n grant.subjectType === \"group\" && grant.subjectId === input.groupId,\n ) ?? null\n );\n}\n\nfunction referencedGroupKeyEpoch(input: {\n readonly groupId: string;\n readonly referencedPrincipalHeads: ReturnType<\n typeof readContainerState\n >[\"referencedPrincipalHeads\"];\n}): number | null {\n return (\n input.referencedPrincipalHeads.find(\n (head) =>\n head.principalType === \"group\" && head.principalId === input.groupId,\n )?.keyEpoch ?? null\n );\n}\n\nasync function loadGrantedContainerContext(\n input: PrincipalContainerRematerializationInput,\n grantRow: PrincipalContainerGrant,\n projection: ContainerWriterProjectionResponse,\n principalPolicyCache: PrincipalPolicyCache,\n) {\n // A projection re-rooted on a rotation this batch has yet to commit must\n // not pin anything: its heads are speculative until acknowledged, and a\n // pin past the group's served head would refuse the other served paths.\n await verifyContainerWriterProjection({\n execSql: input.execSql,\n persistVerificationCheckpoints:\n !isSpeculativeContainerWriterProjection(projection),\n principalPolicyCache,\n projection,\n resolveUserKey: createProjectionUserKeyResolver({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n }),\n stillCurrent: input.stillCurrent,\n warmReferencedPrincipalPolicies: input.warmReferencedPrincipalPolicies,\n });\n const state = readContainerState(\n getTargetContainerContext(projection).manifest,\n );\n const grant = matchingGroupGrant({\n directGrants: state.directGrants,\n groupId: input.groupId,\n });\n if (grant && grant.accessLevel !== grantRow.accessLevel) {\n const nextGrant = input.nextPolicy.grants.find(\n (candidate) => candidate.containerId === grantRow.containerId,\n );\n if (!nextGrant || nextGrant.accessLevel !== grantRow.accessLevel) {\n throw new Error(\n `Container ${grantRow.containerId} does not contain the expected group grant`,\n );\n }\n }\n const referencedKeyEpoch = referencedGroupKeyEpoch({\n groupId: input.groupId,\n referencedPrincipalHeads: state.referencedPrincipalHeads,\n });\n if (grant && referencedKeyEpoch === null) {\n throw new Error(\n `Container ${grantRow.containerId} is missing its group reference`,\n );\n }\n return { grant, projection, referencedKeyEpoch };\n}\n\nexport async function buildPrincipalContainerRematerializationBatch(\n input: PrincipalContainerRematerializationInput,\n): Promise<ContainerMutationRequest[]> {\n return (\n await buildPrincipalContainerRematerializationPlans(input)\n ).entries.map((entry) => entry.planned.plan.request);\n}\n\nexport interface PreparedPrincipalContainerRematerializationBatch {\n readonly preceding?: PrincipalRematerializationPredecessor | undefined;\n readonly retiredContainerIds: readonly string[];\n readonly acknowledge: (\n responses: readonly ContainerMutationResponse[],\n stillCurrent?: (() => boolean) | undefined,\n ) => Promise<void>;\n /**\n * Re-sign the batch with the descendant rekeys the server named for its\n * rotations woven in, parent-first, each against the path the batch will\n * leave behind. A named container the batch already rotates is re-planned\n * under what is carried above it, so no container rotates twice. Returns the\n * whole request list, which replaces `requests`; `acknowledge` then expects\n * the responses in that order.\n */\n readonly carry: (\n requiredContainerIds: readonly string[],\n ) => Promise<readonly ContainerMutationRequest[]>;\n readonly plans: readonly MaterializedPrincipalContainerMutationPlan[];\n readonly requests: readonly ContainerMutationRequest[];\n}\n\nfunction authoredMutationHead(\n planned: MaterializedPrincipalContainerMutationPlan,\n): AuthoredContainerMutationHead {\n return planned.plan;\n}\n\nasync function buildPrincipalContainerRematerializationPlan(input: {\n readonly grantRow: PrincipalContainerGrant;\n /** Keys the batch minted above this container; see `BatchPlanning`. */\n readonly knownContainerKeks: ReadonlyMap<string, Uint8Array>;\n /** Holds the policy this batch commits, which rebased paths already cite. */\n readonly principalPolicyCache: PrincipalPolicyCache;\n /** The served projection, re-rooted on any rotation planned above it. */\n readonly projection: ContainerWriterProjectionResponse;\n readonly rematerialization: PrincipalContainerRematerializationInput;\n readonly resolveProjectionUserKey: ReturnType<\n typeof createProjectionUserKeyResolver\n >;\n}): Promise<MaterializedPrincipalContainerMutationPlan> {\n const { grantRow } = input;\n const rematerialization = {\n ...input.rematerialization,\n nextPolicy: await selectReplacementPrincipalPolicyReferences({\n policy: input.rematerialization.nextPolicy,\n projection: input.projection,\n principalPolicyCache: input.principalPolicyCache,\n resolveAuthoredPolicyReferences:\n input.rematerialization.resolveAuthoredPolicyReferences,\n }),\n };\n const { grant, projection, referencedKeyEpoch } =\n await loadGrantedContainerContext(\n rematerialization,\n grantRow,\n input.projection,\n input.principalPolicyCache,\n );\n const nextGrant = rematerialization.nextPolicy.grants.find(\n (candidate) => candidate.containerId === grantRow.containerId,\n );\n const author = {\n ...rematerialization.author,\n organizationId: projection.organizationId,\n };\n const sharedInput = {\n author,\n execSql: rematerialization.execSql,\n knownContainerKeks: input.knownContainerKeks,\n // Planning against a path this batch has yet to commit pins nothing; the\n // acknowledgement advances every head at once. The rekey planner applies\n // this rule itself; revoke and grant planning take it from here.\n persistVerificationCheckpoints:\n !isSpeculativeContainerWriterProjection(projection),\n previousProjection: projection,\n principalPolicyCache: input.principalPolicyCache,\n resolveProjectionUserKey: input.resolveProjectionUserKey,\n stillCurrent: rematerialization.stillCurrent,\n targetSecretKey: rematerialization.targetSecretKey,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n };\n if (grantRow.containerId === rematerialization.revokedContainerId) {\n if (!grant) {\n throw new Error(\n `Container ${grantRow.containerId} does not contain the revoked group grant`,\n );\n }\n return buildMaterializedContainerRevokePlan({\n ...sharedInput,\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n revokedSubject: {\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n if (!grant || grant.accessLevel !== nextGrant?.accessLevel) {\n if (!nextGrant) {\n throw new Error(\n `Container ${grantRow.containerId} is absent from the next group grant set`,\n );\n }\n return buildMaterializedContainerSharePlan({\n ...sharedInput,\n accessLevel: nextGrant.accessLevel,\n recipient: {\n principalPolicy: rematerialization.nextPolicy,\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n if (referencedKeyEpoch === rematerialization.nextPolicy.keyEpoch) {\n return buildMaterializedContainerSharePlan({\n ...sharedInput,\n accessLevel: grant.accessLevel,\n recipient: {\n principalPolicy: rematerialization.nextPolicy,\n subjectId: rematerialization.groupId,\n subjectType: \"group\",\n },\n });\n }\n return buildMaterializedContainerRekeyPlan({\n ...sharedInput,\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n });\n}\n\n/**\n * Plan one target against the paths already planned above it. A grant's\n * rematerialization extends the served head; a rotation's, and every carried\n * rekey, extends the epoch the batch mints above it.\n */\nasync function planRematerializationTarget(\n batch: BatchPlanning,\n target: RematerializationTarget,\n): Promise<void> {\n const { rematerialization } = batch;\n const rebased = rebaseOnDeepestAncestor(\n target.projection,\n batch.rotatedAbove,\n );\n const stale = rebased\n ? staleLevelsAbove(\n rebased.projection,\n rebased.ancestor.containerKeks.at(-1)?.containerId ?? \"\",\n )\n : [];\n for (const containerId of stale) {\n await carryLevel(\n batch,\n await loadServedProjection(rematerialization.apiClient, containerId),\n );\n }\n if (!target.grantRow) {\n await carryLevel(batch, target.projection);\n return;\n }\n // Each level carried above re-roots the target one step deeper.\n const previousProjection = await rerootOnAuthoredPaths(\n batch,\n target.projection,\n );\n const planned = await buildPrincipalContainerRematerializationPlan({\n grantRow: target.grantRow,\n knownContainerKeks: batch.knownContainerKeks,\n principalPolicyCache: batch.principalPolicyCache,\n projection: previousProjection,\n rematerialization,\n resolveProjectionUserKey: batch.resolveProjectionUserKey,\n });\n const nextProjection =\n \"keyring\" in planned.plan\n ? await containerWriterProjectionFromRotationPlan({\n plan: planned.plan,\n previousProjection,\n })\n : await containerWriterProjectionFromSharePlan({\n plan: planned.plan,\n previousProjection,\n });\n addPlan(batch, {\n carried: false,\n planned,\n rotated: \"keyring\" in planned.plan ? nextProjection : null,\n nextProjection,\n });\n}\n\n/**\n * Sign the batch parent-first. `carriedContainerIds` are the descendant rekeys\n * a refused attempt was told to carry; woven in by depth, each sits under the\n * rotation it rides, and a named container the batch rematerializes anyway is\n * re-planned there rather than rotated twice.\n */\nfunction buildPrincipalContainerRematerializationPlans(\n input: PrincipalContainerRematerializationInput,\n carriedContainerIds: readonly string[] = [],\n) {\n return planOnceMoreAfterRollback(\n input.apiClient,\n [...input.grants.map((grant) => grant.containerId), ...carriedContainerIds],\n () => planRematerializationBatch(input, carriedContainerIds),\n );\n}\n\nasync function planRematerializationBatch(\n input: PrincipalContainerRematerializationInput,\n carriedContainerIds: readonly string[],\n): Promise<{\n entries: PlannedRematerialization[];\n retiredContainerIds: readonly string[];\n preceding: PrincipalRematerializationPredecessor;\n}> {\n input = withPrecedingRematerialization(input);\n if (\n input.revokedContainerId &&\n !input.grants.some(\n (grant) => grant.containerId === input.revokedContainerId,\n )\n ) {\n throw new Error(\"Revoked container is not granted to the group\");\n }\n const { targets, retiredContainerIds } = await loadRematerializationTargets({\n apiClient: input.apiClient,\n carriedContainerIds,\n grants: input.grants,\n });\n const batch: BatchPlanning = {\n knownContainerKeks: new Map(input.preceding?.knownContainerKeks),\n plans: [],\n // A rotation planned above cites the group at the head this batch commits,\n // which no store holds yet; the cache is what lets a rebased path verify.\n principalPolicyCache: new Map([\n ...(input.preceding?.policies ?? []).flatMap((policy) => [\n ...seededPrincipalPolicyCache(policy),\n ]),\n ...seededPrincipalPolicyCache(input.nextPolicy),\n ]),\n rematerialization: input,\n resolveProjectionUserKey: createProjectionUserKeyResolver({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n }),\n rotatedAbove: [],\n authoredAbove: [],\n };\n for (const target of targets) {\n await planRematerializationTarget(batch, target);\n }\n return {\n entries: batch.plans,\n retiredContainerIds,\n preceding: {\n policies: [...(input.preceding?.policies ?? []), input.nextPolicy],\n projections: [\n ...(input.preceding?.projections ?? []),\n ...batch.plans.flatMap((entry) =>\n entry.nextProjection ? [entry.nextProjection] : [],\n ),\n ],\n knownContainerKeks: batch.knownContainerKeks,\n },\n };\n}\n\nexport async function preparePrincipalContainerRematerializationBatch(\n input: PrincipalContainerRematerializationInput,\n): Promise<PreparedPrincipalContainerRematerializationBatch> {\n let { entries, retiredContainerIds, preceding } =\n await buildPrincipalContainerRematerializationPlans(input);\n const requests = () => entries.map((entry) => entry.planned.plan.request);\n return {\n get preceding() {\n return preceding;\n },\n get retiredContainerIds() {\n return retiredContainerIds;\n },\n get plans() {\n return entries.map((entry) => entry.planned);\n },\n get requests() {\n return requests();\n },\n carry: async (requiredContainerIds) => {\n // The refused attempt rolled back whole, so every served head still\n // stands; the batch is signed again from them with the named levels in.\n const replanned = await buildPrincipalContainerRematerializationPlans(\n input,\n requiredContainerIds,\n );\n assertNamedContainersRotated(replanned.entries, requiredContainerIds);\n entries = replanned.entries;\n retiredContainerIds = replanned.retiredContainerIds;\n preceding = replanned.preceding;\n return requests();\n },\n acknowledge: async (responses, stillCurrent) => {\n const isCurrent = () =>\n input.stillCurrent?.() !== false && stillCurrent?.() !== false;\n const heads = entries.map((entry) => authoredMutationHead(entry.planned));\n const acknowledged = await acknowledgeContainerMutationBatch({\n execSql: input.execSql,\n plans: heads,\n responses,\n stillCurrent: isCurrent,\n });\n if (!acknowledged || !isCurrent()) return;\n const plansByOrganization = new Map<\n string,\n AuthoredContainerMutationHead[]\n >();\n for (const head of heads) {\n const organizationId = head.state.organizationId;\n const group = plansByOrganization.get(organizationId) ?? [];\n group.push(head);\n plansByOrganization.set(organizationId, group);\n }\n for (const [organizationId, organizationPlans] of plansByOrganization) {\n try {\n rememberVerifiedContainerHeads({\n organizationId,\n execSql: input.execSql,\n heads: [],\n policies: [input.nextPolicy],\n });\n } catch {\n // Cache failure must not invalidate the durably acknowledged batch.\n continue;\n }\n scheduleHeldDescendantRecitations({\n apiClient: input.apiClient,\n author: { ...input.author, organizationId },\n execSql: input.execSql,\n plans: organizationPlans,\n stillCurrent: input.recitationStillCurrent ?? isCurrent,\n reportSecurityIncident: input.reportSecurityIncident,\n });\n }\n },\n };\n}\n"]}
|
package/workflows/containers/rematerialization/principalContainerRematerializationPlanning.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { KeyingVerificationError, } from "../../../internal/crypto/index.js";
|
|
1
2
|
import { MAX_ROTATION_CONTAINER_REKEYS } from "../../../internal/validators/util/index.js";
|
|
2
3
|
import { verifyContainerWriterProjection, } from "../../../data/keyingProjectionVerification/index.js";
|
|
3
4
|
import { planCarriedDescendantRekey, rebaseOnDeepestAncestor, } from "../child/rekey/carriedDescendantRekeys.js";
|
|
@@ -107,4 +108,41 @@ export function staleLevelsAbove(projection, rotatedAncestorId) {
|
|
|
107
108
|
? []
|
|
108
109
|
: between.slice(firstStale).map((kek) => kek.containerId);
|
|
109
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* The server names a level it found pinned to a retired epoch. A batch answers
|
|
113
|
+
* with a rotation of it: a carried rekey, or its own rematerialization when
|
|
114
|
+
* that is a rekey or revoke. A rematerialized grant rotates nothing, so a
|
|
115
|
+
* batch that both grants a container and must re-key it cannot be signed;
|
|
116
|
+
* no policy change the SDK builds combines the two.
|
|
117
|
+
*/
|
|
118
|
+
export function assertNamedContainersRotated(plans, requiredContainerIds) {
|
|
119
|
+
const rotatedIds = new Set(plans.flatMap((entry) => entry.rotated ? [entry.planned.plan.containerId] : []));
|
|
120
|
+
const unrotated = requiredContainerIds
|
|
121
|
+
.slice(0, MAX_ROTATION_CONTAINER_REKEYS)
|
|
122
|
+
.find((containerId) => !rotatedIds.has(containerId));
|
|
123
|
+
if (unrotated !== undefined) {
|
|
124
|
+
throw new Error(`Container ${unrotated} needs a rekey this policy change only grants`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Plan once more after a rollback. A batch reads the served heads of what it
|
|
129
|
+
* signs for, then verifies them, and this device's own re-citation of held
|
|
130
|
+
* descendants, scheduled by a share above, may pin a newer head in between
|
|
131
|
+
* (#2647). The second plan reads fresh heads held to the same pins, so a
|
|
132
|
+
* server that serves a rolled-back head again still fails the batch.
|
|
133
|
+
*/
|
|
134
|
+
export async function planOnceMoreAfterRollback(apiClient, containerIds, plan) {
|
|
135
|
+
try {
|
|
136
|
+
return await plan();
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (!(error instanceof KeyingVerificationError) ||
|
|
140
|
+
error.code !== "rollback")
|
|
141
|
+
throw error;
|
|
142
|
+
for (const containerId of containerIds) {
|
|
143
|
+
apiClient.evictContainerWriterProjection?.(containerId);
|
|
144
|
+
}
|
|
145
|
+
return plan();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
110
148
|
//# sourceMappingURL=principalContainerRematerializationPlanning.js.map
|
package/workflows/containers/rematerialization/principalContainerRematerializationPlanning.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"principalContainerRematerializationPlanning.js","sourceRoot":"","sources":["principalContainerRematerializationPlanning.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { PrincipalContainerGrant } from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { MAX_ROTATION_CONTAINER_REKEYS } from \"@tearleads/validators/util\";\nimport type { ContainerReciteApi } from \"../../../data/containers/shared/reciteApi\";\nimport type { ContainerMutationAuthor } from \"../../../data/containers/shared/types\";\nimport {\n type PrincipalPolicyCache,\n type ReferencedPrincipalPolicyWarmer,\n verifyContainerWriterProjection,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { ResolvedPrincipalPolicyEvidence } from \"../../../data/keyingProjectionVerification/types\";\nimport type { createProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification/userKeyResolver\";\nimport type { CurrentPolicyReferenceResolver } from \"../../../data/principals/currentPolicyReferenceResolver\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../../data/principals/principalPolicyEvidence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport {\n planCarriedDescendantRekey,\n rebaseOnDeepestAncestor,\n type SpeculativePath,\n} from \"../child/rekey/carriedDescendantRekeys\";\nimport { isSpeculativeContainerWriterProjection } from \"../child/rekey/rekeyProjection\";\nimport type { PlannedRematerialization } from \"./principalContainerRematerializationTargets\";\n\nexport interface RematerializationApi\n extends ContainerReciteApi,\n Pick<ApiClient, \"getContainerWriterProjectionResult\"> {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n}\n\nexport interface PrincipalRematerializationPredecessor {\n readonly authored?: readonly ResolvedPrincipalPolicyEvidence[] | undefined;\n readonly policies: readonly PrincipalPolicyCurrentEvidence[];\n readonly projections: readonly ContainerWriterProjectionResponse[];\n readonly knownContainerKeks: ReadonlyMap<string, Uint8Array>;\n}\n\nexport interface PrincipalContainerRematerializationInput {\n readonly preceding?: PrincipalRematerializationPredecessor | undefined;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly apiClient: RematerializationApi;\n readonly author: ContainerMutationAuthor;\n readonly execSql: ExecSql;\n readonly grants: readonly PrincipalContainerGrant[];\n readonly groupId: string;\n readonly nextPolicy: PrincipalPolicyCurrentEvidence;\n readonly resolveAuthoredPolicyReferences?:\n | CurrentPolicyReferenceResolver\n | undefined;\n readonly revokedContainerId?: string | undefined;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n /** Session lifetime for best-effort work after durable acknowledgement. */\n readonly recitationStillCurrent?: (() => boolean) | undefined;\n readonly targetSecretKey: Uint8Array;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/** What the batch has signed so far, in the order it will be submitted. */\nexport interface BatchPlanning {\n /**\n * Keys the batch minted, by epoch id. A rotation wraps its successor to the\n * group at the head this batch commits, which no store can open yet; a\n * level planned beneath it opens that epoch with the key minted here, and\n * its keyring then opens the retired epoch the level below is pinned to.\n */\n readonly knownContainerKeks: Map<string, Uint8Array>;\n readonly plans: PlannedRematerialization[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly rematerialization: PrincipalContainerRematerializationInput;\n readonly resolveProjectionUserKey: ReturnType<\n typeof createProjectionUserKeyResolver\n >;\n readonly rotatedAbove: SpeculativePath[];\n /** Every path the batch authored, a grant's included, in signing order. */\n readonly authoredAbove: SpeculativePath[];\n}\n\n/** How many rekeys the batch carries beyond its rematerializations. */\nfunction carriedCount(batch: BatchPlanning): number {\n return batch.plans.filter((entry) => entry.carried).length;\n}\n\n/**\n * Sign a rekey of `served` under the batch's rotations above it, and add it.\n * The server accepts at most `MAX_ROTATION_CONTAINER_REKEYS` carried entries;\n * past that a rematerialization below them could not be signed on a current\n * path, so the batch is refused here, before signing what the server would\n * refuse anyway, and the policy change has to wait for a lazy repair of the\n * tree. Groups granted that far apart on one chain are not a shape the SDK\n * creates.\n */\nexport async function carryLevel(\n batch: BatchPlanning,\n served: ContainerWriterProjectionResponse,\n): Promise<void> {\n const { rematerialization } = batch;\n if (carriedCount(batch) >= MAX_ROTATION_CONTAINER_REKEYS) {\n throw new Error(\n `Policy change would carry more than ${MAX_ROTATION_CONTAINER_REKEYS} descendant rekeys`,\n );\n }\n const planned = await planCarriedDescendantRekey({\n apiClient: rematerialization.apiClient,\n author: rematerialization.author,\n execSql: rematerialization.execSql,\n knownContainerKeks: batch.knownContainerKeks,\n principalPolicyCache: batch.principalPolicyCache,\n // Its path cites the policy this batch commits, so like every\n // rematerialized rotation it must cite the successor.\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n resolveAuthoredPolicyReferences:\n rematerialization.resolveAuthoredPolicyReferences,\n resolveProjectionUserKey: batch.resolveProjectionUserKey,\n rotated: batch.rotatedAbove,\n served,\n stillCurrent: rematerialization.stillCurrent,\n targetSecretKey: rematerialization.targetSecretKey,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n });\n addPlan(batch, {\n carried: true,\n planned,\n rotated: planned.writerProjection,\n nextProjection: planned.writerProjection,\n });\n}\n\n/**\n * Re-root a granted target on the deepest path the batch authored above it, a\n * grant's included: the server applies the batch in order, so the target\n * cites every head an earlier entry advances. Its served head is pinned first,\n * as a carried rekey's is. The batch is acknowledged against the latest\n * durable pin, and a re-rooted path pins nothing, so a device that never\n * pinned this container would refuse its own batch once the server took it.\n * Only what the API served is pinned: in a compound transaction the target\n * may already sit on the preceding batch's heads, which a refused commit\n * never makes real.\n */\nexport async function rerootOnAuthoredPaths(\n batch: BatchPlanning,\n served: ContainerWriterProjectionResponse,\n): Promise<ContainerWriterProjectionResponse> {\n const rebased = rebaseOnDeepestAncestor(served, batch.authoredAbove);\n if (!rebased) return served;\n if (isSpeculativeContainerWriterProjection(served)) {\n return rebased.projection;\n }\n const { rematerialization } = batch;\n await verifyContainerWriterProjection({\n execSql: rematerialization.execSql,\n principalPolicyCache: batch.principalPolicyCache,\n projection: served,\n resolveUserKey: batch.resolveProjectionUserKey,\n stillCurrent: rematerialization.stillCurrent,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n });\n return rebased.projection;\n}\n\nexport function addPlan(\n batch: BatchPlanning,\n entry: PlannedRematerialization,\n): void {\n batch.plans.push(entry);\n if (entry.nextProjection) batch.authoredAbove.push(entry.nextProjection);\n if (!entry.rotated) return;\n batch.rotatedAbove.push(entry.rotated);\n const minted = entry.rotated.containerKeks.at(-1)?.containerKeyEpochId;\n if (minted) batch.knownContainerKeks.set(minted, entry.planned.containerKey);\n}\n\n/**\n * Levels of a re-rooted path pinned to an epoch the batch retires above them:\n * those strictly between the rotated ancestor the path was re-rooted on and\n * the target, parent-first, from the first stale one down. A rotation planned\n * above a target strands every level between the two, and each must be\n * re-keyed under the one above before the target can be signed. The batch\n * carries them without waiting to be told: they are proper ancestors of a\n * granted container, so the server would name them, and a refusal per level\n * is a round trip each. Anything stale above the rotated ancestor is not this\n * batch's doing and is left alone. `carryLevel` holds the count to the cap.\n */\nexport function staleLevelsAbove(\n projection: ContainerWriterProjectionResponse,\n rotatedAncestorId: string,\n): string[] {\n const keks = projection.containerKeks;\n const ancestor = keks.findIndex(\n (kek) => kek.containerId === rotatedAncestorId,\n );\n if (ancestor < 0) return [];\n const between = keks.slice(ancestor, -1);\n const firstStale = between.findIndex(\n (kek, index) =>\n index > 0 &&\n kek.parentContainerKeyEpochId !== between[index - 1]?.containerKeyEpochId,\n );\n return firstStale < 0\n ? []\n : between.slice(firstStale).map((kek) => kek.containerId);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"principalContainerRematerializationPlanning.js","sourceRoot":"","sources":["principalContainerRematerializationPlanning.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,GAExB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAG3E,OAAO,EAGL,+BAA+B,GAChC,MAAM,4CAA4C,CAAC;AAQpD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAExB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,sCAAsC,EAAE,MAAM,gCAAgC,CAAC;AA8DxF,uEAAuE;AACvE,SAAS,YAAY,CAAC,KAAoB;IACxC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,KAAoB,EACpB,MAAyC;IAEzC,MAAM,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC;IACpC,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,6BAA6B,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,uCAAuC,6BAA6B,oBAAoB,CACzF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,0BAA0B,CAAC;QAC/C,SAAS,EAAE,iBAAiB,CAAC,SAAS;QACtC,MAAM,EAAE,iBAAiB,CAAC,MAAM;QAChC,OAAO,EAAE,iBAAiB,CAAC,OAAO;QAClC,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,8DAA8D;QAC9D,sDAAsD;QACtD,0BAA0B,EAAE,iBAAiB,CAAC,UAAU;QACxD,+BAA+B,EAC7B,iBAAiB,CAAC,+BAA+B;QACnD,wBAAwB,EAAE,KAAK,CAAC,wBAAwB;QACxD,OAAO,EAAE,KAAK,CAAC,YAAY;QAC3B,MAAM;QACN,YAAY,EAAE,iBAAiB,CAAC,YAAY;QAC5C,eAAe,EAAE,iBAAiB,CAAC,eAAe;QAClD,+BAA+B,EAC7B,iBAAiB,CAAC,+BAA+B;KACpD,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,EAAE;QACb,OAAO,EAAE,IAAI;QACb,OAAO;QACP,OAAO,EAAE,OAAO,CAAC,gBAAgB;QACjC,cAAc,EAAE,OAAO,CAAC,gBAAgB;KACzC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAoB,EACpB,MAAyC;IAEzC,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC;IAC5B,IAAI,sCAAsC,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,OAAO,OAAO,CAAC,UAAU,CAAC;IAC5B,CAAC;IACD,MAAM,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC;IACpC,MAAM,+BAA+B,CAAC;QACpC,OAAO,EAAE,iBAAiB,CAAC,OAAO;QAClC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;QAChD,UAAU,EAAE,MAAM;QAClB,cAAc,EAAE,KAAK,CAAC,wBAAwB;QAC9C,YAAY,EAAE,iBAAiB,CAAC,YAAY;QAC5C,+BAA+B,EAC7B,iBAAiB,CAAC,+BAA+B;KACpD,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,UAAU,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,KAAoB,EACpB,KAA+B;IAE/B,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,KAAK,CAAC,cAAc;QAAE,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO;IAC3B,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC;IACvE,IAAI,MAAM;QAAE,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA6C,EAC7C,iBAAyB;IAEzB,MAAM,IAAI,GAAG,UAAU,CAAC,aAAa,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAC7B,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,KAAK,iBAAiB,CAC/C,CAAC;IACF,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAClC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,KAAK,GAAG,CAAC;QACT,GAAG,CAAC,yBAAyB,KAAK,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,mBAAmB,CAC5E,CAAC;IACF,OAAO,UAAU,GAAG,CAAC;QACnB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAA0C,EAC1C,oBAAuC;IAEvC,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CACtB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CACtD,CACF,CAAC;IACF,MAAM,SAAS,GAAG,oBAAoB;SACnC,KAAK,CAAC,CAAC,EAAE,6BAA6B,CAAC;SACvC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;IACvD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,aAAa,SAAS,+CAA+C,CACtE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,SAAuE,EACvE,YAA+B,EAC/B,IAAsB;IAEtB,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,CAAC,CAAC,KAAK,YAAY,uBAAuB,CAAC;YAC3C,KAAK,CAAC,IAAI,KAAK,UAAU;YAEzB,MAAM,KAAK,CAAC;QACd,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACvC,SAAS,CAAC,8BAA8B,EAAE,CAAC,WAAW,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;AACH,CAAC","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type PrincipalContainerGrant,\n} from \"@tearleads/crypto\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport { MAX_ROTATION_CONTAINER_REKEYS } from \"@tearleads/validators/util\";\nimport type { ContainerReciteApi } from \"../../../data/containers/shared/reciteApi\";\nimport type { ContainerMutationAuthor } from \"../../../data/containers/shared/types\";\nimport {\n type PrincipalPolicyCache,\n type ReferencedPrincipalPolicyWarmer,\n verifyContainerWriterProjection,\n} from \"../../../data/keyingProjectionVerification\";\nimport type { ResolvedPrincipalPolicyEvidence } from \"../../../data/keyingProjectionVerification/types\";\nimport type { createProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification/userKeyResolver\";\nimport type { CurrentPolicyReferenceResolver } from \"../../../data/principals/currentPolicyReferenceResolver\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../../data/principals/principalPolicyEvidence\";\nimport type { SecurityIncidentReporter } from \"../../../data/shared/securityIncidents\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport {\n planCarriedDescendantRekey,\n rebaseOnDeepestAncestor,\n type SpeculativePath,\n} from \"../child/rekey/carriedDescendantRekeys\";\nimport { isSpeculativeContainerWriterProjection } from \"../child/rekey/rekeyProjection\";\nimport type { PlannedRematerialization } from \"./principalContainerRematerializationTargets\";\n\nexport interface RematerializationApi\n extends ContainerReciteApi,\n Pick<ApiClient, \"getContainerWriterProjectionResult\"> {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n evictContainerWriterProjection?(containerId: string): void;\n}\n\nexport interface PrincipalRematerializationPredecessor {\n readonly authored?: readonly ResolvedPrincipalPolicyEvidence[] | undefined;\n readonly policies: readonly PrincipalPolicyCurrentEvidence[];\n readonly projections: readonly ContainerWriterProjectionResponse[];\n readonly knownContainerKeks: ReadonlyMap<string, Uint8Array>;\n}\n\nexport interface PrincipalContainerRematerializationInput {\n readonly preceding?: PrincipalRematerializationPredecessor | undefined;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly apiClient: RematerializationApi;\n readonly author: ContainerMutationAuthor;\n readonly execSql: ExecSql;\n readonly grants: readonly PrincipalContainerGrant[];\n readonly groupId: string;\n readonly nextPolicy: PrincipalPolicyCurrentEvidence;\n readonly resolveAuthoredPolicyReferences?:\n | CurrentPolicyReferenceResolver\n | undefined;\n readonly revokedContainerId?: string | undefined;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n /** Session lifetime for best-effort work after durable acknowledgement. */\n readonly recitationStillCurrent?: (() => boolean) | undefined;\n readonly targetSecretKey: Uint8Array;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/** What the batch has signed so far, in the order it will be submitted. */\nexport interface BatchPlanning {\n /**\n * Keys the batch minted, by epoch id. A rotation wraps its successor to the\n * group at the head this batch commits, which no store can open yet; a\n * level planned beneath it opens that epoch with the key minted here, and\n * its keyring then opens the retired epoch the level below is pinned to.\n */\n readonly knownContainerKeks: Map<string, Uint8Array>;\n readonly plans: PlannedRematerialization[];\n readonly principalPolicyCache: PrincipalPolicyCache;\n readonly rematerialization: PrincipalContainerRematerializationInput;\n readonly resolveProjectionUserKey: ReturnType<\n typeof createProjectionUserKeyResolver\n >;\n readonly rotatedAbove: SpeculativePath[];\n /** Every path the batch authored, a grant's included, in signing order. */\n readonly authoredAbove: SpeculativePath[];\n}\n\n/** How many rekeys the batch carries beyond its rematerializations. */\nfunction carriedCount(batch: BatchPlanning): number {\n return batch.plans.filter((entry) => entry.carried).length;\n}\n\n/**\n * Sign a rekey of `served` under the batch's rotations above it, and add it.\n * The server accepts at most `MAX_ROTATION_CONTAINER_REKEYS` carried entries;\n * past that a rematerialization below them could not be signed on a current\n * path, so the batch is refused here, before signing what the server would\n * refuse anyway, and the policy change has to wait for a lazy repair of the\n * tree. Groups granted that far apart on one chain are not a shape the SDK\n * creates.\n */\nexport async function carryLevel(\n batch: BatchPlanning,\n served: ContainerWriterProjectionResponse,\n): Promise<void> {\n const { rematerialization } = batch;\n if (carriedCount(batch) >= MAX_ROTATION_CONTAINER_REKEYS) {\n throw new Error(\n `Policy change would carry more than ${MAX_ROTATION_CONTAINER_REKEYS} descendant rekeys`,\n );\n }\n const planned = await planCarriedDescendantRekey({\n apiClient: rematerialization.apiClient,\n author: rematerialization.author,\n execSql: rematerialization.execSql,\n knownContainerKeks: batch.knownContainerKeks,\n principalPolicyCache: batch.principalPolicyCache,\n // Its path cites the policy this batch commits, so like every\n // rematerialized rotation it must cite the successor.\n replacementPrincipalPolicy: rematerialization.nextPolicy,\n resolveAuthoredPolicyReferences:\n rematerialization.resolveAuthoredPolicyReferences,\n resolveProjectionUserKey: batch.resolveProjectionUserKey,\n rotated: batch.rotatedAbove,\n served,\n stillCurrent: rematerialization.stillCurrent,\n targetSecretKey: rematerialization.targetSecretKey,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n });\n addPlan(batch, {\n carried: true,\n planned,\n rotated: planned.writerProjection,\n nextProjection: planned.writerProjection,\n });\n}\n\n/**\n * Re-root a granted target on the deepest path the batch authored above it, a\n * grant's included: the server applies the batch in order, so the target\n * cites every head an earlier entry advances. Its served head is pinned first,\n * as a carried rekey's is. The batch is acknowledged against the latest\n * durable pin, and a re-rooted path pins nothing, so a device that never\n * pinned this container would refuse its own batch once the server took it.\n * Only what the API served is pinned: in a compound transaction the target\n * may already sit on the preceding batch's heads, which a refused commit\n * never makes real.\n */\nexport async function rerootOnAuthoredPaths(\n batch: BatchPlanning,\n served: ContainerWriterProjectionResponse,\n): Promise<ContainerWriterProjectionResponse> {\n const rebased = rebaseOnDeepestAncestor(served, batch.authoredAbove);\n if (!rebased) return served;\n if (isSpeculativeContainerWriterProjection(served)) {\n return rebased.projection;\n }\n const { rematerialization } = batch;\n await verifyContainerWriterProjection({\n execSql: rematerialization.execSql,\n principalPolicyCache: batch.principalPolicyCache,\n projection: served,\n resolveUserKey: batch.resolveProjectionUserKey,\n stillCurrent: rematerialization.stillCurrent,\n warmReferencedPrincipalPolicies:\n rematerialization.warmReferencedPrincipalPolicies,\n });\n return rebased.projection;\n}\n\nexport function addPlan(\n batch: BatchPlanning,\n entry: PlannedRematerialization,\n): void {\n batch.plans.push(entry);\n if (entry.nextProjection) batch.authoredAbove.push(entry.nextProjection);\n if (!entry.rotated) return;\n batch.rotatedAbove.push(entry.rotated);\n const minted = entry.rotated.containerKeks.at(-1)?.containerKeyEpochId;\n if (minted) batch.knownContainerKeks.set(minted, entry.planned.containerKey);\n}\n\n/**\n * Levels of a re-rooted path pinned to an epoch the batch retires above them:\n * those strictly between the rotated ancestor the path was re-rooted on and\n * the target, parent-first, from the first stale one down. A rotation planned\n * above a target strands every level between the two, and each must be\n * re-keyed under the one above before the target can be signed. The batch\n * carries them without waiting to be told: they are proper ancestors of a\n * granted container, so the server would name them, and a refusal per level\n * is a round trip each. Anything stale above the rotated ancestor is not this\n * batch's doing and is left alone. `carryLevel` holds the count to the cap.\n */\nexport function staleLevelsAbove(\n projection: ContainerWriterProjectionResponse,\n rotatedAncestorId: string,\n): string[] {\n const keks = projection.containerKeks;\n const ancestor = keks.findIndex(\n (kek) => kek.containerId === rotatedAncestorId,\n );\n if (ancestor < 0) return [];\n const between = keks.slice(ancestor, -1);\n const firstStale = between.findIndex(\n (kek, index) =>\n index > 0 &&\n kek.parentContainerKeyEpochId !== between[index - 1]?.containerKeyEpochId,\n );\n return firstStale < 0\n ? []\n : between.slice(firstStale).map((kek) => kek.containerId);\n}\n\n/**\n * The server names a level it found pinned to a retired epoch. A batch answers\n * with a rotation of it: a carried rekey, or its own rematerialization when\n * that is a rekey or revoke. A rematerialized grant rotates nothing, so a\n * batch that both grants a container and must re-key it cannot be signed;\n * no policy change the SDK builds combines the two.\n */\nexport function assertNamedContainersRotated(\n plans: readonly PlannedRematerialization[],\n requiredContainerIds: readonly string[],\n): void {\n const rotatedIds = new Set(\n plans.flatMap((entry) =>\n entry.rotated ? [entry.planned.plan.containerId] : [],\n ),\n );\n const unrotated = requiredContainerIds\n .slice(0, MAX_ROTATION_CONTAINER_REKEYS)\n .find((containerId) => !rotatedIds.has(containerId));\n if (unrotated !== undefined) {\n throw new Error(\n `Container ${unrotated} needs a rekey this policy change only grants`,\n );\n }\n}\n\n/**\n * Plan once more after a rollback. A batch reads the served heads of what it\n * signs for, then verifies them, and this device's own re-citation of held\n * descendants, scheduled by a share above, may pin a newer head in between\n * (#2647). The second plan reads fresh heads held to the same pins, so a\n * server that serves a rolled-back head again still fails the batch.\n */\nexport async function planOnceMoreAfterRollback<T>(\n apiClient: Pick<RematerializationApi, \"evictContainerWriterProjection\">,\n containerIds: readonly string[],\n plan: () => Promise<T>,\n): Promise<T> {\n try {\n return await plan();\n } catch (error) {\n if (\n !(error instanceof KeyingVerificationError) ||\n error.code !== \"rollback\"\n )\n throw error;\n for (const containerId of containerIds) {\n apiClient.evictContainerWriterProjection?.(containerId);\n }\n return plan();\n }\n}\n"]}
|
|
@@ -8,6 +8,7 @@ import type { TrustedUserIdentityResolver } from "../../../data/trustedUserIdent
|
|
|
8
8
|
import type { CurrentPrincipalPolicyReader } from "../../principals/currentPrincipalPolicyReader.js";
|
|
9
9
|
import { type GroupPolicyNameReader } from "../principalPolicy/principalPolicyRequest.js";
|
|
10
10
|
export interface ContainerManagedPrincipalShareApi extends ContainerShareApi, CurrentPrincipalPolicyReader, Pick<ApiClient, "evictContainerWriterProjection" | "getContainerWriterProjectionResult"> {
|
|
11
|
+
evictContainerWriterProjection(containerId: string): void;
|
|
11
12
|
readonly recoverPendingPrincipalMutation?: ((organizationId: string) => Promise<void>) | undefined;
|
|
12
13
|
commitOrganizationGroupPolicy: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest) => Promise<CommitOrganizationGroupPolicyResponse | null>;
|
|
13
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sharePrincipalPolicy.js","sourceRoot":"","sources":["sharePrincipalPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { CommitOrganizationGroupPolicyRequest } from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n PrincipalPolicyBundleResponse,\n} from \"@tearleads/validators/response\";\nimport type { ContainerShareApi } from \"../../../data/containers/shared/types\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../../data/keyingProjectionVerification/error\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../../data/persistence/keyingCheckpoints/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpoints/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicy/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicy/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/principalPolicy/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../../principals/externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"../../principals/policyVerification\";\nimport { assertGroupMetadataBinding } from \"../groupMetadata/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../principalPolicy/principalPolicyRequest\";\n\nexport interface ContainerManagedPrincipalShareApi\n extends ContainerShareApi,\n CurrentPrincipalPolicyReader,\n Pick<\n ApiClient,\n \"evictContainerWriterProjection\" | \"getContainerWriterProjectionResult\"\n > {\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n}\n\nexport interface VerifiedSharePrincipalPolicy {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly checkpointPolicies: readonly VerifiedPrincipalPolicy[];\n readonly dependencyBundles: readonly PrincipalPolicyBundleResponse[];\n readonly organizationId: string;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\nasync function retainVerifiedSharePolicies(input: {\n bundle: PrincipalPolicyBundleResponse;\n execSql: ExecSql;\n organizationId: string;\n organizationPolicy: VerifiedExternalAdminPolicy | null;\n policy: VerifiedPrincipalPolicy;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const retainedAt = new Date().toISOString();\n if (input.organizationPolicy) {\n for (const entry of externalAdminPolicyPersistenceEntries(\n input.organizationPolicy,\n )) {\n await retainVerifiedPrincipalPolicyBundle({\n ...entry,\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n }\n }\n await retainVerifiedPrincipalPolicyBundle({\n bundle: input.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: input.policy,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n return [\n ...(input.organizationPolicy\n ? externalAdminPolicyPersistenceEntries(input.organizationPolicy).map(\n (entry) => entry.policy,\n )\n : []),\n input.policy,\n ];\n}\n\n/**\n * Commits the verification a loadVerifiedGroupSharePrincipalPolicy call\n * deferred: advances the keying checkpoints for the verified policies and\n * caches the dependency bundles they were verified against.\n */\nexport async function advanceVerifiedSharePolicies(\n execSql: ExecSql,\n verified: Pick<\n VerifiedSharePrincipalPolicy,\n \"checkpointPolicies\" | \"dependencyBundles\" | \"organizationId\"\n >,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql,\n organizationId: verified.organizationId,\n policies: verified.checkpointPolicies,\n stillCurrent,\n });\n if (stillCurrent?.() === false) return;\n for (const bundle of verified.dependencyBundles) {\n await savePrincipalPolicyBundle(\n execSql,\n bundle,\n new Date().toISOString(),\n verified.organizationId,\n { stillCurrent },\n );\n if (stillCurrent?.() === false) return;\n }\n}\n\nfunction assertGroupPolicyTarget(\n bundle: PrincipalPolicyBundleResponse,\n groupId: string,\n): void {\n if (\n bundle.currentState.principalType !== \"group\" ||\n bundle.currentState.principalId !== groupId\n ) {\n throw new Error(\"Container share principal policy target mismatch\");\n }\n}\n\nfunction signerPublicKeyLoadErrorMessage(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): string {\n switch (code) {\n case \"fingerprint-mismatch\":\n return \"principal policy signer key fingerprint mismatch\";\n case \"not-found\":\n return \"principal policy signer key could not be loaded\";\n }\n}\n\nasync function loadGroupSharePolicyBundle(input: {\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n groupId: string;\n localCheckpoint: PrincipalPolicyCheckpoint | null;\n}): Promise<PrincipalPolicyBundleResponse> {\n if (\n input.expectedGroupHead &&\n (input.expectedGroupHead.principalType !== \"group\" ||\n input.expectedGroupHead.principalId !== input.groupId)\n ) {\n throw new Error(\"Container share expected group policy target mismatch\");\n }\n const bundleFromCache = input.expectedGroupHead\n ? await loadPrincipalPolicyBundleForReference(\n input.execSql,\n input.expectedGroupHead,\n input.localCheckpoint,\n )\n : null;\n let bundle = bundleFromCache;\n bundle ??= await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n if (!bundle) {\n throw new Error(\"Container share principal policy could not be loaded\");\n }\n assertGroupPolicyTarget(bundle, input.groupId);\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedGroupHead,\n )\n ) {\n throw new Error(\n \"Container share group policy does not match the signed organization directory\",\n );\n }\n return bundle;\n}\n\n/**\n * The chosen group label does not match the name in the group's verified\n * policy. A KeyingVerificationError (so it is reported as an incident), with\n * its own class so a caller can tell this refusal from every other\n * `object_mismatch` and say what happened.\n */\nexport class GroupShareNameMismatchError extends KeyingVerificationError {\n constructor(message: string) {\n super(\"object_mismatch\", message);\n this.name = \"GroupShareNameMismatchError\";\n }\n}\n\n/**\n * The share picker labels groups from the organization read model, which a\n * compromised server can relabel, so the name the user chose must equal the\n * name committed in the target's verified policy. Names compare by their\n * canonical key so look-alike spellings count as the same name. Uniqueness of\n * signed names within an organization is enforced where names enter — group\n * creation verifies every group in the signed directory — so a single\n * verified match identifies the group.\n */\nasync function assertShareGroupName(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n bundle: PrincipalPolicyBundleResponse;\n expectedGroupName: string;\n}): Promise<void> {\n // The label comes from the untrusted read model. Signed names never carry\n // an invisible code point, so a label that does is a look-alike that would\n // only match after canonicalization strips it; refuse it outright.\n const mismatch = await groupPolicyNameMismatch(\n input.bundle,\n input.expectedGroupName,\n input.readEncryptedName,\n );\n if (mismatch === \"forbidden_characters\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name contains control or format characters\",\n );\n }\n if (mismatch === \"name_mismatch\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name does not match the signed group policy\",\n );\n }\n}\n\nexport async function loadVerifiedGroupSharePrincipalPolicy(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n /**\n * The display name the user chose the group by. The read model that labels\n * groups is a server projection; only the name committed in the signed\n * group payload can confirm the share lands on the group the user saw.\n */\n expectedGroupName?: string | undefined;\n groupId: string;\n organizationId: string;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedSharePrincipalPolicy> {\n const organizationAdminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n if (!organizationAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const committedGroupHead = requireOrganizationGroupHead(\n organizationAdminPolicy.descriptor,\n input.groupId,\n );\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(input.expectedGroupHead, committedGroupHead)\n ) {\n throw new Error(\n \"Container share expected group head conflicts with the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.groupId,\n );\n const bundle = await loadGroupSharePolicyBundle({\n ...input,\n expectedGroupHead: committedGroupHead,\n localCheckpoint,\n });\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(signerPublicKeyLoadErrorMessage(signerPublicKeys.error));\n }\n\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: committedGroupHead,\n loadExternalAuthority: async () =>\n organizationAdminPolicy.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Container share principal policy verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, organizationAdminPolicy.descriptor);\n if (input.expectedGroupName !== undefined) {\n await assertShareGroupName({\n readEncryptedName: input.readEncryptedName,\n bundle,\n expectedGroupName: input.expectedGroupName,\n });\n }\n\n const checkpointPolicies = await retainVerifiedSharePolicies({\n bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n organizationPolicy: organizationAdminPolicy,\n policy: verified.value,\n stillCurrent: input.stillCurrent,\n });\n return {\n bundle,\n checkpointPolicies,\n dependencyBundles: externalAdminPolicyPersistenceEntries(\n organizationAdminPolicy,\n ).map((entry) => entry.bundle),\n organizationId: input.organizationId,\n policy: verified.value,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sharePrincipalPolicy.js","sourceRoot":"","sources":["sharePrincipalPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport {\n KeyingVerificationError,\n type PrincipalPolicyCheckpoint,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { CommitOrganizationGroupPolicyRequest } from \"@tearleads/validators/request\";\nimport type {\n CommitOrganizationGroupPolicyResponse,\n PrincipalPolicyBundleResponse,\n} from \"@tearleads/validators/response\";\nimport type { ContainerShareApi } from \"../../../data/containers/shared/types\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../../data/keyingProjectionVerification/error\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../../data/persistence/keyingCheckpoints/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpoints/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicy/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicy/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/principalPolicy/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../../data/trustedUserIdentity\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../../principals/externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"../../principals/policyVerification\";\nimport { assertGroupMetadataBinding } from \"../groupMetadata/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../principalPolicy/principalPolicyRequest\";\n\nexport interface ContainerManagedPrincipalShareApi\n extends ContainerShareApi,\n CurrentPrincipalPolicyReader,\n Pick<\n ApiClient,\n \"evictContainerWriterProjection\" | \"getContainerWriterProjectionResult\"\n > {\n evictContainerWriterProjection(containerId: string): void;\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n commitOrganizationGroupPolicy: (\n organizationId: string,\n groupId: string,\n input: CommitOrganizationGroupPolicyRequest,\n ) => Promise<CommitOrganizationGroupPolicyResponse | null>;\n}\n\nexport interface VerifiedSharePrincipalPolicy {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly checkpointPolicies: readonly VerifiedPrincipalPolicy[];\n readonly dependencyBundles: readonly PrincipalPolicyBundleResponse[];\n readonly organizationId: string;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\nasync function retainVerifiedSharePolicies(input: {\n bundle: PrincipalPolicyBundleResponse;\n execSql: ExecSql;\n organizationId: string;\n organizationPolicy: VerifiedExternalAdminPolicy | null;\n policy: VerifiedPrincipalPolicy;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const retainedAt = new Date().toISOString();\n if (input.organizationPolicy) {\n for (const entry of externalAdminPolicyPersistenceEntries(\n input.organizationPolicy,\n )) {\n await retainVerifiedPrincipalPolicyBundle({\n ...entry,\n execSql: input.execSql,\n organizationId: input.organizationId,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n }\n }\n await retainVerifiedPrincipalPolicyBundle({\n bundle: input.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: input.policy,\n stillCurrent: input.stillCurrent,\n updatedAt: retainedAt,\n });\n return [\n ...(input.organizationPolicy\n ? externalAdminPolicyPersistenceEntries(input.organizationPolicy).map(\n (entry) => entry.policy,\n )\n : []),\n input.policy,\n ];\n}\n\n/**\n * Commits the verification a loadVerifiedGroupSharePrincipalPolicy call\n * deferred: advances the keying checkpoints for the verified policies and\n * caches the dependency bundles they were verified against.\n */\nexport async function advanceVerifiedSharePolicies(\n execSql: ExecSql,\n verified: Pick<\n VerifiedSharePrincipalPolicy,\n \"checkpointPolicies\" | \"dependencyBundles\" | \"organizationId\"\n >,\n stillCurrent?: (() => boolean) | undefined,\n): Promise<void> {\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql,\n organizationId: verified.organizationId,\n policies: verified.checkpointPolicies,\n stillCurrent,\n });\n if (stillCurrent?.() === false) return;\n for (const bundle of verified.dependencyBundles) {\n await savePrincipalPolicyBundle(\n execSql,\n bundle,\n new Date().toISOString(),\n verified.organizationId,\n { stillCurrent },\n );\n if (stillCurrent?.() === false) return;\n }\n}\n\nfunction assertGroupPolicyTarget(\n bundle: PrincipalPolicyBundleResponse,\n groupId: string,\n): void {\n if (\n bundle.currentState.principalType !== \"group\" ||\n bundle.currentState.principalId !== groupId\n ) {\n throw new Error(\"Container share principal policy target mismatch\");\n }\n}\n\nfunction signerPublicKeyLoadErrorMessage(\n code: PrincipalPolicySignerPublicKeyLoadErrorCode,\n): string {\n switch (code) {\n case \"fingerprint-mismatch\":\n return \"principal policy signer key fingerprint mismatch\";\n case \"not-found\":\n return \"principal policy signer key could not be loaded\";\n }\n}\n\nasync function loadGroupSharePolicyBundle(input: {\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n groupId: string;\n localCheckpoint: PrincipalPolicyCheckpoint | null;\n}): Promise<PrincipalPolicyBundleResponse> {\n if (\n input.expectedGroupHead &&\n (input.expectedGroupHead.principalType !== \"group\" ||\n input.expectedGroupHead.principalId !== input.groupId)\n ) {\n throw new Error(\"Container share expected group policy target mismatch\");\n }\n const bundleFromCache = input.expectedGroupHead\n ? await loadPrincipalPolicyBundleForReference(\n input.execSql,\n input.expectedGroupHead,\n input.localCheckpoint,\n )\n : null;\n let bundle = bundleFromCache;\n bundle ??= await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n input.groupId,\n );\n if (!bundle) {\n throw new Error(\"Container share principal policy could not be loaded\");\n }\n assertGroupPolicyTarget(bundle, input.groupId);\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n input.expectedGroupHead,\n )\n ) {\n throw new Error(\n \"Container share group policy does not match the signed organization directory\",\n );\n }\n return bundle;\n}\n\n/**\n * The chosen group label does not match the name in the group's verified\n * policy. A KeyingVerificationError (so it is reported as an incident), with\n * its own class so a caller can tell this refusal from every other\n * `object_mismatch` and say what happened.\n */\nexport class GroupShareNameMismatchError extends KeyingVerificationError {\n constructor(message: string) {\n super(\"object_mismatch\", message);\n this.name = \"GroupShareNameMismatchError\";\n }\n}\n\n/**\n * The share picker labels groups from the organization read model, which a\n * compromised server can relabel, so the name the user chose must equal the\n * name committed in the target's verified policy. Names compare by their\n * canonical key so look-alike spellings count as the same name. Uniqueness of\n * signed names within an organization is enforced where names enter — group\n * creation verifies every group in the signed directory — so a single\n * verified match identifies the group.\n */\nasync function assertShareGroupName(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n bundle: PrincipalPolicyBundleResponse;\n expectedGroupName: string;\n}): Promise<void> {\n // The label comes from the untrusted read model. Signed names never carry\n // an invisible code point, so a label that does is a look-alike that would\n // only match after canonicalization strips it; refuse it outright.\n const mismatch = await groupPolicyNameMismatch(\n input.bundle,\n input.expectedGroupName,\n input.readEncryptedName,\n );\n if (mismatch === \"forbidden_characters\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name contains control or format characters\",\n );\n }\n if (mismatch === \"name_mismatch\") {\n throw new GroupShareNameMismatchError(\n \"Container share group name does not match the signed group policy\",\n );\n }\n}\n\nexport async function loadVerifiedGroupSharePrincipalPolicy(input: {\n readEncryptedName?: GroupPolicyNameReader | undefined;\n apiClient: ContainerManagedPrincipalShareApi;\n execSql: ExecSql;\n expectedGroupHead?: ReferencedPrincipalHead | undefined;\n /**\n * The display name the user chose the group by. The read model that labels\n * groups is a server projection; only the name committed in the signed\n * group payload can confirm the share lands on the group the user saw.\n */\n expectedGroupName?: string | undefined;\n groupId: string;\n organizationId: string;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedSharePrincipalPolicy> {\n const organizationAdminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n if (!organizationAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const committedGroupHead = requireOrganizationGroupHead(\n organizationAdminPolicy.descriptor,\n input.groupId,\n );\n if (\n input.expectedGroupHead &&\n !principalHeadMatchesReference(input.expectedGroupHead, committedGroupHead)\n ) {\n throw new Error(\n \"Container share expected group head conflicts with the signed organization directory\",\n );\n }\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n input.groupId,\n );\n const bundle = await loadGroupSharePolicyBundle({\n ...input,\n expectedGroupHead: committedGroupHead,\n localCheckpoint,\n });\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(signerPublicKeyLoadErrorMessage(signerPublicKeys.error));\n }\n\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: committedGroupHead,\n loadExternalAuthority: async () =>\n organizationAdminPolicy.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Container share principal policy verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, organizationAdminPolicy.descriptor);\n if (input.expectedGroupName !== undefined) {\n await assertShareGroupName({\n readEncryptedName: input.readEncryptedName,\n bundle,\n expectedGroupName: input.expectedGroupName,\n });\n }\n\n const checkpointPolicies = await retainVerifiedSharePolicies({\n bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n organizationPolicy: organizationAdminPolicy,\n policy: verified.value,\n stillCurrent: input.stillCurrent,\n });\n return {\n bundle,\n checkpointPolicies,\n dependencyBundles: externalAdminPolicyPersistenceEntries(\n organizationAdminPolicy,\n ).map((entry) => entry.bundle),\n organizationId: input.organizationId,\n policy: verified.value,\n };\n}\n"]}
|