@tearleads/client-sdk 0.1.55 → 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.
Files changed (31) hide show
  1. package/data/containers/shared/types.d.ts +1 -2
  2. package/data/containers/shared/types.js.map +1 -1
  3. package/package.json +1 -1
  4. package/workflows/container-contents/container-state/types.js.map +1 -1
  5. package/workflows/container-contents/remoteHydration/types.d.ts +5 -4
  6. package/workflows/container-contents/remoteHydration/types.js.map +1 -1
  7. package/workflows/containers/child/sharePrincipalPolicy.d.ts +3 -3
  8. package/workflows/containers/child/sharePrincipalPolicy.js +1 -1
  9. package/workflows/containers/child/sharePrincipalPolicy.js.map +1 -1
  10. package/workflows/organizations/deleteOrganizationGroup.js +1 -1
  11. package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
  12. package/workflows/organizations/groupCreation.js +1 -1
  13. package/workflows/organizations/groupCreation.js.map +1 -1
  14. package/workflows/organizations/groupNameUniqueness.js.map +1 -1
  15. package/workflows/organizations/groupPolicyMutationContext.js +1 -1
  16. package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
  17. package/workflows/organizations/organizationGroupNamePolicies.js +19 -5
  18. package/workflows/organizations/organizationGroupNamePolicies.js.map +1 -1
  19. package/workflows/organizations/principalPolicyApi.d.ts +3 -3
  20. package/workflows/organizations/principalPolicyApi.js.map +1 -1
  21. package/workflows/principals/currentPrincipalPolicyReader.d.ts +12 -0
  22. package/workflows/principals/currentPrincipalPolicyReader.js +2 -0
  23. package/workflows/principals/currentPrincipalPolicyReader.js.map +1 -0
  24. package/workflows/principals/externalAdminPolicy.d.ts +2 -1
  25. package/workflows/principals/externalAdminPolicy.js +3 -3
  26. package/workflows/principals/externalAdminPolicy.js.map +1 -1
  27. package/workflows/principals/policyCache.d.ts +3 -2
  28. package/workflows/principals/policyCache.js +9 -9
  29. package/workflows/principals/policyCache.js.map +1 -1
  30. package/workflows/principals/principalPolicyOrganizationScope.js +1 -1
  31. package/workflows/principals/principalPolicyOrganizationScope.js.map +1 -1
@@ -1,6 +1,6 @@
1
1
  import type { AccessEvent, AccessManifest, ContainerAccessManifestState, ContainerCreateAccessEventBody, ContainerDirectGrant, ContainerGrantAccessEventBody, ContainerGrantPrincipalHead, ContainerKekKeyring, ContainerKekRecipientTarget, ContainerKeyEpoch, ContainerKeyWrap, ContainerMoveAccessEventBody, ContainerRekeyAccessEventBody, ContainerRevokeAccessEventBody, ContainerUserRecipientKey } from "../../../internal/crypto/index.js";
2
2
  import type { AccessManifestBundleWire, ContainerCreateWithMetadataDocumentRequest, ContainerMutationRequest, ContainerRotationRequest } from "../../../internal/validators/request/index.js";
3
- import type { ContainerCreateWithMetadataDocumentResponse, ContainerKekResponse, ContainerMutationResponse, ContainerRotationResponse, ContainerWriterProjectionResponse, PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
3
+ import type { ContainerCreateWithMetadataDocumentResponse, ContainerKekResponse, ContainerMutationResponse, ContainerRotationResponse, ContainerWriterProjectionResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
4
4
  import type { PrincipalPolicyCurrentEvidence } from "../../principals/principalPolicyEvidence.js";
5
5
  import type { ExecSql } from "../../sqlite/sqlSchema.js";
6
6
  import type { ContainerMutationRequestOptions } from "./mutationRequestOptions.js";
@@ -87,7 +87,6 @@ export interface ContainerCreateApi {
87
87
  createContainerWithMetadataDocumentResult(input: ContainerCreateWithMetadataDocumentRequest, options?: ContainerMutationRequestOptions): Promise<ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>>;
88
88
  evictContainerWriterProjection(containerId: string): void;
89
89
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
90
- getCurrentPrincipalPolicy(principalType: "group" | "organization", principalId: string): Promise<PrincipalPolicyBundleResponse | null>;
91
90
  }
92
91
  export interface ContainerShareApi extends ContainerReciteApi {
93
92
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerCreateAccessEventBody,\n ContainerDirectGrant,\n ContainerGrantAccessEventBody,\n ContainerGrantPrincipalHead,\n ContainerKekKeyring,\n ContainerKekRecipientTarget,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerMoveAccessEventBody,\n ContainerRekeyAccessEventBody,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerCreateWithMetadataDocumentRequest,\n ContainerMutationRequest,\n ContainerRotationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerCreateWithMetadataDocumentResponse,\n ContainerKekResponse,\n ContainerMutationResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport type { ContainerMutationRequestOptions } from \"./mutationRequestOptions\";\nimport type { ContainerReciteApi } from \"./reciteApi\";\n\nexport interface ContainerMutationAuthor {\n organizationId: string;\n signerDeviceId: string;\n signerKeyFingerprint: string;\n signerPrivateKey: Uint8Array;\n signerUserId: string;\n}\n\nexport interface BuildContainerCreatePlanInput {\n author: ContainerMutationAuthor;\n containerId?: string | undefined;\n containerKey: Uint8Array;\n containerKeyEpochId?: string | undefined;\n eventId?: string | undefined;\n /**\n * Optional group grant to bake into the\n * create manifest, wrapping the new container key to that principal in\n * addition to the normal parent-inheritance wrap. Used to birth a child\n * container that is directly readable by a group — e.g. the org public\n * metadata container granted to the reserved Members group. The principal's\n * verified policy must also be supplied via `principalPolicies` so the server\n * can derive the recipient target.\n */\n managedPrincipalGrant?:\n | {\n accessLevel: ContainerDirectGrant[\"accessLevel\"];\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n }\n | undefined;\n metadataDocumentId?: string | undefined;\n systemSlot?: string | null | undefined;\n parentProjection: ContainerWriterProjectionResponse;\n principalPolicies?: readonly PrincipalPolicyCurrentEvidence[] | undefined;\n signedAt?: string | undefined;\n}\n\nexport interface ContainerCreatePlan {\n body: ContainerCreateAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyEpochHash: string;\n keyTargetHash: string;\n manifest: AccessManifest;\n manifestHash: string;\n metadataDocumentId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n recipientTargets: ContainerKekRecipientTarget[];\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerCreatePlan {\n containerKey: Uint8Array;\n plan: ContainerCreatePlan;\n}\n\nexport interface ContainerCreatePlanContext\n extends BuildContainerCreatePlanInput {\n containerKeyPublicKey: string;\n containerId: string;\n containerKeyEpochId: string;\n eventId: string;\n metadataDocumentId: string;\n parent: ParentContainerCreateContext;\n signedAt: string;\n}\n\nexport interface ContainerMutationSubmitFailure {\n readonly code?: string | undefined;\n readonly message: string;\n readonly ok: false;\n readonly report: () => void;\n readonly stalePrincipalHeads?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n readonly status: number | null;\n}\n\nexport type ContainerMutationSubmitResult<T> =\n | {\n readonly data: T;\n readonly ok: true;\n }\n | ContainerMutationSubmitFailure;\n\nexport interface ContainerCreateApi {\n createContainerResult(\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n createContainerWithMetadataDocumentResult(\n input: ContainerCreateWithMetadataDocumentRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<\n ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>\n >;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n}\n\nexport interface ContainerShareApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n shareContainer(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationResponse | null>;\n}\n\n/**\n * A status-bearing rotation outcome. A refusal that names\n * `requiredContainerIds` is answerable: sign those descendant rekeys and\n * resubmit with them carried.\n */\nexport type ContainerRotationResult =\n | { readonly ok: true; readonly data: ContainerRotationResponse }\n | {\n readonly ok: false;\n readonly code?: string | undefined;\n /** Surface a failure that was submitted with `reportErrors: false`. */\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n };\n\nexport interface ContainerMoveApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n moveContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n moveContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerRevokeApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n revokeContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n revokeContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface CreateRemoteContainerResult {\n containerKey: Uint8Array;\n containerId: string;\n metadataDocumentId: string;\n plan: ContainerCreatePlan;\n response: ContainerMutationResponse;\n}\n\nexport interface ContainerSharePlan {\n body: ContainerGrantAccessEventBody;\n /** The unchanged epoch's stored keyring, echoed back by the server. */\n previousKeyring?: Record<string, unknown> | null | undefined;\n containerId: string;\n event: AccessEvent;\n eventHash: string;\n grant: ContainerDirectGrant;\n keyEpoch: ContainerKeyEpoch;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n recipientTarget: ContainerKekRecipientTarget;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerSharePlan {\n containerKey: Uint8Array;\n plan: ContainerSharePlan;\n}\n\nexport interface ContainerRevokePlan {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRevokePlan {\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n}\n\nexport interface ContainerRekeyPlan {\n body: ContainerRekeyAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRekeyPlan {\n containerKey: Uint8Array;\n plan: ContainerRekeyPlan;\n writerProjection: ContainerWriterProjectionResponse;\n}\n\nexport interface ContainerRekeyApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n rekeyContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; see DocumentSyncApi. */\n rekeyContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerMovePlan {\n body: ContainerMoveAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerMovePlan {\n containerKey: Uint8Array;\n plan: ContainerMovePlan;\n}\n\nexport interface BuildMaterializedContainerMovePlanInput {\n author: ContainerMutationAuthor;\n containerKeyEpochId?: string | undefined;\n destinationParentProjection: ContainerWriterProjectionResponse;\n eventId?: string | undefined;\n execSql: ExecSql;\n previousProjection: ContainerWriterProjectionResponse;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n}\n\nexport interface ParentContainerCreateContext {\n manifest: ContainerWriterProjectionResponse[\"path\"][number];\n kek: ContainerKekResponse;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n AccessEvent,\n AccessManifest,\n ContainerAccessManifestState,\n ContainerCreateAccessEventBody,\n ContainerDirectGrant,\n ContainerGrantAccessEventBody,\n ContainerGrantPrincipalHead,\n ContainerKekKeyring,\n ContainerKekRecipientTarget,\n ContainerKeyEpoch,\n ContainerKeyWrap,\n ContainerMoveAccessEventBody,\n ContainerRekeyAccessEventBody,\n ContainerRevokeAccessEventBody,\n ContainerUserRecipientKey,\n} from \"@tearleads/crypto\";\nimport type {\n AccessManifestBundleWire,\n ContainerCreateWithMetadataDocumentRequest,\n ContainerMutationRequest,\n ContainerRotationRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n ContainerCreateWithMetadataDocumentResponse,\n ContainerKekResponse,\n ContainerMutationResponse,\n ContainerRotationResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { PrincipalPolicyCurrentEvidence } from \"../../principals/principalPolicyEvidence\";\nimport type { ExecSql } from \"../../sqlite/sqlSchema\";\nimport type { ContainerMutationRequestOptions } from \"./mutationRequestOptions\";\nimport type { ContainerReciteApi } from \"./reciteApi\";\n\nexport interface ContainerMutationAuthor {\n organizationId: string;\n signerDeviceId: string;\n signerKeyFingerprint: string;\n signerPrivateKey: Uint8Array;\n signerUserId: string;\n}\n\nexport interface BuildContainerCreatePlanInput {\n author: ContainerMutationAuthor;\n containerId?: string | undefined;\n containerKey: Uint8Array;\n containerKeyEpochId?: string | undefined;\n eventId?: string | undefined;\n /**\n * Optional group grant to bake into the\n * create manifest, wrapping the new container key to that principal in\n * addition to the normal parent-inheritance wrap. Used to birth a child\n * container that is directly readable by a group — e.g. the org public\n * metadata container granted to the reserved Members group. The principal's\n * verified policy must also be supplied via `principalPolicies` so the server\n * can derive the recipient target.\n */\n managedPrincipalGrant?:\n | {\n accessLevel: ContainerDirectGrant[\"accessLevel\"];\n principalEncapsulationPublicKey: string;\n principalHead: ContainerGrantPrincipalHead;\n }\n | undefined;\n metadataDocumentId?: string | undefined;\n systemSlot?: string | null | undefined;\n parentProjection: ContainerWriterProjectionResponse;\n principalPolicies?: readonly PrincipalPolicyCurrentEvidence[] | undefined;\n signedAt?: string | undefined;\n}\n\nexport interface ContainerCreatePlan {\n body: ContainerCreateAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyEpochHash: string;\n keyTargetHash: string;\n manifest: AccessManifest;\n manifestHash: string;\n metadataDocumentId: string;\n parentContainerId: string | null;\n parentManifestHash: string | null;\n recipientTargets: ContainerKekRecipientTarget[];\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerCreatePlan {\n containerKey: Uint8Array;\n plan: ContainerCreatePlan;\n}\n\nexport interface ContainerCreatePlanContext\n extends BuildContainerCreatePlanInput {\n containerKeyPublicKey: string;\n containerId: string;\n containerKeyEpochId: string;\n eventId: string;\n metadataDocumentId: string;\n parent: ParentContainerCreateContext;\n signedAt: string;\n}\n\nexport interface ContainerMutationSubmitFailure {\n readonly code?: string | undefined;\n readonly message: string;\n readonly ok: false;\n readonly report: () => void;\n readonly stalePrincipalHeads?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n readonly status: number | null;\n}\n\nexport type ContainerMutationSubmitResult<T> =\n | {\n readonly data: T;\n readonly ok: true;\n }\n | ContainerMutationSubmitFailure;\n\nexport interface ContainerCreateApi {\n createContainerResult(\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationSubmitResult<ContainerMutationResponse>>;\n createContainerWithMetadataDocumentResult(\n input: ContainerCreateWithMetadataDocumentRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<\n ContainerMutationSubmitResult<ContainerCreateWithMetadataDocumentResponse>\n >;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n}\n\nexport interface ContainerShareApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n shareContainer(\n containerId: string,\n input: ContainerMutationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerMutationResponse | null>;\n}\n\n/**\n * A status-bearing rotation outcome. A refusal that names\n * `requiredContainerIds` is answerable: sign those descendant rekeys and\n * resubmit with them carried.\n */\nexport type ContainerRotationResult =\n | { readonly ok: true; readonly data: ContainerRotationResponse }\n | {\n readonly ok: false;\n readonly code?: string | undefined;\n /** Surface a failure that was submitted with `reportErrors: false`. */\n readonly report?: (() => void) | undefined;\n readonly requiredContainerIds?: readonly string[] | undefined;\n readonly status: number | null;\n };\n\nexport interface ContainerMoveApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n moveContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n moveContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerRevokeApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n revokeContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; without it nothing can be carried. */\n revokeContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface CreateRemoteContainerResult {\n containerKey: Uint8Array;\n containerId: string;\n metadataDocumentId: string;\n plan: ContainerCreatePlan;\n response: ContainerMutationResponse;\n}\n\nexport interface ContainerSharePlan {\n body: ContainerGrantAccessEventBody;\n /** The unchanged epoch's stored keyring, echoed back by the server. */\n previousKeyring?: Record<string, unknown> | null | undefined;\n containerId: string;\n event: AccessEvent;\n eventHash: string;\n grant: ContainerDirectGrant;\n keyEpoch: ContainerKeyEpoch;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n recipientTarget: ContainerKekRecipientTarget;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerSharePlan {\n containerKey: Uint8Array;\n plan: ContainerSharePlan;\n}\n\nexport interface ContainerRevokePlan {\n body: ContainerRevokeAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRevokePlan {\n containerKey: Uint8Array;\n plan: ContainerRevokePlan;\n}\n\nexport interface ContainerRekeyPlan {\n body: ContainerRekeyAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n userRecipientKeys: ContainerUserRecipientKey[];\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerRekeyPlan {\n containerKey: Uint8Array;\n plan: ContainerRekeyPlan;\n writerProjection: ContainerWriterProjectionResponse;\n}\n\nexport interface ContainerRekeyApi extends ContainerReciteApi {\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n rekeyContainer(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResponse | null>;\n /** Optional status-bearing variant; see DocumentSyncApi. */\n rekeyContainerResult?(\n containerId: string,\n input: ContainerRotationRequest,\n options?: ContainerMutationRequestOptions,\n ): Promise<ContainerRotationResult>;\n}\n\nexport interface ContainerMovePlan {\n body: ContainerMoveAccessEventBody;\n containerId: string;\n containerKeyEpochId: string;\n event: AccessEvent;\n eventHash: string;\n keyEpoch: ContainerKeyEpoch;\n keyring: ContainerKekKeyring;\n manifest: AccessManifest;\n manifestHash: string;\n previousManifest: AccessManifestBundleWire;\n request: ContainerMutationRequest;\n state: ContainerAccessManifestState;\n wraps: ContainerKeyWrap[];\n}\n\nexport interface MaterializedContainerMovePlan {\n containerKey: Uint8Array;\n plan: ContainerMovePlan;\n}\n\nexport interface BuildMaterializedContainerMovePlanInput {\n author: ContainerMutationAuthor;\n containerKeyEpochId?: string | undefined;\n destinationParentProjection: ContainerWriterProjectionResponse;\n eventId?: string | undefined;\n execSql: ExecSql;\n previousProjection: ContainerWriterProjectionResponse;\n signedAt?: string | undefined;\n targetSecretKey: Uint8Array;\n}\n\nexport interface ParentContainerCreateContext {\n manifest: ContainerWriterProjectionResponse[\"path\"][number];\n kek: ContainerKekResponse;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tearleads/client-sdk",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "description": "React-free Tearleads client runtime: local SQLite persistence, keys, sync, encrypted blobs, and domain workflows.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient } from \"@tearleads/api-client\";\nimport type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n requiredAncestorId?: string | undefined;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type {\n ContainerDeleteResponse,\n ContainerWriterProjectionResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createInitializedContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { ProjectionUserKeyResolver } from \"../../../data/keyingProjectionVerification\";\nimport type { DocumentRecord } from \"../../../data/sqlite/documentPersistence\";\nimport type {\n createRemoteContainer,\n moveRemoteContainer,\n shareRemoteContainer,\n shareRemoteContainerWithGroup,\n} from \"../../containers\";\nimport type { createRemoteDocument } from \"../../documents\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport type { PrincipalPolicyHistoryReader } from \"../../principals/principalPolicyHistoryReader\";\nimport type {\n ContainerContentsPersistence,\n ContainerCreateIntentRecord,\n ContainerMoveIntentRecord,\n} from \"../containerPersistence\";\nimport type {\n ContainerState,\n RemoteContainer as HydratedRemoteContainer,\n RemoteContainerHydrationHost,\n} from \"../remoteHydration\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ContainerWorkflowApi = Parameters<\n typeof createRemoteContainer\n>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof shareRemoteContainerWithGroup>[0][\"apiClient\"] &\n Parameters<typeof moveRemoteContainer>[0][\"apiClient\"] &\n Parameters<typeof createRemoteDocument>[0][\"apiClient\"] &\n Partial<PrincipalPolicyHistoryReader> &\n CurrentPrincipalPolicyReader & {\n deleteContainerResult(\n containerId: string,\n options?: {\n expectedPaymentRequiredOrganizationId?: string;\n reportErrors?: boolean;\n requiredAncestorId?: string | undefined;\n },\n ): Promise<\n | { data: ContainerDeleteResponse; ok: true }\n | {\n ok: false;\n report: () => void;\n status: number | null;\n }\n >;\n primeContainerWriterProjection(\n containerId: string,\n projection: ContainerWriterProjectionResponse,\n ): void;\n };\n\nexport interface ContainerWorkflowRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n readonly apiClient: ContainerWorkflowApi;\n}\n\nexport interface CreatedRemoteContainerState {\n accessManifestHash: string;\n /** The slot this client signed into the create; never the server's echo. */\n systemSlot: ContainerSystemSlot | null;\n containerId: string;\n createdAt: string;\n metadataDocumentId: string;\n organizationId: string;\n /** A created container is a child; the acknowledgement echoed this parent. */\n parentId: string;\n persistedMetadataState: Pick<\n DocumentRecord,\n | \"documentId\"\n | \"contentKeyBundle\"\n | \"documentKekTargets\"\n | \"documentManifestBundle\"\n >;\n updatedAt: string;\n}\n\nexport interface SharedRemoteContainerState {\n accessEpoch: number;\n accessManifestHash: string;\n createdAt: string;\n metadataDocumentId: string;\n referencedPrincipalHeads: ReferencedPrincipalStateResponse[];\n updatedAt: string;\n writerProjection: ContainerWriterProjectionResponse | null;\n}\n\nexport interface SharedContainerState {\n container: ContainerState[\"container\"];\n record: ContainerState[\"record\"];\n}\n\nexport type SharedContainerStateResult =\n | { status: \"missing\" }\n | { status: \"confirmed\" }\n | (SharedContainerState & {\n status: \"identity-superseded\" | \"persisted\";\n });\n\nexport type RemoteContainer = HydratedRemoteContainer;\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createInitializedContainerMetadataDocument>\n>[\"doc\"];\n\nexport interface CreatedChildContainerState {\n containerState: ContainerState;\n initialUpdate: Uint8Array;\n shouldRequestSync: boolean;\n}\n\nexport interface ContainerIntentSyncState {\n containersById: ReadonlyMap<string, ContainerState>;\n persistence: ContainerContentsPersistence;\n resolveProjectionUserKey: ProjectionUserKeyResolver;\n runtime: ContainerWorkflowRuntime;\n /**\n * Verifies that a listed container is this device's pending create before it\n * is adopted and returns where it sits; `assertContainerCreateAdoptable`\n * unless a test replaces it.\n */\n verifyCreateAdoption?: (input: {\n readonly containerId: string;\n readonly expectedOrganizationId: string;\n }) => Promise<AdoptedContainerPlacement>;\n}\n\n/** Where a verified listed create committed, and where its container is now. */\nexport interface AdoptedContainerPlacement {\n /** The parent its signed epoch-1 `container.create` named. */\n readonly createdParentId: string;\n /** The verified head's parent; another writer may have moved it since. */\n readonly currentParentId: string;\n}\n\nexport type ContainerCreateIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerCreateIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\"\n>;\n\nexport interface ContainerCreateIntentSyncInput {\n host: ContainerCreateIntentSyncHost;\n /** A parent whose own create has not settled takes no child create. */\n isCreatePending: (containerId: string) => boolean;\n isCurrent: () => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerCreateIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerCreateIntentSyncState;\n}\n\nexport type ContainerMoveIntentSyncState = ContainerIntentSyncState;\n\nexport type ContainerMoveIntentSyncHost = Pick<\n RemoteContainerHydrationHost,\n \"persistContainerState\" | \"updateSnapshot\"\n>;\n\nexport interface ContainerMoveIntentSyncInput {\n host: ContainerMoveIntentSyncHost;\n isCurrent: () => boolean;\n /** A folder whose create has not settled takes no structural writes. */\n isCreatePending: (containerId: string) => boolean;\n isRemoteSyncBlocked: (organizationId: string) => boolean;\n intent: ContainerMoveIntentRecord;\n requestRemoteReconciliation: (parentContainerId: string | null) => void;\n state: ContainerMoveIntentSyncState;\n}\n"]}
@@ -1,8 +1,10 @@
1
- import type { ApiClient, RequestResult } from "../../../internal/api-client/index.js";
1
+ import type { RequestResult } from "../../../internal/api-client/index.js";
2
2
  import type { ContainerReplacementAuthorizationsResponse } from "../../../internal/validators/operation/index.js";
3
3
  import type { ListContainerParentLanesRequest } from "../../../internal/validators/request/index.js";
4
- import type { ContainerSummary, ContainerWriterProjectionResponse, DocumentWriterProjectionResponse, ListContainerParentLanesResponse, ListContainersResponse, PrincipalPolicyBundleResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
4
+ import type { ContainerSummary, ContainerWriterProjectionResponse, DocumentWriterProjectionResponse, ListContainerParentLanesResponse, ListContainersResponse, ReferencedPrincipalStateResponse } from "../../../internal/validators/response/index.js";
5
5
  import type { createContainerMetadataDocument } from "../../../data/containers/containerMetadataDocument.js";
6
+ import type { CurrentPrincipalPolicyReader } from "../../principals/currentPrincipalPolicyReader.js";
7
+ import type { PrincipalPolicyHistoryReader } from "../../principals/principalPolicyHistoryReader.js";
6
8
  import type { ContainerContentsPersistence, ContainerDocumentRecord, ContainerHydrationTombstone, ContainerRecord, createContainerParentSyncLane } from "../containerPersistence.js";
7
9
  import type { ContainerMetadataPatch } from "../metadata.js";
8
10
  import type { ContainerContentsWorkflowRuntime } from "../runtime.js";
@@ -30,11 +32,10 @@ export interface ContainerState {
30
32
  pullContinuation?: ContainerDocumentRecord["pullContinuation"] | undefined;
31
33
  record: ContainerDocumentRecord;
32
34
  }
33
- interface RemoteContainerHydrationApi extends Partial<Pick<ApiClient, "getPrincipalPolicyPages" | "getProjectionPolicyHistoryPages">> {
35
+ interface RemoteContainerHydrationApi extends Partial<PrincipalPolicyHistoryReader>, CurrentPrincipalPolicyReader {
34
36
  getContainerReplacementAuthorizationsResult(containerId: string, replacesOrganizationId: string): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;
35
37
  evictContainerWriterProjection(containerId: string): void;
36
38
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
37
- getCurrentPrincipalPolicy(principalType: "group" | "organization", principalId: string): Promise<PrincipalPolicyBundleResponse | null>;
38
39
  listContainerParentLanes(input: ListContainerParentLanesRequest): Promise<ListContainerParentLanesResponse | null>;
39
40
  }
40
41
  interface RemoteContainerHydrationRuntime extends Pick<ContainerContentsWorkflowRuntime, "auth" | "adoptRootContainer" | "crypto" | "infra" | "resolveTrustedUserIdentity" | "state" | "util"> {
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiClient, RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi\n extends Partial<\n Pick<\n ApiClient,\n \"getPrincipalPolicyPages\" | \"getProjectionPolicyHistoryPages\"\n >\n > {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n getCurrentPrincipalPolicy(\n principalType: \"group\" | \"organization\",\n principalId: string,\n ): Promise<PrincipalPolicyBundleResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport type { ContainerReplacementAuthorizationsResponse } from \"@tearleads/validators/operation\";\nimport type { ListContainerParentLanesRequest } from \"@tearleads/validators/request\";\nimport type {\n ContainerSummary,\n ContainerWriterProjectionResponse,\n DocumentWriterProjectionResponse,\n ListContainerParentLanesResponse,\n ListContainersResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport type { createContainerMetadataDocument } from \"../../../data/containers/containerMetadataDocument\";\nimport type { CurrentPrincipalPolicyReader } from \"../../principals/currentPrincipalPolicyReader\";\nimport type { PrincipalPolicyHistoryReader } from \"../../principals/principalPolicyHistoryReader\";\nimport type {\n ContainerContentsPersistence,\n ContainerDocumentRecord,\n ContainerHydrationTombstone,\n ContainerRecord,\n createContainerParentSyncLane,\n} from \"../containerPersistence\";\nimport type { ContainerMetadataPatch } from \"../metadata\";\nimport type { ContainerContentsWorkflowRuntime } from \"../runtime\";\n\nexport type ListedRemoteContainerPageItem =\n ListContainersResponse[\"items\"][number];\nexport type ContainerChildIndex = Map<string, Set<string>>;\nexport type QueueContainerParentLane = (parentId: string | null) => void;\nexport type RemoteContainerIngestQueue = Map<string, RemoteContainer>;\nexport type SaveContainerOptions = Parameters<\n ContainerContentsPersistence[\"saveContainer\"]\n>[3];\n\nexport type ContainerMetadataDocumentState = Awaited<\n ReturnType<typeof createContainerMetadataDocument>\n>;\nexport type RemoteContainer = Pick<\n ContainerSummary,\n | \"createdAt\"\n | \"effectiveAccessLevel\"\n | \"systemSlot\"\n | \"id\"\n | \"metadataAccessEpoch\"\n | \"metadataAccessStateHash\"\n | \"metadataDocumentId\"\n | \"metadataReferencedPrincipals\"\n | \"organizationId\"\n | \"parentId\"\n | \"updatedAt\"\n>;\n\nexport interface ContainerState {\n containerWriterProjection?:\n | ContainerWriterProjectionResponse\n | null\n | undefined;\n /**\n * Bumped whenever a realtime hint drops this container's cached writer\n * projections (container and metadata document alike); an operation that\n * fetched or carried a projection installs it only if this is unchanged\n * since it started, so a pre-hint answer never lands after the hint.\n */\n writerProjectionGeneration?: number | undefined;\n metadataReferencedPrincipals?:\n | readonly ReferencedPrincipalStateResponse[]\n | undefined;\n container: ContainerRecord;\n doc: ContainerMetadataDocumentState;\n metadataWriterProjection?:\n | DocumentWriterProjectionResponse\n | null\n | undefined;\n /** Durable pull progress mirrored from the metadata document record. */\n pullContinuation?: ContainerDocumentRecord[\"pullContinuation\"] | undefined;\n record: ContainerDocumentRecord;\n}\n\ninterface RemoteContainerHydrationApi\n extends Partial<PrincipalPolicyHistoryReader>,\n CurrentPrincipalPolicyReader {\n getContainerReplacementAuthorizationsResult(\n containerId: string,\n replacesOrganizationId: string,\n ): Promise<RequestResult<ContainerReplacementAuthorizationsResponse>>;\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n listContainerParentLanes(\n input: ListContainerParentLanesRequest,\n ): Promise<ListContainerParentLanesResponse | null>;\n}\n\ninterface RemoteContainerHydrationRuntime\n extends Pick<\n ContainerContentsWorkflowRuntime,\n | \"auth\"\n | \"adoptRootContainer\"\n | \"crypto\"\n | \"infra\"\n | \"resolveTrustedUserIdentity\"\n | \"state\"\n | \"util\"\n > {\n apiClient: RemoteContainerHydrationApi;\n}\n\nexport interface RemoteContainerHydrationState {\n containersById: Map<string, ContainerState>;\n lifecycleGeneration?: number | undefined;\n persistence: ContainerContentsPersistence;\n rootLaneHydrated?: boolean | undefined;\n runtime: RemoteContainerHydrationRuntime;\n structuralGeneration?: number | undefined;\n}\n\nexport type PersistContainerStateResult =\n | { status: \"identity-superseded\"; record: ContainerDocumentRecord }\n | { status: \"missing\" }\n | {\n status: \"persisted\";\n record: ContainerDocumentRecord;\n createIntentSettled?: true | undefined;\n moveIntentSettled?: true | undefined;\n }\n | { status: \"stale-generation\" };\n\nexport interface RemoteContainerHydrationHost {\n persistContainerState: (\n containerState: ContainerState,\n patch?: Partial<ContainerMetadataPatch>,\n updateView?: boolean,\n saveOptions?: SaveContainerOptions,\n mutationOptions?: {\n expectedStateWhenMissing?: ContainerState | undefined;\n isCurrent?: (() => boolean) | undefined;\n createIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"createIntentSettlement\"];\n moveIntentSettlement?: Parameters<\n ContainerContentsPersistence[\"commitMetadataMutation\"]\n >[1][\"moveIntentSettlement\"];\n preserveDurableStructureWhenPending?: boolean | undefined;\n },\n ) => Promise<PersistContainerStateResult>;\n requestDocumentPriming?: (() => void) | undefined;\n updateSnapshot: () => void;\n}\n\nexport interface ContainerParentHydrationLane {\n parentId: string | null;\n watermark?: ListContainersResponse[\"nextWatermark\"];\n}\n\nexport interface ExpectedContainerState {\n container: ContainerRecord;\n fingerprint: string;\n}\n\nexport interface FetchedContainerParentLanePage {\n expectedContainerStates: ReadonlyMap<string, ExpectedContainerState>;\n expectedHydrationTombstones: ReadonlyMap<string, ContainerHydrationTombstone>;\n lane: ContainerParentHydrationLane;\n response: ListContainersResponse;\n syncLane: ReturnType<typeof createContainerParentSyncLane>;\n}\n"]}
@@ -1,15 +1,15 @@
1
1
  import type { ApiClient } from "../../../internal/api-client/index.js";
2
- import { KeyingVerificationError, type ManagedPrincipalKind, type ReferencedPrincipalHead, type VerifiedPrincipalPolicy } from "../../../internal/crypto/index.js";
2
+ import { KeyingVerificationError, type ReferencedPrincipalHead, type VerifiedPrincipalPolicy } from "../../../internal/crypto/index.js";
3
3
  import type { CommitOrganizationGroupPolicyRequest } from "../../../internal/validators/request/index.js";
4
4
  import type { CommitOrganizationGroupPolicyResponse, PrincipalPolicyBundleResponse } from "../../../internal/validators/response/index.js";
5
5
  import type { ContainerShareApi } from "../../../data/containers/shared/types.js";
6
6
  import type { ExecSql } from "../../../data/sqlite/sqlSchema.js";
7
7
  import type { TrustedUserIdentityResolver } from "../../../data/trustedUserIdentity/index.js";
8
8
  import { type GroupPolicyNameReader } from "../../organizations/principalPolicyRequest.js";
9
- export interface ContainerManagedPrincipalShareApi extends ContainerShareApi, Pick<ApiClient, "evictContainerWriterProjection" | "getContainerWriterProjectionResult"> {
9
+ import type { CurrentPrincipalPolicyReader } from "../../principals/currentPrincipalPolicyReader.js";
10
+ export interface ContainerManagedPrincipalShareApi extends ContainerShareApi, CurrentPrincipalPolicyReader, Pick<ApiClient, "evictContainerWriterProjection" | "getContainerWriterProjectionResult"> {
10
11
  readonly recoverPendingPrincipalMutation?: ((organizationId: string) => Promise<void>) | undefined;
11
12
  commitOrganizationGroupPolicy: (organizationId: string, groupId: string, input: CommitOrganizationGroupPolicyRequest) => Promise<CommitOrganizationGroupPolicyResponse | null>;
12
- getCurrentPrincipalPolicy: (principalType: ManagedPrincipalKind, principalId: string) => Promise<PrincipalPolicyBundleResponse | null>;
13
13
  }
14
14
  export interface VerifiedSharePrincipalPolicy {
15
15
  readonly bundle: PrincipalPolicyBundleResponse;
@@ -130,8 +130,8 @@ async function assertShareGroupName(input) {
130
130
  }
131
131
  export async function loadVerifiedGroupSharePrincipalPolicy(input) {
132
132
  const organizationAdminPolicy = await loadOrganizationExternalAdminPolicy({
133
+ currentPolicyReader: input.apiClient,
133
134
  execSql: input.execSql,
134
- getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
135
135
  organizationId: input.organizationId,
136
136
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
137
137
  stillCurrent: input.stillCurrent,
@@ -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 ManagedPrincipalKind,\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/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/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 { assertGroupMetadataBinding } from \"../../organizations/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../../organizations/principalPolicyRequest\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"../../principals/externalAdminPolicy\";\nimport {\n collectPrincipalPolicySignerPublicKeys,\n type PrincipalPolicySignerPublicKeyLoadErrorCode,\n} from \"../../principals/policyVerification\";\n\nexport interface ContainerManagedPrincipalShareApi\n extends ContainerShareApi,\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 getCurrentPrincipalPolicy: (\n principalType: ManagedPrincipalKind,\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | 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 execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\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/keyingCheckpointAdvancePersistence\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../../data/persistence/keyingCheckpointPersistence\";\nimport { savePrincipalPolicyBundle } from \"../../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../../data/persistence/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 { assertGroupMetadataBinding } from \"../../organizations/groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"../../organizations/principalPolicyRequest\";\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\";\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"]}
@@ -11,8 +11,8 @@ export async function deleteOrganizationGroup(input) {
11
11
  await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
12
12
  assertProjectionVerificationCurrent(stillCurrent);
13
13
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
14
+ currentPolicyReader: input.apiClient,
14
15
  execSql: input.execSql,
15
- getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
16
16
  organizationId: input.organizationId,
17
17
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
18
18
  stillCurrent,
@@ -1 +1 @@
1
- {"version":3,"file":"deleteOrganizationGroup.js","sourceRoot":"","sources":["deleteOrganizationGroup.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { DeleteOrganizationGroupRequest } from \"@tearleads/validators/request\";\nimport type { DeleteOrganizationGroupResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { persistLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport {\n acknowledgeGroupPolicyState,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { PrincipalPolicyReadApi } from \"./groupPolicyMutationContext\";\nimport { groupPolicyMutationHead } from \"./groupPolicyMutationHead\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n removeOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\nexport async function deleteOrganizationGroup(input: {\n readonly apiClient: PrincipalPolicyReadApi & {\n deleteOrganizationGroup: (\n organizationId: string,\n groupId: string,\n request: DeleteOrganizationGroupRequest,\n ) => Promise<DeleteOrganizationGroupResponse | null>;\n };\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 readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<DeleteOrganizationGroupResponse> {\n const stillCurrent = input.stillCurrent;\n assertProjectionVerificationCurrent(stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n const externalAdminPolicy = 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 stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n if (!externalAdminPolicy?.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const adminProjection = externalAdminPolicy.adminBundle.currentProjection;\n const adminUsers = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n });\n const organizationRequest =\n await buildOrganizationGroupDirectoryPolicyRequest({\n adminProjection,\n adminUsers,\n currentPolicy: externalAdminPolicy.bundle,\n descriptor: externalAdminPolicy.descriptor,\n groupHeads: removeOrganizationGroupHead({\n descriptor: externalAdminPolicy.descriptor,\n groupId: input.groupId,\n }),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: organizationRequest },\n );\n assertProjectionVerificationCurrent(stillCurrent);\n if (!stored) {\n throw new Error(\"Group could not be deleted\");\n }\n if (\n stored.organizationId !== input.organizationId ||\n stored.groupId !== input.groupId\n ) {\n throw new Error(\"Group deletion response target mismatch\");\n }\n const organizationHead = await groupPolicyMutationHead(organizationRequest);\n const organizationPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy,\n });\n await persistLocallyAcknowledgedPrincipalPolicyBundles({\n entries: [{ bundle: organizationBundle, policy: organizationPolicy }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return stored;\n}\n"]}
1
+ {"version":3,"file":"deleteOrganizationGroup.js","sourceRoot":"","sources":["deleteOrganizationGroup.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { DeleteOrganizationGroupRequest } from \"@tearleads/validators/request\";\nimport type { DeleteOrganizationGroupResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { persistLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport {\n acknowledgeGroupPolicyState,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { PrincipalPolicyReadApi } from \"./groupPolicyMutationContext\";\nimport { groupPolicyMutationHead } from \"./groupPolicyMutationHead\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n removeOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\nexport async function deleteOrganizationGroup(input: {\n readonly apiClient: PrincipalPolicyReadApi & {\n deleteOrganizationGroup: (\n organizationId: string,\n groupId: string,\n request: DeleteOrganizationGroupRequest,\n ) => Promise<DeleteOrganizationGroupResponse | null>;\n };\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 readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<DeleteOrganizationGroupResponse> {\n const stillCurrent = input.stillCurrent;\n assertProjectionVerificationCurrent(stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n if (!externalAdminPolicy?.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const adminProjection = externalAdminPolicy.adminBundle.currentProjection;\n const adminUsers = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n });\n const organizationRequest =\n await buildOrganizationGroupDirectoryPolicyRequest({\n adminProjection,\n adminUsers,\n currentPolicy: externalAdminPolicy.bundle,\n descriptor: externalAdminPolicy.descriptor,\n groupHeads: removeOrganizationGroupHead({\n descriptor: externalAdminPolicy.descriptor,\n groupId: input.groupId,\n }),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: organizationRequest },\n );\n assertProjectionVerificationCurrent(stillCurrent);\n if (!stored) {\n throw new Error(\"Group could not be deleted\");\n }\n if (\n stored.organizationId !== input.organizationId ||\n stored.groupId !== input.groupId\n ) {\n throw new Error(\"Group deletion response target mismatch\");\n }\n const organizationHead = await groupPolicyMutationHead(organizationRequest);\n const organizationPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy,\n });\n await persistLocallyAcknowledgedPrincipalPolicyBundles({\n entries: [{ bundle: organizationBundle, policy: organizationPolicy }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return stored;\n}\n"]}
@@ -8,8 +8,8 @@ export async function createOrganizationGroup(input) {
8
8
  await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
9
9
  assertProjectionVerificationCurrent(input.stillCurrent);
10
10
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
11
+ currentPolicyReader: input.apiClient,
11
12
  execSql: input.execSql,
12
- getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
13
13
  organizationId: input.organizationId,
14
14
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
15
15
  stillCurrent: input.stillCurrent,
@@ -1 +1 @@
1
- {"version":3,"file":"groupCreation.js","sourceRoot":"","sources":["groupCreation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EncapsulationKeyPair, SigningKeyPair } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport type { createGroupMetadataAccess } from \"./groupMetadataAccess\";\nimport {\n cacheGroupPolicy,\n type OrganizationPrincipalPolicyApi,\n} from \"./groupPolicyMutationContext\";\nimport { commitCreatedGroupToDirectory } from \"./organizationGroupDirectory\";\nimport { buildInitialGroupPolicyRequest } from \"./principalPolicyRequest\";\nimport type { OrganizationGroupSummary } from \"./readModel\";\n\nexport async function createOrganizationGroup(input: {\n readonly metadataAccess: ReturnType<typeof createGroupMetadataAccess>;\n readonly apiClient: OrganizationPrincipalPolicyApi;\n readonly creatorEncapsulationKeyPair: EncapsulationKeyPair;\n readonly execSql: ExecSql;\n readonly name: string;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<OrganizationGroupSummary> {\n assertProjectionVerificationCurrent(input.stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(input.stillCurrent);\n const externalAdminPolicy = 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 stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (!externalAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n if (!externalAdminPolicy.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const request = await buildInitialGroupPolicyRequest({\n creatorEncapsulationKeyPair: input.creatorEncapsulationKeyPair,\n externalAuthority: externalAdminPolicy.externalAuthority.currentHead,\n groupId: crypto.randomUUID(),\n includeSignerAsAdmin: false,\n name: input.name,\n metadataKey: await input.metadataAccess.loadEncryptionKey(),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n const { group, head: expectedHead } = await commitCreatedGroupToDirectory({\n ...input,\n externalAdminPolicy,\n readEncryptedName: input.metadataAccess.readName,\n request,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n await cacheGroupPolicy({\n acknowledgedMemberEnvelopes: {\n envelopes: request.initialGroupPolicy.memberEnvelopes,\n epoch: request.initialGroupPolicy.state.keyEpoch,\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n },\n apiClient: input.apiClient,\n execSql: input.execSql,\n expectedCurrentHead: expectedHead,\n externalAuthority: externalAdminPolicy.externalAuthority,\n groupId: group.groupId,\n localPolicyCheckpoint: {\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n version: expectedHead.version,\n },\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n return { ...group, name: input.name.trim(), nameUnreadable: false };\n}\n"]}
1
+ {"version":3,"file":"groupCreation.js","sourceRoot":"","sources":["groupCreation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EncapsulationKeyPair, SigningKeyPair } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport type { createGroupMetadataAccess } from \"./groupMetadataAccess\";\nimport {\n cacheGroupPolicy,\n type OrganizationPrincipalPolicyApi,\n} from \"./groupPolicyMutationContext\";\nimport { commitCreatedGroupToDirectory } from \"./organizationGroupDirectory\";\nimport { buildInitialGroupPolicyRequest } from \"./principalPolicyRequest\";\nimport type { OrganizationGroupSummary } from \"./readModel\";\n\nexport async function createOrganizationGroup(input: {\n readonly metadataAccess: ReturnType<typeof createGroupMetadataAccess>;\n readonly apiClient: OrganizationPrincipalPolicyApi;\n readonly creatorEncapsulationKeyPair: EncapsulationKeyPair;\n readonly execSql: ExecSql;\n readonly name: string;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<OrganizationGroupSummary> {\n assertProjectionVerificationCurrent(input.stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(input.stillCurrent);\n const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.apiClient,\n execSql: input.execSql,\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (!externalAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n if (!externalAdminPolicy.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const request = await buildInitialGroupPolicyRequest({\n creatorEncapsulationKeyPair: input.creatorEncapsulationKeyPair,\n externalAuthority: externalAdminPolicy.externalAuthority.currentHead,\n groupId: crypto.randomUUID(),\n includeSignerAsAdmin: false,\n name: input.name,\n metadataKey: await input.metadataAccess.loadEncryptionKey(),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n const { group, head: expectedHead } = await commitCreatedGroupToDirectory({\n ...input,\n externalAdminPolicy,\n readEncryptedName: input.metadataAccess.readName,\n request,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n await cacheGroupPolicy({\n acknowledgedMemberEnvelopes: {\n envelopes: request.initialGroupPolicy.memberEnvelopes,\n epoch: request.initialGroupPolicy.state.keyEpoch,\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n },\n apiClient: input.apiClient,\n execSql: input.execSql,\n expectedCurrentHead: expectedHead,\n externalAuthority: externalAdminPolicy.externalAuthority,\n groupId: group.groupId,\n localPolicyCheckpoint: {\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n version: expectedHead.version,\n },\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n return { ...group, name: input.name.trim(), nameUnreadable: false };\n}\n"]}
@@ -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
- // This callback runs after the organization signature and scope checks.
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":"AACA,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;AACvG,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,yBAAyB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;YAC5C,IAAI,IAAI,KAAK,cAAc;gBACzB,OAAO,EAAE,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;YAC3D,wEAAwE;YACxE,MAAM,UAAU,GAAG,oCAAoC,CACrD,YAAY,CAAC,cAAc,CAAC,UAAU,CACvC,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,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClC,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\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 { 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 getCurrentPrincipalPolicy: async (type, id) => {\n if (type === \"organization\")\n return id === input.organizationId ? organization : null;\n // This callback runs after the organization signature and scope checks.\n const descriptor = parseOrganizationAuthorityDescriptor(\n organization.currentPayload.ciphertext,\n );\n const head = descriptor.groupHeads.find(\n (head) => head.principalId === id,\n );\n return head ? load(head) : null;\n },\n });\n}\n"]}
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, PrincipalPolicyBundleResponse } from "../../internal/validators/response/index.js";
4
- export interface PrincipalPolicyReadApi {
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 PrincipalPolicyBundleResponse,\n} from \"@tearleads/validators/response\";\n\nexport interface PrincipalPolicyReadApi {\n /** Resolve authored work before reading the state for a new mutation. */\n readonly recoverPendingPrincipalMutation?:\n | ((organizationId: string) => Promise<void>)\n | undefined;\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\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"]}
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"]}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=currentPrincipalPolicyReader.js.map
@@ -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
- getCurrentPrincipalPolicy: input.getCurrentPrincipalPolicy,
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, execSql, getCurrentPrincipalPolicy, log, organizationId, reportSecurityIncident, resolveTrustedUserIdentity, stillCurrent, }: CachePrincipalPolicyBundlesOptions): Promise<void>;
20
+ export declare function cachePrincipalPolicyBundles({ bundles, currentPolicyReader, execSql, log, organizationId, reportSecurityIncident, resolveTrustedUserIdentity, stillCurrent, }: CachePrincipalPolicyBundlesOptions): Promise<void>;
@@ -29,7 +29,7 @@ function dedupePrincipalPolicyBundles(bundles) {
29
29
  }
30
30
  return Array.from(bundlesByPrincipal.values());
31
31
  }
32
- async function cacheReferencedPrincipalPolicy(execSql, getCurrentPrincipalPolicy, reference, resolveTrustedUserIdentity, log, loadExternalAdminPolicy, organizationId, stillCurrent, loadDirectory, fresh) {
32
+ async function cacheReferencedPrincipalPolicy(execSql, reader, reference, resolveTrustedUserIdentity, log, loadExternalAdminPolicy, organizationId, stillCurrent, loadDirectory, fresh) {
33
33
  const localCheckpoint = await loadPrincipalPolicyCheckpoint(execSql, reference.principalType, reference.principalId);
34
34
  // Only null is a recoverable local miss. Integrity failures remain typed and
35
35
  // must escape rather than being hidden by a canonical network retry.
@@ -37,7 +37,7 @@ async function cacheReferencedPrincipalPolicy(execSql, getCurrentPrincipalPolicy
37
37
  ? null
38
38
  : await loadPrincipalPolicyBundleForReference(execSql, reference, localCheckpoint);
39
39
  let bundle = cachedBundle ??
40
- (await getCurrentPrincipalPolicy(reference.principalType, reference.principalId));
40
+ (await reader.getCurrentPrincipalPolicy(reference.principalType, reference.principalId));
41
41
  if (!bundle) {
42
42
  log?.(`Principal policy cache: failed to fetch ${getReferencedPrincipalKey(reference)}`);
43
43
  return [];
@@ -64,7 +64,7 @@ async function cacheReferencedPrincipalPolicy(execSql, getCurrentPrincipalPolicy
64
64
  loadDirectory,
65
65
  organizationId,
66
66
  reloadBundle: cachedBundle
67
- ? () => getCurrentPrincipalPolicy("group", reference.principalId)
67
+ ? () => reader.getCurrentPrincipalPolicy("group", reference.principalId)
68
68
  : null,
69
69
  });
70
70
  if (!scoped) {
@@ -151,8 +151,8 @@ async function runPrincipalPolicyCache(input) {
151
151
  let externalAdminPolicy = null;
152
152
  const loadExternalAdminPolicy = () => {
153
153
  externalAdminPolicy ??= loadOrganizationExternalAdminPolicy({
154
+ currentPolicyReader: input.currentPolicyReader,
154
155
  execSql: input.execSql,
155
- getCurrentPrincipalPolicy: input.getCurrentPrincipalPolicy,
156
156
  organizationId: input.organizationId,
157
157
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
158
158
  stillCurrent: input.stillCurrent,
@@ -197,12 +197,12 @@ async function runPrincipalPolicyCache(input) {
197
197
  return [];
198
198
  }
199
199
  }
200
- async function loadReferencedPrincipalPolicies({ execSql, getCurrentPrincipalPolicy, log, organizationId, reportSecurityIncident, references, resolveTrustedUserIdentity, stillCurrent, }) {
200
+ async function loadReferencedPrincipalPolicies({ currentPolicyReader, execSql, log, organizationId, reportSecurityIncident, references, resolveTrustedUserIdentity, stillCurrent, }) {
201
201
  return runPrincipalPolicyCache({
202
- cacheItem: (reference, loadExternalAdminPolicy, loadDirectory, fresh) => cacheReferencedPrincipalPolicy(execSql, getCurrentPrincipalPolicy, reference, resolveTrustedUserIdentity, log, loadExternalAdminPolicy, organizationId, stillCurrent, loadDirectory, fresh),
202
+ cacheItem: (reference, loadExternalAdminPolicy, loadDirectory, fresh) => cacheReferencedPrincipalPolicy(execSql, currentPolicyReader, reference, resolveTrustedUserIdentity, log, loadExternalAdminPolicy, organizationId, stillCurrent, loadDirectory, fresh),
203
+ currentPolicyReader,
203
204
  dedupe: dedupeReferencedPrincipalStates,
204
205
  execSql,
205
- getCurrentPrincipalPolicy,
206
206
  itemKey: getReferencedPrincipalKey,
207
207
  items: references,
208
208
  log,
@@ -215,7 +215,7 @@ async function loadReferencedPrincipalPolicies({ execSql, getCurrentPrincipalPol
215
215
  export async function cacheReferencedPrincipalPolicies(input) {
216
216
  await loadReferencedPrincipalPolicies(input);
217
217
  }
218
- export async function cachePrincipalPolicyBundles({ bundles, execSql, getCurrentPrincipalPolicy, log, organizationId, reportSecurityIncident, resolveTrustedUserIdentity, stillCurrent, }) {
218
+ export async function cachePrincipalPolicyBundles({ bundles, currentPolicyReader, execSql, log, organizationId, reportSecurityIncident, resolveTrustedUserIdentity, stillCurrent, }) {
219
219
  await runPrincipalPolicyCache({
220
220
  cacheItem: (bundle, loadExternalAdminPolicy, loadDirectory) => cachePrincipalPolicyBundle({
221
221
  bundle,
@@ -227,9 +227,9 @@ export async function cachePrincipalPolicyBundles({ bundles, execSql, getCurrent
227
227
  resolveTrustedUserIdentity,
228
228
  stillCurrent,
229
229
  }),
230
+ currentPolicyReader,
230
231
  dedupe: dedupePrincipalPolicyBundles,
231
232
  execSql,
232
- getCurrentPrincipalPolicy,
233
233
  itemKey: getBundlePrincipalKey,
234
234
  items: bundles,
235
235
  log,
@@ -1 +1 @@
1
- {"version":3,"file":"policyCache.js","sourceRoot":"","sources":["policyCache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VerifiedPrincipalPolicy } from \"@tearleads/crypto\";\nimport type {\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport { isProjectionVerificationCancelledError } from \"../../data/keyingProjectionVerification\";\nimport {\n reportAndRethrowKeyingVerificationError,\n rethrowKeyingVerificationError,\n} from \"../../data/keyingProjectionVerification/error\";\nimport { dedupeReferencedPrincipalStates } from \"../../data/keyingProjectionVerification/principalPolicyCache\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { ensurePrincipalPolicyTables } from \"../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"./externalAdminPolicy\";\nimport {\n persistPrincipalPolicyCacheEntries,\n retryPrincipalPolicyCacheAdvance,\n} from \"./principalPolicyCacheAdvance\";\nimport { scopeReferencedPolicyForCache } from \"./principalPolicyCacheScope\";\nimport { validatePrincipalPolicyBundleForCache } from \"./principalPolicyCacheValidation\";\nimport {\n createPolicyDirectoryLoader,\n directoryBindsGroupReference,\n type PolicyDirectoryLoader,\n type VerifiedPolicyDirectory,\n} from \"./principalPolicyOrganizationScope\";\n\nexport interface CacheReferencedPrincipalPoliciesOptions {\n execSql: ExecSql;\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n log?: (message: string) => void;\n organizationId: string;\n reportSecurityIncident: SecurityIncidentReporter;\n references: ReadonlyArray<ReferencedPrincipalStateResponse> | undefined;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface CachePrincipalPolicyBundlesOptions\n extends Omit<CacheReferencedPrincipalPoliciesOptions, \"references\"> {\n bundles: ReadonlyArray<PrincipalPolicyBundleResponse> | undefined;\n}\n\nfunction getReferencedPrincipalKey(\n reference: ReferencedPrincipalStateResponse,\n): string {\n return `${reference.principalType}:${reference.principalId}`;\n}\n\nfunction getBundlePrincipalKey(bundle: PrincipalPolicyBundleResponse): string {\n return `${bundle.currentState.principalType}:${bundle.currentState.principalId}`;\n}\n\nfunction dedupePrincipalPolicyBundles(\n bundles: ReadonlyArray<PrincipalPolicyBundleResponse>,\n): PrincipalPolicyBundleResponse[] {\n const bundlesByPrincipal = new Map<string, PrincipalPolicyBundleResponse>();\n\n for (const bundle of bundles) {\n const key = getBundlePrincipalKey(bundle);\n const previous = bundlesByPrincipal.get(key);\n if (\n !previous ||\n bundle.currentState.version > previous.currentState.version\n ) {\n bundlesByPrincipal.set(key, bundle);\n }\n }\n\n return Array.from(bundlesByPrincipal.values());\n}\n\nasync function cacheReferencedPrincipalPolicy(\n execSql: ExecSql,\n getCurrentPrincipalPolicy: CacheReferencedPrincipalPoliciesOptions[\"getCurrentPrincipalPolicy\"],\n reference: ReferencedPrincipalStateResponse,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n log: ((message: string) => void) | undefined,\n loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>,\n organizationId: string,\n stillCurrent: (() => boolean) | undefined,\n loadDirectory: PolicyDirectoryLoader,\n fresh: boolean,\n): Promise<VerifiedPrincipalPolicy[]> {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n execSql,\n reference.principalType,\n reference.principalId,\n );\n // Only null is a recoverable local miss. Integrity failures remain typed and\n // must escape rather than being hidden by a canonical network retry.\n const cachedBundle = fresh\n ? null\n : await loadPrincipalPolicyBundleForReference(\n execSql,\n reference,\n localCheckpoint,\n );\n let bundle =\n cachedBundle ??\n (await getCurrentPrincipalPolicy(\n reference.principalType,\n reference.principalId,\n ));\n if (!bundle) {\n log?.(\n `Principal policy cache: failed to fetch ${getReferencedPrincipalKey(reference)}`,\n );\n return [];\n }\n\n let validation = await validatePrincipalPolicyBundleForCache({\n bundle,\n loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity,\n });\n if (!validation.ok) {\n throw validation.error;\n }\n\n const scoped = await scopeReferencedPolicyForCache({\n bundle,\n validation,\n validationInput: {\n loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity,\n },\n loadDirectory,\n organizationId,\n reloadBundle: cachedBundle\n ? () => getCurrentPrincipalPolicy(\"group\", reference.principalId)\n : null,\n });\n if (!scoped) {\n log?.(\n `Principal policy cache: signed organization directory does not cover ${getReferencedPrincipalKey(reference)}`,\n );\n return [];\n }\n bundle = scoped.bundle;\n validation = scoped.validation;\n const directory = scoped.directory;\n const externalEntries = validation.externalAdminPolicy\n ? externalAdminPolicyPersistenceEntries(\n validation.externalAdminPolicy,\n ).filter(\n (entry) => !directory || entry.policy.principalType !== \"organization\",\n )\n : [];\n const directoryEntries = directory\n ? [{ bundle: directory.bundle, policy: directory.policy }]\n : [];\n await persistPrincipalPolicyCacheEntries({\n entries: [\n ...externalEntries,\n ...directoryEntries,\n { bundle, policy: validation.policy },\n ],\n execSql,\n organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent,\n });\n return [...externalEntries.map(({ policy }) => policy), validation.policy];\n}\n\nasync function cachePrincipalPolicyBundle(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>;\n readonly log: ((message: string) => void) | undefined;\n readonly loadDirectory: PolicyDirectoryLoader;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const reference = principalPolicyReferenceFromBundle(input.bundle);\n // The API supplied this bundle after rejecting a write, but local storage is\n // only updated after normal signature, signer, and checkpoint verification.\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n reference.principalType,\n reference.principalId,\n );\n const validation = await validatePrincipalPolicyBundleForCache({\n bundle: input.bundle,\n loadExternalAdminPolicy: input.loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!validation.ok) {\n throw validation.error;\n }\n\n let directory: VerifiedPolicyDirectory | null = null;\n if (reference.principalType === \"group\") {\n directory = await input.loadDirectory(false);\n if (!directoryBindsGroupReference(directory, validation.policy, reference))\n directory = await input.loadDirectory(true);\n if (\n !directoryBindsGroupReference(directory, validation.policy, reference)\n ) {\n input.log?.(\n \"Principal policy cache: signed organization directory does not cover supplied group\",\n );\n return [];\n }\n } else if (reference.principalId !== input.organizationId) {\n return [];\n }\n const externalEntries = validation.externalAdminPolicy\n ? externalAdminPolicyPersistenceEntries(\n validation.externalAdminPolicy,\n ).filter(\n (entry) => !directory || entry.policy.principalType !== \"organization\",\n )\n : [];\n const directoryEntries = directory\n ? [{ bundle: directory.bundle, policy: directory.policy }]\n : [];\n await persistPrincipalPolicyCacheEntries({\n entries: [\n ...externalEntries,\n ...directoryEntries,\n { bundle: input.bundle, policy: validation.policy },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent: input.stillCurrent,\n });\n return [...externalEntries.map(({ policy }) => policy), validation.policy];\n}\n\nasync function runPrincipalPolicyCache<Item, Result>(input: {\n readonly cacheItem: (\n item: Item,\n loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>,\n loadDirectory: PolicyDirectoryLoader,\n fresh: boolean,\n ) => Promise<readonly Result[]>;\n readonly dedupe: (items: ReadonlyArray<Item>) => Item[];\n readonly execSql: ExecSql;\n readonly getCurrentPrincipalPolicy: CacheReferencedPrincipalPoliciesOptions[\"getCurrentPrincipalPolicy\"];\n readonly itemKey: (item: Item) => string;\n readonly items: ReadonlyArray<Item> | undefined;\n readonly log: ((message: string) => void) | undefined;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<Result[]> {\n if (!input.items || input.items.length === 0) {\n return [];\n }\n\n try {\n await ensurePrincipalPolicyTables(input.execSql);\n const uniqueItems = input.dedupe(input.items);\n const loadDirectory = createPolicyDirectoryLoader(input);\n let externalAdminPolicy: Promise<VerifiedExternalAdminPolicy | null> | null =\n null;\n const loadExternalAdminPolicy = () => {\n externalAdminPolicy ??= loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: input.getCurrentPrincipalPolicy,\n organizationId: input.organizationId,\n\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n return externalAdminPolicy;\n };\n\n const results = await Promise.all(\n uniqueItems.map(async (item) => {\n try {\n return await retryPrincipalPolicyCacheAdvance((fresh) => {\n const reloadDirectory = createPolicyDirectoryLoader(input);\n let reloadedAuthority: Promise<VerifiedExternalAdminPolicy | null> | null =\n null;\n return input.cacheItem(\n item,\n fresh\n ? () =>\n (reloadedAuthority ??=\n loadOrganizationExternalAdminPolicy(input))\n : loadExternalAdminPolicy,\n fresh ? () => reloadDirectory(true) : loadDirectory,\n fresh,\n );\n });\n } catch (error) {\n if (isProjectionVerificationCancelledError(error)) return [];\n await reportAndRethrowKeyingVerificationError(\n error,\n input.reportSecurityIncident,\n {\n objectId: input.itemKey(item),\n objectKind: \"principal\",\n operation: \"principal.policy.cache\",\n organizationId: input.organizationId,\n },\n );\n const message = errorMessage(error);\n input.log?.(\n `Principal policy cache: failed to store ${input.itemKey(item)}: ${message}`,\n );\n return [];\n }\n }),\n );\n return results.flat();\n } catch (error) {\n if (isProjectionVerificationCancelledError(error)) return [];\n // Per-item verification failures were reported above before reaching this\n // initialization/aggregation boundary.\n rethrowKeyingVerificationError(error);\n const message = errorMessage(error);\n input.log?.(\n `Principal policy cache: failed to initialize cache: ${message}`,\n );\n return [];\n }\n}\n\nasync function loadReferencedPrincipalPolicies({\n execSql,\n getCurrentPrincipalPolicy,\n log,\n organizationId,\n reportSecurityIncident,\n references,\n resolveTrustedUserIdentity,\n stillCurrent,\n}: CacheReferencedPrincipalPoliciesOptions): Promise<\n VerifiedPrincipalPolicy[]\n> {\n return runPrincipalPolicyCache({\n cacheItem: (reference, loadExternalAdminPolicy, loadDirectory, fresh) =>\n cacheReferencedPrincipalPolicy(\n execSql,\n getCurrentPrincipalPolicy,\n reference,\n resolveTrustedUserIdentity,\n log,\n loadExternalAdminPolicy,\n organizationId,\n stillCurrent,\n loadDirectory,\n fresh,\n ),\n dedupe: dedupeReferencedPrincipalStates,\n execSql,\n getCurrentPrincipalPolicy,\n itemKey: getReferencedPrincipalKey,\n items: references,\n log,\n organizationId,\n\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n });\n}\n\nexport async function cacheReferencedPrincipalPolicies(\n input: CacheReferencedPrincipalPoliciesOptions,\n): Promise<void> {\n await loadReferencedPrincipalPolicies(input);\n}\n\nexport async function cachePrincipalPolicyBundles({\n bundles,\n execSql,\n getCurrentPrincipalPolicy,\n log,\n organizationId,\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n}: CachePrincipalPolicyBundlesOptions): Promise<void> {\n await runPrincipalPolicyCache({\n cacheItem: (bundle, loadExternalAdminPolicy, loadDirectory) =>\n cachePrincipalPolicyBundle({\n bundle,\n execSql,\n loadExternalAdminPolicy,\n loadDirectory,\n log,\n organizationId,\n resolveTrustedUserIdentity,\n stillCurrent,\n }),\n dedupe: dedupePrincipalPolicyBundles,\n execSql,\n getCurrentPrincipalPolicy,\n itemKey: getBundlePrincipalKey,\n items: bundles,\n log,\n organizationId,\n\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n });\n}\n"]}
1
+ {"version":3,"file":"policyCache.js","sourceRoot":"","sources":["policyCache.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VerifiedPrincipalPolicy } from \"@tearleads/crypto\";\nimport type {\n PrincipalPolicyBundleResponse,\n ReferencedPrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { errorMessage } from \"../../data/errorMessage\";\nimport { isProjectionVerificationCancelledError } from \"../../data/keyingProjectionVerification\";\nimport {\n reportAndRethrowKeyingVerificationError,\n rethrowKeyingVerificationError,\n} from \"../../data/keyingProjectionVerification/error\";\nimport { dedupeReferencedPrincipalStates } from \"../../data/keyingProjectionVerification/principalPolicyCache\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { ensurePrincipalPolicyTables } from \"../../data/persistence/principalPolicyPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { principalPolicyReferenceFromBundle } 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 \"./currentPrincipalPolicyReader\";\nimport {\n externalAdminPolicyPersistenceEntries,\n loadOrganizationExternalAdminPolicy,\n type VerifiedExternalAdminPolicy,\n} from \"./externalAdminPolicy\";\nimport {\n persistPrincipalPolicyCacheEntries,\n retryPrincipalPolicyCacheAdvance,\n} from \"./principalPolicyCacheAdvance\";\nimport { scopeReferencedPolicyForCache } from \"./principalPolicyCacheScope\";\nimport { validatePrincipalPolicyBundleForCache } from \"./principalPolicyCacheValidation\";\nimport {\n createPolicyDirectoryLoader,\n directoryBindsGroupReference,\n type PolicyDirectoryLoader,\n type VerifiedPolicyDirectory,\n} from \"./principalPolicyOrganizationScope\";\n\nexport interface CacheReferencedPrincipalPoliciesOptions {\n currentPolicyReader: CurrentPrincipalPolicyReader;\n execSql: ExecSql;\n log?: (message: string) => void;\n organizationId: string;\n reportSecurityIncident: SecurityIncidentReporter;\n references: ReadonlyArray<ReferencedPrincipalStateResponse> | undefined;\n resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n stillCurrent?: (() => boolean) | undefined;\n}\n\nexport interface CachePrincipalPolicyBundlesOptions\n extends Omit<CacheReferencedPrincipalPoliciesOptions, \"references\"> {\n bundles: ReadonlyArray<PrincipalPolicyBundleResponse> | undefined;\n}\n\nfunction getReferencedPrincipalKey(\n reference: ReferencedPrincipalStateResponse,\n): string {\n return `${reference.principalType}:${reference.principalId}`;\n}\n\nfunction getBundlePrincipalKey(bundle: PrincipalPolicyBundleResponse): string {\n return `${bundle.currentState.principalType}:${bundle.currentState.principalId}`;\n}\n\nfunction dedupePrincipalPolicyBundles(\n bundles: ReadonlyArray<PrincipalPolicyBundleResponse>,\n): PrincipalPolicyBundleResponse[] {\n const bundlesByPrincipal = new Map<string, PrincipalPolicyBundleResponse>();\n\n for (const bundle of bundles) {\n const key = getBundlePrincipalKey(bundle);\n const previous = bundlesByPrincipal.get(key);\n if (\n !previous ||\n bundle.currentState.version > previous.currentState.version\n ) {\n bundlesByPrincipal.set(key, bundle);\n }\n }\n\n return Array.from(bundlesByPrincipal.values());\n}\n\nasync function cacheReferencedPrincipalPolicy(\n execSql: ExecSql,\n reader: CurrentPrincipalPolicyReader,\n reference: ReferencedPrincipalStateResponse,\n resolveTrustedUserIdentity: TrustedUserIdentityResolver,\n log: ((message: string) => void) | undefined,\n loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>,\n organizationId: string,\n stillCurrent: (() => boolean) | undefined,\n loadDirectory: PolicyDirectoryLoader,\n fresh: boolean,\n): Promise<VerifiedPrincipalPolicy[]> {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n execSql,\n reference.principalType,\n reference.principalId,\n );\n // Only null is a recoverable local miss. Integrity failures remain typed and\n // must escape rather than being hidden by a canonical network retry.\n const cachedBundle = fresh\n ? null\n : await loadPrincipalPolicyBundleForReference(\n execSql,\n reference,\n localCheckpoint,\n );\n let bundle =\n cachedBundle ??\n (await reader.getCurrentPrincipalPolicy(\n reference.principalType,\n reference.principalId,\n ));\n if (!bundle) {\n log?.(\n `Principal policy cache: failed to fetch ${getReferencedPrincipalKey(reference)}`,\n );\n return [];\n }\n\n let validation = await validatePrincipalPolicyBundleForCache({\n bundle,\n loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity,\n });\n if (!validation.ok) {\n throw validation.error;\n }\n\n const scoped = await scopeReferencedPolicyForCache({\n bundle,\n validation,\n validationInput: {\n loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity,\n },\n loadDirectory,\n organizationId,\n reloadBundle: cachedBundle\n ? () => reader.getCurrentPrincipalPolicy(\"group\", reference.principalId)\n : null,\n });\n if (!scoped) {\n log?.(\n `Principal policy cache: signed organization directory does not cover ${getReferencedPrincipalKey(reference)}`,\n );\n return [];\n }\n bundle = scoped.bundle;\n validation = scoped.validation;\n const directory = scoped.directory;\n const externalEntries = validation.externalAdminPolicy\n ? externalAdminPolicyPersistenceEntries(\n validation.externalAdminPolicy,\n ).filter(\n (entry) => !directory || entry.policy.principalType !== \"organization\",\n )\n : [];\n const directoryEntries = directory\n ? [{ bundle: directory.bundle, policy: directory.policy }]\n : [];\n await persistPrincipalPolicyCacheEntries({\n entries: [\n ...externalEntries,\n ...directoryEntries,\n { bundle, policy: validation.policy },\n ],\n execSql,\n organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent,\n });\n return [...externalEntries.map(({ policy }) => policy), validation.policy];\n}\n\nasync function cachePrincipalPolicyBundle(input: {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly execSql: ExecSql;\n readonly loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>;\n readonly log: ((message: string) => void) | undefined;\n readonly loadDirectory: PolicyDirectoryLoader;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<VerifiedPrincipalPolicy[]> {\n const reference = principalPolicyReferenceFromBundle(input.bundle);\n // The API supplied this bundle after rejecting a write, but local storage is\n // only updated after normal signature, signer, and checkpoint verification.\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n reference.principalType,\n reference.principalId,\n );\n const validation = await validatePrincipalPolicyBundleForCache({\n bundle: input.bundle,\n loadExternalAdminPolicy: input.loadExternalAdminPolicy,\n localCheckpoint,\n reference,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (!validation.ok) {\n throw validation.error;\n }\n\n let directory: VerifiedPolicyDirectory | null = null;\n if (reference.principalType === \"group\") {\n directory = await input.loadDirectory(false);\n if (!directoryBindsGroupReference(directory, validation.policy, reference))\n directory = await input.loadDirectory(true);\n if (\n !directoryBindsGroupReference(directory, validation.policy, reference)\n ) {\n input.log?.(\n \"Principal policy cache: signed organization directory does not cover supplied group\",\n );\n return [];\n }\n } else if (reference.principalId !== input.organizationId) {\n return [];\n }\n const externalEntries = validation.externalAdminPolicy\n ? externalAdminPolicyPersistenceEntries(\n validation.externalAdminPolicy,\n ).filter(\n (entry) => !directory || entry.policy.principalType !== \"organization\",\n )\n : [];\n const directoryEntries = directory\n ? [{ bundle: directory.bundle, policy: directory.policy }]\n : [];\n await persistPrincipalPolicyCacheEntries({\n entries: [\n ...externalEntries,\n ...directoryEntries,\n { bundle: input.bundle, policy: validation.policy },\n ],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent: input.stillCurrent,\n });\n return [...externalEntries.map(({ policy }) => policy), validation.policy];\n}\n\nasync function runPrincipalPolicyCache<Item, Result>(input: {\n readonly cacheItem: (\n item: Item,\n loadExternalAdminPolicy: () => Promise<VerifiedExternalAdminPolicy | null>,\n loadDirectory: PolicyDirectoryLoader,\n fresh: boolean,\n ) => Promise<readonly Result[]>;\n readonly currentPolicyReader: CurrentPrincipalPolicyReader;\n readonly dedupe: (items: ReadonlyArray<Item>) => Item[];\n readonly execSql: ExecSql;\n readonly itemKey: (item: Item) => string;\n readonly items: ReadonlyArray<Item> | undefined;\n readonly log: ((message: string) => void) | undefined;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<Result[]> {\n if (!input.items || input.items.length === 0) {\n return [];\n }\n\n try {\n await ensurePrincipalPolicyTables(input.execSql);\n const uniqueItems = input.dedupe(input.items);\n const loadDirectory = createPolicyDirectoryLoader(input);\n let externalAdminPolicy: Promise<VerifiedExternalAdminPolicy | null> | null =\n null;\n const loadExternalAdminPolicy = () => {\n externalAdminPolicy ??= loadOrganizationExternalAdminPolicy({\n currentPolicyReader: input.currentPolicyReader,\n execSql: input.execSql,\n organizationId: input.organizationId,\n\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n return externalAdminPolicy;\n };\n\n const results = await Promise.all(\n uniqueItems.map(async (item) => {\n try {\n return await retryPrincipalPolicyCacheAdvance((fresh) => {\n const reloadDirectory = createPolicyDirectoryLoader(input);\n let reloadedAuthority: Promise<VerifiedExternalAdminPolicy | null> | null =\n null;\n return input.cacheItem(\n item,\n fresh\n ? () =>\n (reloadedAuthority ??=\n loadOrganizationExternalAdminPolicy(input))\n : loadExternalAdminPolicy,\n fresh ? () => reloadDirectory(true) : loadDirectory,\n fresh,\n );\n });\n } catch (error) {\n if (isProjectionVerificationCancelledError(error)) return [];\n await reportAndRethrowKeyingVerificationError(\n error,\n input.reportSecurityIncident,\n {\n objectId: input.itemKey(item),\n objectKind: \"principal\",\n operation: \"principal.policy.cache\",\n organizationId: input.organizationId,\n },\n );\n const message = errorMessage(error);\n input.log?.(\n `Principal policy cache: failed to store ${input.itemKey(item)}: ${message}`,\n );\n return [];\n }\n }),\n );\n return results.flat();\n } catch (error) {\n if (isProjectionVerificationCancelledError(error)) return [];\n // Per-item verification failures were reported above before reaching this\n // initialization/aggregation boundary.\n rethrowKeyingVerificationError(error);\n const message = errorMessage(error);\n input.log?.(\n `Principal policy cache: failed to initialize cache: ${message}`,\n );\n return [];\n }\n}\n\nasync function loadReferencedPrincipalPolicies({\n currentPolicyReader,\n execSql,\n log,\n organizationId,\n reportSecurityIncident,\n references,\n resolveTrustedUserIdentity,\n stillCurrent,\n}: CacheReferencedPrincipalPoliciesOptions): Promise<\n VerifiedPrincipalPolicy[]\n> {\n return runPrincipalPolicyCache({\n cacheItem: (reference, loadExternalAdminPolicy, loadDirectory, fresh) =>\n cacheReferencedPrincipalPolicy(\n execSql,\n currentPolicyReader,\n reference,\n resolveTrustedUserIdentity,\n log,\n loadExternalAdminPolicy,\n organizationId,\n stillCurrent,\n loadDirectory,\n fresh,\n ),\n currentPolicyReader,\n dedupe: dedupeReferencedPrincipalStates,\n execSql,\n itemKey: getReferencedPrincipalKey,\n items: references,\n log,\n organizationId,\n\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n });\n}\n\nexport async function cacheReferencedPrincipalPolicies(\n input: CacheReferencedPrincipalPoliciesOptions,\n): Promise<void> {\n await loadReferencedPrincipalPolicies(input);\n}\n\nexport async function cachePrincipalPolicyBundles({\n bundles,\n currentPolicyReader,\n execSql,\n log,\n organizationId,\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n}: CachePrincipalPolicyBundlesOptions): Promise<void> {\n await runPrincipalPolicyCache({\n cacheItem: (bundle, loadExternalAdminPolicy, loadDirectory) =>\n cachePrincipalPolicyBundle({\n bundle,\n execSql,\n loadExternalAdminPolicy,\n loadDirectory,\n log,\n organizationId,\n resolveTrustedUserIdentity,\n stillCurrent,\n }),\n currentPolicyReader,\n dedupe: dedupePrincipalPolicyBundles,\n execSql,\n itemKey: getBundlePrincipalKey,\n items: bundles,\n log,\n organizationId,\n\n reportSecurityIncident,\n resolveTrustedUserIdentity,\n stillCurrent,\n });\n}\n"]}
@@ -24,7 +24,7 @@ export function createPolicyDirectoryLoader(input) {
24
24
  if (bundle &&
25
25
  !principalPolicyHeadMeetsCheckpoint(bundle.currentState, checkpoint))
26
26
  bundle = null;
27
- bundle ??= await input.getCurrentPrincipalPolicy("organization", input.organizationId);
27
+ bundle ??= await input.currentPolicyReader.getCurrentPrincipalPolicy("organization", input.organizationId);
28
28
  if (!bundle)
29
29
  return null;
30
30
  const signers = await collectPrincipalPolicySignerPublicKeys({
@@ -1 +1 @@
1
- {"version":3,"file":"principalPolicyOrganizationScope.js","sourceRoot":"","sources":["principalPolicyOrganizationScope.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { principalPolicyHeadMeetsCheckpoint } from \"../../data/persistence/principalPolicyCheckpointSelection\";\nimport { loadPrincipalPolicyBundle } from \"../../data/persistence/principalPolicyPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n parseOrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { verifyOrganizationAdminPolicy } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"./policyVerification\";\n\nexport interface VerifiedPolicyDirectory {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n readonly descriptor: OrganizationAuthorityDescriptor;\n}\n\nexport type PolicyDirectoryLoader = (\n remote: boolean,\n) => Promise<VerifiedPolicyDirectory | null>;\n\nexport function directoryBindsGroupReference(\n directory: VerifiedPolicyDirectory | null,\n policy: VerifiedPrincipalPolicy,\n reference: ReferencedPrincipalHead,\n): boolean {\n const head = directory?.descriptor.groupHeads.find(\n (head) => head.principalId === reference.principalId,\n );\n // A newer directory head authenticates its verified predecessors too.\n // Requiring equality with a historical manifest citation bricks old reads.\n return (\n !!head &&\n head.version >= reference.version &&\n head.version === policy.version &&\n [policy.state, ...(policy.history ?? []).map((entry) => entry.state)].some(\n (state) => principalHeadMatchesReference(state, head),\n )\n );\n}\n\nexport function createPolicyDirectoryLoader(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly getCurrentPrincipalPolicy: (\n kind: \"group\" | \"organization\",\n id: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): PolicyDirectoryLoader {\n const memo = new Map<boolean, Promise<VerifiedPolicyDirectory | null>>();\n const load = async (\n remote: boolean,\n ): Promise<VerifiedPolicyDirectory | null> => {\n const checkpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n let bundle = remote\n ? null\n : await loadPrincipalPolicyBundle(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n if (\n bundle &&\n !principalPolicyHeadMeetsCheckpoint(bundle.currentState, checkpoint)\n )\n bundle = null;\n bundle ??= await input.getCurrentPrincipalPolicy(\n \"organization\",\n input.organizationId,\n );\n if (!bundle) return null;\n const signers = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signers) {\n if (signers.error === \"fingerprint-mismatch\")\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"organization directory signer fingerprint does not match\",\n );\n return null;\n }\n const verified = await verifyOrganizationAdminPolicy({\n bundle,\n localCheckpoint: checkpoint,\n organizationId: input.organizationId,\n signerPublicKeys: signers.signerPublicKeys,\n });\n if (!verified.ok) throw verified.error;\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 directory payload is invalid\",\n );\n }\n if (descriptor.organizationId !== input.organizationId)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"organization directory scope does not match\",\n );\n return { bundle, policy: verified.value, descriptor };\n };\n return (remote) => {\n let pending = memo.get(remote);\n if (!pending) {\n pending = load(remote);\n memo.set(remote, pending);\n }\n return pending;\n };\n}\n"]}
1
+ {"version":3,"file":"principalPolicyOrganizationScope.js","sourceRoot":"","sources":["principalPolicyOrganizationScope.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { principalPolicyHeadMeetsCheckpoint } from \"../../data/persistence/principalPolicyCheckpointSelection\";\nimport { loadPrincipalPolicyBundle } from \"../../data/persistence/principalPolicyPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n parseOrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { verifyOrganizationAdminPolicy } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { CurrentPrincipalPolicyReader } from \"./currentPrincipalPolicyReader\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"./policyVerification\";\n\nexport interface VerifiedPolicyDirectory {\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n readonly descriptor: OrganizationAuthorityDescriptor;\n}\n\nexport type PolicyDirectoryLoader = (\n remote: boolean,\n) => Promise<VerifiedPolicyDirectory | null>;\n\nexport function directoryBindsGroupReference(\n directory: VerifiedPolicyDirectory | null,\n policy: VerifiedPrincipalPolicy,\n reference: ReferencedPrincipalHead,\n): boolean {\n const head = directory?.descriptor.groupHeads.find(\n (head) => head.principalId === reference.principalId,\n );\n // A newer directory head authenticates its verified predecessors too.\n // Requiring equality with a historical manifest citation bricks old reads.\n return (\n !!head &&\n head.version >= reference.version &&\n head.version === policy.version &&\n [policy.state, ...(policy.history ?? []).map((entry) => entry.state)].some(\n (state) => principalHeadMatchesReference(state, head),\n )\n );\n}\n\nexport function createPolicyDirectoryLoader(input: {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly currentPolicyReader: CurrentPrincipalPolicyReader;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}): PolicyDirectoryLoader {\n const memo = new Map<boolean, Promise<VerifiedPolicyDirectory | null>>();\n const load = async (\n remote: boolean,\n ): Promise<VerifiedPolicyDirectory | null> => {\n const checkpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n let bundle = remote\n ? null\n : await loadPrincipalPolicyBundle(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n if (\n bundle &&\n !principalPolicyHeadMeetsCheckpoint(bundle.currentState, checkpoint)\n )\n bundle = null;\n bundle ??= await input.currentPolicyReader.getCurrentPrincipalPolicy(\n \"organization\",\n input.organizationId,\n );\n if (!bundle) return null;\n const signers = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signers) {\n if (signers.error === \"fingerprint-mismatch\")\n throw new KeyingVerificationError(\n \"signer_mismatch\",\n \"organization directory signer fingerprint does not match\",\n );\n return null;\n }\n const verified = await verifyOrganizationAdminPolicy({\n bundle,\n localCheckpoint: checkpoint,\n organizationId: input.organizationId,\n signerPublicKeys: signers.signerPublicKeys,\n });\n if (!verified.ok) throw verified.error;\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 directory payload is invalid\",\n );\n }\n if (descriptor.organizationId !== input.organizationId)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"organization directory scope does not match\",\n );\n return { bundle, policy: verified.value, descriptor };\n };\n return (remote) => {\n let pending = memo.get(remote);\n if (!pending) {\n pending = load(remote);\n memo.set(remote, pending);\n }\n return pending;\n };\n}\n"]}