@tearleads/client-sdk 0.1.15 → 0.1.16

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 (166) hide show
  1. package/client/Tearleads.d.ts +2 -0
  2. package/client/Tearleads.js +1 -0
  3. package/client/Tearleads.js.map +1 -1
  4. package/client/index.d.ts +1 -1
  5. package/client/index.js.map +1 -1
  6. package/client/organizations/index.d.ts +4 -42
  7. package/client/organizations/index.js +13 -0
  8. package/client/organizations/index.js.map +1 -1
  9. package/client/organizations/organizationGroupNameHydration.js +8 -1
  10. package/client/organizations/organizationGroupNameHydration.js.map +1 -1
  11. package/client/organizations/organizationsTypes.d.ts +47 -0
  12. package/client/organizations/organizationsTypes.js +2 -0
  13. package/client/organizations/organizationsTypes.js.map +1 -0
  14. package/client/organizations/principalMutationRecovery.d.ts +16 -0
  15. package/client/organizations/principalMutationRecovery.js +23 -0
  16. package/client/organizations/principalMutationRecovery.js.map +1 -0
  17. package/client/organizations/principalMutationScope.js +7 -2
  18. package/client/organizations/principalMutationScope.js.map +1 -1
  19. package/client/principalMutationApi.d.ts +24 -0
  20. package/client/principalMutationApi.js +108 -0
  21. package/client/principalMutationApi.js.map +1 -0
  22. package/client/principalMutationDeadline.js +23 -0
  23. package/client/principalMutationDeadline.js.map +1 -0
  24. package/client/workflowRuntime.d.ts +3 -1
  25. package/client/workflowRuntime.js +44 -5
  26. package/client/workflowRuntime.js.map +1 -1
  27. package/data/persistence/principalCurrentAcknowledgementPersistence.d.ts +27 -0
  28. package/data/persistence/principalCurrentAcknowledgementPersistence.js +109 -0
  29. package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -0
  30. package/data/persistence/principalGrantRetirementPersistence.d.ts +3 -3
  31. package/data/persistence/principalGrantRetirementPersistence.js +4 -1
  32. package/data/persistence/principalGrantRetirementPersistence.js.map +1 -1
  33. package/data/persistence/principalKeyEnvelopeCandidates.js +12 -1
  34. package/data/persistence/principalKeyEnvelopeCandidates.js.map +1 -1
  35. package/data/persistence/principalMutationJournalPersistence.d.ts +19 -0
  36. package/data/persistence/principalMutationJournalPersistence.js +53 -0
  37. package/data/persistence/principalMutationJournalPersistence.js.map +1 -0
  38. package/data/persistence/verifiedPrincipalPolicyBundle.js +7 -51
  39. package/data/persistence/verifiedPrincipalPolicyBundle.js.map +1 -1
  40. package/data/persistence/verifiedPrincipalPolicyCurrent.js +56 -0
  41. package/data/persistence/verifiedPrincipalPolicyCurrent.js.map +1 -0
  42. package/data/principals/principalMutationJournal.d.ts +26 -0
  43. package/data/principals/principalMutationJournal.js +90 -0
  44. package/data/principals/principalMutationJournal.js.map +1 -0
  45. package/data/principals/principalMutationJournalRecord.d.ts +8 -0
  46. package/data/principals/principalMutationJournalRecord.js +25 -0
  47. package/data/principals/principalMutationJournalRecord.js.map +1 -0
  48. package/data/sqlite/principalHistoryStageSchema.js +3 -0
  49. package/data/sqlite/principalHistoryStageSchema.js.map +1 -1
  50. package/data/sqlite/principalMutationJournalSchema.d.ts +85 -0
  51. package/data/sqlite/principalMutationJournalSchema.js +13 -0
  52. package/data/sqlite/principalMutationJournalSchema.js.map +1 -0
  53. package/data/sqlite/schema.d.ts +83 -0
  54. package/data/sqlite/schema.js +3 -0
  55. package/data/sqlite/schema.js.map +1 -1
  56. package/index.d.ts +3 -3
  57. package/index.js +1 -1
  58. package/index.js.map +1 -1
  59. package/internal/crypto/index.d.ts +1 -1
  60. package/internal/crypto/index.js +1 -1
  61. package/internal/crypto/index.js.map +1 -1
  62. package/internal/crypto/keying/index.d.ts +2 -0
  63. package/internal/crypto/keying/index.js +2 -0
  64. package/internal/crypto/keying/index.js.map +1 -1
  65. package/internal/crypto/keying/principalPolicyCurrent.d.ts +1 -1
  66. package/internal/crypto/keying/principalPolicyCurrent.js +3 -3
  67. package/internal/crypto/keying/principalPolicyCurrent.js.map +1 -1
  68. package/internal/crypto/keying/principalPolicyCurrentEvidence.js +16 -0
  69. package/internal/crypto/keying/principalPolicyCurrentEvidence.js.map +1 -0
  70. package/internal/crypto/keying/principalPolicyCurrentMutation.d.ts +10 -0
  71. package/internal/crypto/keying/principalPolicyCurrentMutation.js +51 -0
  72. package/internal/crypto/keying/principalPolicyCurrentMutation.js.map +1 -0
  73. package/internal/crypto/keying/principalPolicyCurrentSuccessor.d.ts +10 -0
  74. package/internal/crypto/keying/principalPolicyCurrentSuccessor.js +60 -0
  75. package/internal/crypto/keying/principalPolicyCurrentSuccessor.js.map +1 -0
  76. package/internal/crypto/keying/principalPolicyHistory.js +4 -1
  77. package/internal/crypto/keying/principalPolicyHistory.js.map +1 -1
  78. package/internal/crypto/keying/principalPolicyHistoryReferences.js +1 -1
  79. package/internal/crypto/keying/principalPolicyHistoryReferences.js.map +1 -1
  80. package/internal/crypto/keying/principalPolicyHistoryTypes.d.ts +5 -2
  81. package/internal/crypto/keying/principalPolicyHistoryTypes.js +6 -3
  82. package/internal/crypto/keying/principalPolicyHistoryTypes.js.map +1 -1
  83. package/package.json +1 -1
  84. package/workflows/containers/child/share.js +4 -0
  85. package/workflows/containers/child/share.js.map +1 -1
  86. package/workflows/containers/child/sharePrincipalPolicy.d.ts +1 -0
  87. package/workflows/containers/child/sharePrincipalPolicy.js.map +1 -1
  88. package/workflows/organizations/acknowledgedDirectoryBindings.js +18 -0
  89. package/workflows/organizations/acknowledgedDirectoryBindings.js.map +1 -0
  90. package/workflows/organizations/currentGroupMetadataAuthority.js +32 -0
  91. package/workflows/organizations/currentGroupMetadataAuthority.js.map +1 -0
  92. package/workflows/organizations/currentOrganizationAuthority.js +88 -0
  93. package/workflows/organizations/currentOrganizationAuthority.js.map +1 -0
  94. package/workflows/organizations/currentOrganizationGroupNames.js +68 -0
  95. package/workflows/organizations/currentOrganizationGroupNames.js.map +1 -0
  96. package/workflows/organizations/currentPolicyMutationAcknowledgement.js +65 -0
  97. package/workflows/organizations/currentPolicyMutationAcknowledgement.js.map +1 -0
  98. package/workflows/organizations/deleteOrganizationGroup.js +1 -0
  99. package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
  100. package/workflows/organizations/groupCreation.js +1 -0
  101. package/workflows/organizations/groupCreation.js.map +1 -1
  102. package/workflows/organizations/groupMetadataAccess.d.ts +2 -2
  103. package/workflows/organizations/groupMetadataAccess.js +1 -1
  104. package/workflows/organizations/groupMetadataAccess.js.map +1 -1
  105. package/workflows/organizations/groupMetadataBinding.js.map +1 -1
  106. package/workflows/organizations/groupMetadataContainerAuthority.js +2 -47
  107. package/workflows/organizations/groupMetadataContainerAuthority.js.map +1 -1
  108. package/workflows/organizations/groupMetadataRootBinding.js +51 -0
  109. package/workflows/organizations/groupMetadataRootBinding.js.map +1 -0
  110. package/workflows/organizations/groupMetadataRuntime.js +20 -9
  111. package/workflows/organizations/groupMetadataRuntime.js.map +1 -1
  112. package/workflows/organizations/groupMutationAuthorization.js.map +1 -1
  113. package/workflows/organizations/groupPolicyMutationAcknowledgement.js +9 -75
  114. package/workflows/organizations/groupPolicyMutationAcknowledgement.js.map +1 -1
  115. package/workflows/organizations/groupPolicyMutationContext.d.ts +5 -13
  116. package/workflows/organizations/groupPolicyMutationContext.js +1 -0
  117. package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
  118. package/workflows/organizations/groupPolicyMutationEvidence.d.ts +26 -0
  119. package/workflows/organizations/groupPolicyMutationEvidence.js +64 -0
  120. package/workflows/organizations/groupPolicyMutationEvidence.js.map +1 -0
  121. package/workflows/organizations/groupPolicyMutationHead.js.map +1 -1
  122. package/workflows/organizations/groupPolicyRequests.js +69 -63
  123. package/workflows/organizations/groupPolicyRequests.js.map +1 -1
  124. package/workflows/organizations/index.d.ts +6 -0
  125. package/workflows/organizations/index.js +5 -0
  126. package/workflows/organizations/index.js.map +1 -1
  127. package/workflows/organizations/organizationGroupNames.js +7 -0
  128. package/workflows/organizations/organizationGroupNames.js.map +1 -1
  129. package/workflows/organizations/principalCurrentPublicationSeal.js +51 -0
  130. package/workflows/organizations/principalCurrentPublicationSeal.js.map +1 -0
  131. package/workflows/organizations/principalMutationJournalLane.js +26 -0
  132. package/workflows/organizations/principalMutationJournalLane.js.map +1 -0
  133. package/workflows/organizations/principalMutationJournalManagement.d.ts +26 -0
  134. package/workflows/organizations/principalMutationJournalManagement.js +104 -0
  135. package/workflows/organizations/principalMutationJournalManagement.js.map +1 -0
  136. package/workflows/organizations/principalMutationJournalSession.d.ts +26 -0
  137. package/workflows/organizations/principalMutationJournalSession.js +85 -0
  138. package/workflows/organizations/principalMutationJournalSession.js.map +1 -0
  139. package/workflows/organizations/principalMutationReceipt.js +21 -0
  140. package/workflows/organizations/principalMutationReceipt.js.map +1 -0
  141. package/workflows/organizations/principalPolicyReceiptArtifacts.js +74 -0
  142. package/workflows/organizations/principalPolicyReceiptArtifacts.js.map +1 -0
  143. package/workflows/organizations/principalPolicyRequest.d.ts +5 -4
  144. package/workflows/organizations/principalPolicyRequest.js.map +1 -1
  145. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.d.ts +21 -0
  146. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js +155 -0
  147. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js.map +1 -0
  148. package/workflows/principals/principalCurrentRecoveryBatch.js +14 -0
  149. package/workflows/principals/principalCurrentRecoveryBatch.js.map +1 -0
  150. package/workflows/principals/principalRecoveryDirectory.d.ts +1 -1
  151. package/workflows/principals/principalRecoveryDirectory.js +29 -4
  152. package/workflows/principals/principalRecoveryDirectory.js.map +1 -1
  153. package/workflows/principals/principalRecoveryDiscoveryPage.js +36 -0
  154. package/workflows/principals/principalRecoveryDiscoveryPage.js.map +1 -0
  155. package/workflows/principals/principalRecoveryLocalPreference.js +28 -0
  156. package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -0
  157. package/workflows/principals/principalRecoveryMemo.d.ts +16 -0
  158. package/workflows/principals/principalRecoveryMemo.js +88 -16
  159. package/workflows/principals/principalRecoveryMemo.js.map +1 -1
  160. package/workflows/principals/recoverCurrentOrganizationPolicy.js +33 -0
  161. package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -0
  162. package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +2 -1
  163. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +13 -15
  164. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
  165. package/workflows/principals/runtimePolicyRecovery.js +14 -8
  166. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
@@ -0,0 +1,88 @@
1
+ import { KeyingVerificationError, } from "../../internal/crypto/index.js";
2
+ import { ProjectionDependencyUnavailableError } from "../../data/keyingProjectionVerification/dependencyUnavailable.js";
3
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
4
+ import { validateKeyingCheckpointsAtomically } from "../../data/persistence/keyingCheckpointAdvancePersistence.js";
5
+ import { assertCurrentMatchesVerifiedPolicy } from "../../data/persistence/verifiedPrincipalPolicyCurrent.js";
6
+ import { parseOrganizationAuthorityDescriptor, principalHeadMatchesReference, requireOrganizationGroupHead, } from "../../data/principals/organizationAuthorityDescriptor.js";
7
+ /** One directory view with bounded, authenticated current artifacts for its groups. */
8
+ export async function loadCurrentOrganizationAuthority(input) {
9
+ if (input.organizationReference)
10
+ assertReferenceScope(input.organizationReference, "organization", input.organizationId);
11
+ const recoveryBatch = input.recoveryBatch ?? {};
12
+ const resolve = (reference) => input.resolveCurrentPolicy({
13
+ organizationId: input.organizationId,
14
+ reference: reference ?? undefined,
15
+ preferLocalCurrent: true,
16
+ recoveryBatch,
17
+ stillCurrent: input.stillCurrent,
18
+ });
19
+ const directory = await resolve(input.organizationReference);
20
+ await assertExactCurrent(directory, input.organizationReference ?? directory.policy.state);
21
+ const descriptor = parseOrganizationAuthorityDescriptor(directory.current.currentPayload.ciphertext);
22
+ if (descriptor.organizationId !== input.organizationId)
23
+ throw new KeyingVerificationError("object_mismatch", "Organization current policy payload is outside its scope");
24
+ const adminHead = requireOrganizationGroupHead(descriptor, descriptor.adminGroupId);
25
+ const admins = await resolve(adminHead);
26
+ await assertExactCurrent(admins, adminHead);
27
+ assertDirectoryDependency(admins, directory);
28
+ const stillCurrent = () => input.stillCurrent() && directory.stillCurrent() && admins.stillCurrent();
29
+ const validate = async (value) => {
30
+ assertProjectionVerificationCurrent(stillCurrent);
31
+ await validateKeyingCheckpointsAtomically({
32
+ access: [],
33
+ execSql: input.execSql,
34
+ policies: [...value.dependencies, value.policy],
35
+ stillCurrent: () => stillCurrent() && value.stillCurrent(),
36
+ });
37
+ assertProjectionVerificationCurrent(() => stillCurrent() && value.stillCurrent());
38
+ };
39
+ await validate(admins);
40
+ return {
41
+ admins,
42
+ descriptor,
43
+ directory,
44
+ stillCurrent,
45
+ async readGroup(groupId, reference) {
46
+ assertProjectionVerificationCurrent(stillCurrent);
47
+ const head = requireOrganizationGroupHead(descriptor, groupId);
48
+ const selected = reference ?? head;
49
+ assertReferenceScope(selected, "group", groupId);
50
+ const result = groupId === descriptor.adminGroupId &&
51
+ principalHeadMatchesReference(admins.policy.state, selected)
52
+ ? admins
53
+ : await resolve(selected);
54
+ await assertExactCurrent(result, head);
55
+ assertDirectoryDependency(result, directory);
56
+ if (groupId !== descriptor.adminGroupId)
57
+ assertAdminsDependency(result, adminHead);
58
+ await validate(result);
59
+ return result;
60
+ },
61
+ };
62
+ }
63
+ function assertReferenceScope(reference, principalType, principalId) {
64
+ if (reference.principalType !== principalType ||
65
+ reference.principalId !== principalId)
66
+ throw new KeyingVerificationError("object_mismatch", "Current policy reference is outside its scope");
67
+ }
68
+ async function assertExactCurrent(value, expected) {
69
+ assertProjectionVerificationCurrent(value.stillCurrent);
70
+ await assertCurrentMatchesVerifiedPolicy(value);
71
+ if (!principalHeadMatchesReference(value.policy.state, expected))
72
+ throw new ProjectionDependencyUnavailableError("Current policy changed from the selected organization directory");
73
+ assertProjectionVerificationCurrent(value.stillCurrent);
74
+ }
75
+ function assertDirectoryDependency(value, directory) {
76
+ const dependency = value.dependencies.find((policy) => policy.principalType === "organization" &&
77
+ policy.principalId === directory.policy.principalId);
78
+ if (!dependency ||
79
+ !principalHeadMatchesReference(dependency.state, directory.policy.state))
80
+ throw new ProjectionDependencyUnavailableError("Current policy belongs to a changed organization directory");
81
+ }
82
+ function assertAdminsDependency(value, expected) {
83
+ const authority = value.dependencies.find((policy) => policy.principalType === "group" &&
84
+ policy.principalId === expected.principalId);
85
+ if (!authority || !principalHeadMatchesReference(authority.state, expected))
86
+ throw new KeyingVerificationError("missing_dependency", "Current group policy omits its exact Admins dependency");
87
+ }
88
+ //# sourceMappingURL=currentOrganizationAuthority.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currentOrganizationAuthority.js","sourceRoot":"","sources":["currentOrganizationAuthority.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type ReferencedPrincipalHead,\n} from \"@tearleads/crypto\";\nimport { ProjectionDependencyUnavailableError } from \"../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { validateKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { assertCurrentMatchesVerifiedPolicy } from \"../../data/persistence/verifiedPrincipalPolicyCurrent\";\nimport {\n parseOrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type {\n createRuntimePrincipalPolicyCurrentResolver,\n ResolvedPrincipalPolicyCurrent,\n} from \"../principals/runtimePolicyRecovery\";\n\ntype CurrentPolicyResolver = NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n>;\n\ninterface CurrentOrganizationAuthorityInput {\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly organizationReference?: ReferencedPrincipalHead | null | undefined;\n readonly resolveCurrentPolicy: CurrentPolicyResolver;\n readonly recoveryBatch?: object | undefined;\n readonly stillCurrent: () => boolean;\n}\n\n/** One directory view with bounded, authenticated current artifacts for its groups. */\nexport async function loadCurrentOrganizationAuthority(\n input: CurrentOrganizationAuthorityInput,\n) {\n if (input.organizationReference)\n assertReferenceScope(\n input.organizationReference,\n \"organization\",\n input.organizationId,\n );\n const recoveryBatch = input.recoveryBatch ?? {};\n const resolve = (reference?: ReferencedPrincipalHead | null) =>\n input.resolveCurrentPolicy({\n organizationId: input.organizationId,\n reference: reference ?? undefined,\n preferLocalCurrent: true,\n recoveryBatch,\n stillCurrent: input.stillCurrent,\n });\n const directory = await resolve(input.organizationReference);\n await assertExactCurrent(\n directory,\n input.organizationReference ?? directory.policy.state,\n );\n const descriptor = parseOrganizationAuthorityDescriptor(\n directory.current.currentPayload.ciphertext,\n );\n if (descriptor.organizationId !== input.organizationId)\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Organization current policy payload is outside its scope\",\n );\n const adminHead = requireOrganizationGroupHead(\n descriptor,\n descriptor.adminGroupId,\n );\n const admins = await resolve(adminHead);\n await assertExactCurrent(admins, adminHead);\n assertDirectoryDependency(admins, directory);\n const stillCurrent = () =>\n input.stillCurrent() && directory.stillCurrent() && admins.stillCurrent();\n const validate = async (value: ResolvedPrincipalPolicyCurrent) => {\n assertProjectionVerificationCurrent(stillCurrent);\n await validateKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n policies: [...value.dependencies, value.policy],\n stillCurrent: () => stillCurrent() && value.stillCurrent(),\n });\n assertProjectionVerificationCurrent(\n () => stillCurrent() && value.stillCurrent(),\n );\n };\n await validate(admins);\n return {\n admins,\n descriptor,\n directory,\n stillCurrent,\n async readGroup(groupId: string, reference?: ReferencedPrincipalHead) {\n assertProjectionVerificationCurrent(stillCurrent);\n const head = requireOrganizationGroupHead(descriptor, groupId);\n const selected = reference ?? head;\n assertReferenceScope(selected, \"group\", groupId);\n const result =\n groupId === descriptor.adminGroupId &&\n principalHeadMatchesReference(admins.policy.state, selected)\n ? admins\n : await resolve(selected);\n await assertExactCurrent(result, head);\n assertDirectoryDependency(result, directory);\n if (groupId !== descriptor.adminGroupId)\n assertAdminsDependency(result, adminHead);\n await validate(result);\n return result;\n },\n };\n}\n\nfunction assertReferenceScope(\n reference: ReferencedPrincipalHead,\n principalType: ReferencedPrincipalHead[\"principalType\"],\n principalId: string,\n) {\n if (\n reference.principalType !== principalType ||\n reference.principalId !== principalId\n )\n throw new KeyingVerificationError(\n \"object_mismatch\",\n \"Current policy reference is outside its scope\",\n );\n}\n\nasync function assertExactCurrent(\n value: ResolvedPrincipalPolicyCurrent,\n expected: ReferencedPrincipalHead,\n) {\n assertProjectionVerificationCurrent(value.stillCurrent);\n await assertCurrentMatchesVerifiedPolicy(value);\n if (!principalHeadMatchesReference(value.policy.state, expected))\n throw new ProjectionDependencyUnavailableError(\n \"Current policy changed from the selected organization directory\",\n );\n assertProjectionVerificationCurrent(value.stillCurrent);\n}\n\nfunction assertDirectoryDependency(\n value: ResolvedPrincipalPolicyCurrent,\n directory: ResolvedPrincipalPolicyCurrent,\n) {\n const dependency = value.dependencies.find(\n (policy) =>\n policy.principalType === \"organization\" &&\n policy.principalId === directory.policy.principalId,\n );\n if (\n !dependency ||\n !principalHeadMatchesReference(dependency.state, directory.policy.state)\n )\n throw new ProjectionDependencyUnavailableError(\n \"Current policy belongs to a changed organization directory\",\n );\n}\n\nfunction assertAdminsDependency(\n value: ResolvedPrincipalPolicyCurrent,\n expected: ReferencedPrincipalHead,\n) {\n const authority = value.dependencies.find(\n (policy) =>\n policy.principalType === \"group\" &&\n policy.principalId === expected.principalId,\n );\n if (!authority || !principalHeadMatchesReference(authority.state, expected))\n throw new KeyingVerificationError(\n \"missing_dependency\",\n \"Current group policy omits its exact Admins dependency\",\n );\n}\n"]}
@@ -0,0 +1,68 @@
1
+ import { KeyingVerificationError } from "../../internal/crypto/index.js";
2
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
3
+ import { advanceKeyingCheckpointsAtomically } from "../../data/persistence/keyingCheckpointAdvancePersistence.js";
4
+ import { saveOrganizationGroupDisplayNames } from "../../data/persistence/organizations/organizationGroupNamePersistence.js";
5
+ import { loadCurrentOrganizationAuthority } from "./currentOrganizationAuthority.js";
6
+ import { GroupMetadataUnreadableError } from "./groupMetadataAccess.js";
7
+ import { assertGroupMetadataBinding } from "./groupMetadataBinding.js";
8
+ import { readGroupPolicyPayloadName, } from "./principalPolicyRequest.js";
9
+ /** Names consume verified current artifacts; paged recovery retains their separate evidence. */
10
+ export async function hydrateCurrentOrganizationGroupNames(input) {
11
+ const authority = await loadCurrentOrganizationAuthority(input);
12
+ const names = [];
13
+ const unreadable = new Set();
14
+ const lifetimes = [authority.stillCurrent];
15
+ for (const group of input.directory.groups) {
16
+ const head = authority.descriptor.groupHeads.find((head) => head.principalId === group.groupId);
17
+ if (!head || head.stateHash !== group.currentState?.stateHash)
18
+ throw new Error("Group listing does not match the signed directory");
19
+ const verified = await authority.readGroup(group.groupId);
20
+ lifetimes.push(verified.stillCurrent);
21
+ assertGroupMetadataBinding(verified.current, authority.descriptor);
22
+ const name = await readCurrentGroupName(input, verified.current);
23
+ if (name === null)
24
+ unreadable.add(group.groupId);
25
+ else
26
+ names.push({ groupId: group.groupId, name, stateHash: head.stateHash });
27
+ }
28
+ const stillCurrent = () => input.stillCurrent() && lifetimes.every((current) => current());
29
+ assertProjectionVerificationCurrent(stillCurrent);
30
+ // Match the authority admission performed by the full-bundle path. Group
31
+ // name reads alone do not move group checkpoints.
32
+ await advanceKeyingCheckpointsAtomically({
33
+ access: [],
34
+ execSql: input.execSql,
35
+ organizationId: input.organizationId,
36
+ policies: [authority.directory.policy, authority.admins.policy],
37
+ stillCurrent,
38
+ });
39
+ await saveOrganizationGroupDisplayNames({ ...input, names, stillCurrent });
40
+ assertProjectionVerificationCurrent(stillCurrent);
41
+ const byId = new Map(names.map((entry) => [entry.groupId, entry.name]));
42
+ return {
43
+ ...input.directory,
44
+ groups: input.directory.groups.map((group) => ({
45
+ ...group,
46
+ name: byId.get(group.groupId) ?? "",
47
+ nameUnreadable: unreadable.has(group.groupId),
48
+ })),
49
+ };
50
+ }
51
+ async function readCurrentGroupName(input, current) {
52
+ try {
53
+ return await readGroupPolicyPayloadName(current, input.readEncryptedName);
54
+ }
55
+ catch (error) {
56
+ if (!(error instanceof GroupMetadataUnreadableError))
57
+ throw error;
58
+ await input.reportSecurityIncident(new KeyingVerificationError("invalid_shape", "Signed group name does not open under the cited metadata key"), {
59
+ evidenceHashes: { stateHash: current.currentState.stateHash },
60
+ objectId: current.currentState.principalId,
61
+ objectKind: "principal",
62
+ operation: "organization.groupName",
63
+ organizationId: input.organizationId,
64
+ });
65
+ return null;
66
+ }
67
+ }
68
+ //# sourceMappingURL=currentOrganizationGroupNames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currentOrganizationGroupNames.js","sourceRoot":"","sources":["currentOrganizationGroupNames.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { advanceKeyingCheckpointsAtomically } from \"../../data/persistence/keyingCheckpointAdvancePersistence\";\nimport { saveOrganizationGroupDisplayNames } from \"../../data/persistence/organizations/organizationGroupNamePersistence\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport { loadCurrentOrganizationAuthority } from \"./currentOrganizationAuthority\";\nimport { GroupMetadataUnreadableError } from \"./groupMetadataAccess\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport {\n type GroupPolicyNameReader,\n readGroupPolicyPayloadName,\n} from \"./principalPolicyRequest\";\nimport type { OrganizationDirectoryAndGroups } from \"./readModel\";\n\ntype Input = Parameters<typeof loadCurrentOrganizationAuthority>[0] & {\n readonly directory: OrganizationDirectoryAndGroups;\n readonly readEncryptedName?: GroupPolicyNameReader | undefined;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n};\n\n/** Names consume verified current artifacts; paged recovery retains their separate evidence. */\nexport async function hydrateCurrentOrganizationGroupNames(\n input: Input,\n): Promise<OrganizationDirectoryAndGroups> {\n const authority = await loadCurrentOrganizationAuthority(input);\n const names: { groupId: string; name: string; stateHash: string }[] = [];\n const unreadable = new Set<string>();\n const lifetimes: (() => boolean)[] = [authority.stillCurrent];\n for (const group of input.directory.groups) {\n const head = authority.descriptor.groupHeads.find(\n (head) => head.principalId === group.groupId,\n );\n if (!head || head.stateHash !== group.currentState?.stateHash)\n throw new Error(\"Group listing does not match the signed directory\");\n const verified = await authority.readGroup(group.groupId);\n lifetimes.push(verified.stillCurrent);\n assertGroupMetadataBinding(verified.current, authority.descriptor);\n const name = await readCurrentGroupName(input, verified.current);\n if (name === null) unreadable.add(group.groupId);\n else\n names.push({ groupId: group.groupId, name, stateHash: head.stateHash });\n }\n const stillCurrent = () =>\n input.stillCurrent() && lifetimes.every((current) => current());\n assertProjectionVerificationCurrent(stillCurrent);\n // Match the authority admission performed by the full-bundle path. Group\n // name reads alone do not move group checkpoints.\n await advanceKeyingCheckpointsAtomically({\n access: [],\n execSql: input.execSql,\n organizationId: input.organizationId,\n policies: [authority.directory.policy, authority.admins.policy],\n stillCurrent,\n });\n await saveOrganizationGroupDisplayNames({ ...input, names, stillCurrent });\n assertProjectionVerificationCurrent(stillCurrent);\n const byId = new Map(names.map((entry) => [entry.groupId, entry.name]));\n return {\n ...input.directory,\n groups: input.directory.groups.map((group) => ({\n ...group,\n name: byId.get(group.groupId) ?? \"\",\n nameUnreadable: unreadable.has(group.groupId),\n })),\n };\n}\n\nasync function readCurrentGroupName(\n input: Input,\n current: Parameters<GroupPolicyNameReader>[0],\n) {\n try {\n return await readGroupPolicyPayloadName(current, 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: current.currentState.stateHash },\n objectId: current.currentState.principalId,\n objectKind: \"principal\",\n operation: \"organization.groupName\",\n organizationId: input.organizationId,\n },\n );\n return null;\n }\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { verifyPrincipalPolicyCheckpoint, verifyPrincipalPolicyCurrentSuccessor, } from "../../internal/crypto/index.js";
2
+ import { canonicalKeyingJsonString } from "../../data/keyingCanonicalJson.js";
3
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
4
+ import { assertPrincipalPolicyCurrentStateMatchesHead } from "./groupPolicyMutationHead.js";
5
+ /** Verify a single authored successor; this neither admits it nor persists a pin. */
6
+ export async function verifyCurrentPolicyMutation(input, acknowledgedState) {
7
+ const previous = input.verifiedCurrentPolicy;
8
+ const stillCurrent = input.stillCurrent;
9
+ const owned = structuredClone({
10
+ expectedHead: input.expectedHead,
11
+ request: input.request,
12
+ signerPublicKeys: input.signerPublicKeys,
13
+ externalAuthority: input.externalAuthority,
14
+ localPolicyCheckpoint: input.localPolicyCheckpoint,
15
+ acknowledgedState,
16
+ });
17
+ assertProjectionVerificationCurrent(stillCurrent);
18
+ const { request, expectedHead } = owned;
19
+ const state = owned.acknowledgedState ?? {
20
+ ...request.state,
21
+ stateHash: expectedHead.stateHash,
22
+ createdAt: request.state.signedAt,
23
+ };
24
+ if (owned.acknowledgedState) {
25
+ const { createdAt: _createdAt, stateHash, ...responseState } = state;
26
+ if (stateHash !== expectedHead.stateHash ||
27
+ canonicalKeyingJsonString(responseState, "stored group policy state") !==
28
+ canonicalKeyingJsonString(request.state, "authored group policy state"))
29
+ throw new Error("Group policy state acknowledgement mismatch");
30
+ }
31
+ const verified = await verifyPrincipalPolicyCurrentSuccessor({
32
+ previous,
33
+ signerPublicKeys: owned.signerPublicKeys,
34
+ externalAuthority: owned.externalAuthority,
35
+ current: {
36
+ currentState: state,
37
+ currentProjection: request.projection,
38
+ currentGrants: request.grants,
39
+ currentPayload: {
40
+ ...request.encryptedPayload,
41
+ principalId: state.principalId,
42
+ principalType: state.principalType,
43
+ stateHash: state.stateHash,
44
+ },
45
+ currentMemberEnvelopes: {
46
+ envelopes: request.memberEnvelopes,
47
+ principalId: state.principalId,
48
+ principalType: state.principalType,
49
+ stateHash: state.stateHash,
50
+ epoch: state.keyEpoch,
51
+ },
52
+ },
53
+ });
54
+ if (!verified.ok)
55
+ throw verified.error;
56
+ assertPrincipalPolicyCurrentStateMatchesHead(verified.value.state, expectedHead);
57
+ verifyPrincipalPolicyCheckpoint({
58
+ chain: verified.value.retainedHistory,
59
+ currentState: verified.value.state,
60
+ localCheckpoint: owned.localPolicyCheckpoint,
61
+ });
62
+ assertProjectionVerificationCurrent(stillCurrent);
63
+ return verified.value;
64
+ }
65
+ //# sourceMappingURL=currentPolicyMutationAcknowledgement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currentPolicyMutationAcknowledgement.js","sourceRoot":"","sources":["currentPolicyMutationAcknowledgement.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type PrincipalPolicyCheckpoint,\n type PrincipalPolicyExternalAuthority,\n type PrincipalPolicySignerPublicKey,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicyCurrent,\n verifyPrincipalPolicyCheckpoint,\n verifyPrincipalPolicyCurrentSuccessor,\n} from \"@tearleads/crypto\";\nimport type { PutPrincipalPolicyRequest } from \"@tearleads/validators/request\";\nimport type { PrincipalStateResponse } from \"@tearleads/validators/response\";\nimport { canonicalKeyingJsonString } from \"../../data/keyingCanonicalJson\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { assertPrincipalPolicyCurrentStateMatchesHead } from \"./groupPolicyMutationHead\";\n\nexport interface CurrentPolicyMutationInput {\n readonly currentPolicy?: never;\n readonly verifiedCurrentPolicy: VerifiedPrincipalPolicyCurrent;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly request: PutPrincipalPolicyRequest;\n readonly signerPublicKeys: readonly PrincipalPolicySignerPublicKey[];\n readonly externalAuthority?: PrincipalPolicyExternalAuthority | undefined;\n readonly localPolicyCheckpoint?: PrincipalPolicyCheckpoint | null;\n readonly stillCurrent: () => boolean;\n}\n\n/** Verify a single authored successor; this neither admits it nor persists a pin. */\nexport async function verifyCurrentPolicyMutation(\n input: CurrentPolicyMutationInput,\n acknowledgedState?: PrincipalStateResponse,\n): Promise<VerifiedPrincipalPolicyCurrent> {\n const previous = input.verifiedCurrentPolicy;\n const stillCurrent = input.stillCurrent;\n const owned = structuredClone({\n expectedHead: input.expectedHead,\n request: input.request,\n signerPublicKeys: input.signerPublicKeys,\n externalAuthority: input.externalAuthority,\n localPolicyCheckpoint: input.localPolicyCheckpoint,\n acknowledgedState,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const { request, expectedHead } = owned;\n const state = owned.acknowledgedState ?? {\n ...request.state,\n stateHash: expectedHead.stateHash,\n createdAt: request.state.signedAt,\n };\n if (owned.acknowledgedState) {\n const { createdAt: _createdAt, stateHash, ...responseState } = state;\n if (\n stateHash !== expectedHead.stateHash ||\n canonicalKeyingJsonString(responseState, \"stored group policy state\") !==\n canonicalKeyingJsonString(request.state, \"authored group policy state\")\n )\n throw new Error(\"Group policy state acknowledgement mismatch\");\n }\n const verified = await verifyPrincipalPolicyCurrentSuccessor({\n previous,\n signerPublicKeys: owned.signerPublicKeys,\n externalAuthority: owned.externalAuthority,\n current: {\n currentState: state,\n currentProjection: request.projection,\n currentGrants: request.grants,\n currentPayload: {\n ...request.encryptedPayload,\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: state.stateHash,\n },\n currentMemberEnvelopes: {\n envelopes: request.memberEnvelopes,\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: state.stateHash,\n epoch: state.keyEpoch,\n },\n },\n });\n if (!verified.ok) throw verified.error;\n assertPrincipalPolicyCurrentStateMatchesHead(\n verified.value.state,\n expectedHead,\n );\n verifyPrincipalPolicyCheckpoint({\n chain: verified.value.retainedHistory,\n currentState: verified.value.state,\n localCheckpoint: owned.localPolicyCheckpoint,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return verified.value;\n}\n"]}
@@ -5,6 +5,7 @@ import { groupPolicyMutationHead } from "./groupPolicyMutationHead.js";
5
5
  import { buildOrganizationGroupDirectoryPolicyRequest, removeOrganizationGroupHead, } from "./organizationGroupDirectory.js";
6
6
  import { projectionUserIds, resolveRequiredUserIdentities, } from "./trustedOrganizationUsers.js";
7
7
  export async function deleteOrganizationGroup(input) {
8
+ await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
8
9
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
9
10
  execSql: input.execSql,
10
11
  getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
@@ -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 { 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}): Promise<DeleteOrganizationGroupResponse> {\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 });\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 const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: organizationRequest },\n );\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 });\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 { 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}): Promise<DeleteOrganizationGroupResponse> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\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 });\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 const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: organizationRequest },\n );\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 });\n return stored;\n}\n"]}
@@ -3,6 +3,7 @@ import { cacheGroupPolicy, } from "./groupPolicyMutationContext.js";
3
3
  import { commitCreatedGroupToDirectory } from "./organizationGroupDirectory.js";
4
4
  import { buildInitialGroupPolicyRequest } from "./principalPolicyRequest.js";
5
5
  export async function createOrganizationGroup(input) {
6
+ await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
6
7
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
7
8
  execSql: input.execSql,
8
9
  getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
@@ -1 +1 @@
1
- {"version":3,"file":"groupCreation.js","sourceRoot":"","sources":["groupCreation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mCAAmC,EAAE,MAAM,mCAAmC,CAAC;AAExF,OAAO,EACL,gBAAgB,GAEjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAG1E,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,KAY7C;IACC,MAAM,mBAAmB,GAAG,MAAM,mCAAmC,CAAC;QACpE,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,yBAAyB,EAAE,CAAC,aAAa,EAAE,WAAW,EAAE,EAAE,CACxD,KAAK,CAAC,SAAS,CAAC,yBAAyB,CAAC,aAAa,EAAE,WAAW,CAAC;QACvE,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;KAC7D,CAAC,CAAC;IACH,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,mBAAmB,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,8BAA8B,CAAC;QACnD,2BAA2B,EAAE,KAAK,CAAC,2BAA2B;QAC9D,iBAAiB,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,WAAW;QACpE,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE;QAC5B,oBAAoB,EAAE,KAAK;QAC3B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,MAAM,KAAK,CAAC,cAAc,CAAC,iBAAiB,EAAE;QAC3D,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,cAAc,EAAE,KAAK,CAAC,cAAc;KACrC,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,6BAA6B,CAAC;QACxE,GAAG,KAAK;QACR,mBAAmB;QACnB,iBAAiB,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ;QAChD,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,gBAAgB,CAAC;QACrB,2BAA2B,EAAE;YAC3B,SAAS,EAAE,OAAO,CAAC,kBAAkB,CAAC,eAAe;YACrD,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,QAAQ;YAChD,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,aAAa,EAAE,OAAO;YACtB,SAAS,EAAE,YAAY,CAAC,SAAS;SAClC;QACD,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,mBAAmB,EAAE,YAAY;QACjC,iBAAiB,EAAE,mBAAmB,CAAC,iBAAiB;QACxD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,qBAAqB,EAAE;YACrB,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,aAAa,EAAE,OAAO;YACtB,SAAS,EAAE,YAAY,CAAC,SAAS;YACjC,OAAO,EAAE,YAAY,CAAC,OAAO;SAC9B;QACD,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;KAC7D,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;AACtE,CAAC","sourcesContent":["import type { EncapsulationKeyPair, SigningKeyPair } from \"@tearleads/crypto\";\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}): Promise<OrganizationGroupSummary> {\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 });\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\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 });\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 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}): Promise<OrganizationGroupSummary> {\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\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 });\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\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 });\n return { ...group, name: input.name.trim(), nameUnreadable: false };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type ContainerAccessManifestState, type GroupMetadataKey } from "../../internal/crypto/index.js";
1
+ import { type ContainerAccessManifestState, type GroupMetadataKey, type ReferencedPrincipalHead } from "../../internal/crypto/index.js";
2
2
  import type { ContainerWriterProjectionResponse } from "../../internal/validators/response/index.js";
3
3
  import type { ProjectionUserKeyResolver, ReferencedPrincipalPolicyWarmer } from "../../data/keyingProjectionVerification.js";
4
4
  import type { ExecSql } from "../../data/sqlite/sqlSchema.js";
@@ -8,7 +8,7 @@ export interface GroupMetadataAccessInput {
8
8
  evictContainerWriterProjection(containerId: string): void;
9
9
  getContainerWriterProjection(containerId: string): Promise<ContainerWriterProjectionResponse | null>;
10
10
  };
11
- readonly verifyMetadataContainer: (state: ContainerAccessManifestState) => Promise<void>;
11
+ readonly verifyMetadataContainer: (state: ContainerAccessManifestState, organizationReference?: ReferencedPrincipalHead | undefined) => Promise<void>;
12
12
  readonly execSql: ExecSql;
13
13
  readonly organizationId: string;
14
14
  readonly resolveProjectionUserKey: ProjectionUserKeyResolver;
@@ -37,7 +37,7 @@ async function loadMetadataKeyringOnce(input, containerId) {
37
37
  state.systemSlot !== slot ||
38
38
  state.parentContainerId !== null)
39
39
  throw new Error("Group metadata requires the signed organization metadata container");
40
- await input.verifyMetadataContainer(state);
40
+ await input.verifyMetadataContainer(state, projection.policyEvidence.organization?.head);
41
41
  assertProjectionVerificationCurrent(input.stillCurrent);
42
42
  const keys = await unwrapContainerKekPath({
43
43
  ...input,
@@ -1 +1 @@
1
- {"version":3,"file":"groupMetadataAccess.js","sourceRoot":"","sources":["groupMetadataAccess.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EAEpB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,6CAA6C,EAAE,MAAM,2CAA2C,CAAC;AAE1G,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8CAA8C,CAAC;AAKtF,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AAExF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAuB/D;;;;;;GAMG;AACH,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IACrD,YAAY,OAAe,EAAE,KAAc;QACzC,KAAK,CAAC,SAAS,OAAO,+BAA+B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAED,SAAS,mBAAmB,CAC1B,KAA+B,EAC/B,WAAmB;IAEnB,OAAO,sBAAsB,CAC3B,GAAG,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,WAAW,CAAC,EACjD,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,8BAA8B,CAAC,WAAW,CAAC,CAClE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,uBAAuB,CACpC,KAA+B,EAC/B,WAAmB;IAEnB,MAAM,UAAU,GACd,MAAM,KAAK,CAAC,SAAS,CAAC,4BAA4B,CAAC,WAAW,CAAC,CAAC;IAClE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IACE,UAAU,CAAC,WAAW,KAAK,WAAW;QACtC,UAAU,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAElD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,6CAA6C,CAAC;QAC/D,cAAc,EAAE,KAAK,CAAC,cAAc;KACrC,CAAC,CAAC;IACH,IACE,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC7C,KAAK,CAAC,UAAU,KAAK,IAAI;QACzB,KAAK,CAAC,iBAAiB,KAAK,IAAI;QAEhC,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,MAAM,KAAK,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAC3C,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC;QACxC,GAAG,KAAK;QACR,UAAU;QACV,SAAS,EAAE,KAAK,CAAC,eAAe;KACjC,CAAC,CAAC;IACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW;QACd,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,OAAO;QACL,OAAO,EAAE;YACP,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW;YACX,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,mBAAmB;YACnD,WAAW;SACZ;QACD,IAAI;KACL,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,yBAAyB,CAAC,KAA+B;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAMlB,CAAC;IACJ,SAAS,MAAM,CAAC,WAAmB;QACjC,IAAI,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,mBAAmB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YAClD,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,QAAQ,GAA0B,KAAK,EAAE,MAAM,EAAE,EAAE;QACvD,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,MAAM,IAAI,QAAQ;YACpB,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,IACE,QAAQ,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;YAChD,QAAQ,CAAC,OAAO,KAAK,MAAM,CAAC,YAAY,CAAC,WAAW;YACpD,MAAM,CAAC,YAAY,CAAC,aAAa,KAAK,OAAO;YAE7C,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QACpE,IAAI,CAAC,WAAW;YACd,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,IAAI,CAAC;YACH,OAAO,MAAM,oBAAoB,CAAC;gBAChC,GAAG,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE;gBACjC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,WAAW;gBACxC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU;aAC1C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,4BAA4B,CACpC,MAAM,CAAC,YAAY,CAAC,WAAW,EAC/B,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IACF,OAAO;QACL,QAAQ;QACR,KAAK,CAAC,iBAAiB;YACrB,MAAM,IAAI,GAAG,MAAM,6CAA6C,CAAC;gBAC/D,cAAc,EAAE,KAAK,CAAC,cAAc;aACrC,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACvD,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAC/B,CAAC,GAAG,EAAE,EAAE,CACN,GAAG,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;gBAC3C,GAAG,CAAC,UAAU,KAAK,IAAI,CAC1B,CAAC;YACF,IAAI,CAAC,SAAS;gBACZ,MAAM,IAAI,KAAK,CACb,yDAAyD,CAC1D,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACzE,OAAO,OAAO,CAAC,OAAO,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import {\n type ContainerAccessManifestState,\n decryptGroupMetadata,\n type GroupMetadataKey,\n readGroupMetadata,\n} from \"@tearleads/crypto\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../data/containers/shared/projection\";\nimport { unwrapContainerKekPath } from \"../../data/documents/shared/containerKekPath\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../data/keyingProjectionVerification\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadContainers } from \"../../data/persistence/containers/containerPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport { withMetadataRootReload } from \"./groupMetadataErrors\";\nimport type { GroupPolicyNameReader } from \"./principalPolicyRequest\";\n\nexport interface GroupMetadataAccessInput {\n readonly apiClient: {\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n };\n readonly verifyMetadataContainer: (\n state: ContainerAccessManifestState,\n ) => Promise<void>;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly targetSecretKey: Uint8Array;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/**\n * A group's signed name does not open under the metadata key it cites, or opens\n * to an invalid name. Every reader holding that key gets the same result. The\n * API checks the citation but can never check the ciphertext, so a dishonest\n * admin can sign such a name; directory walks isolate the group instead of\n * failing every name in the organization.\n */\nexport class GroupMetadataUnreadableError extends Error {\n constructor(groupId: string, cause: unknown) {\n super(`Group ${groupId} metadata cannot be decrypted`, { cause });\n this.name = \"GroupMetadataUnreadableError\";\n }\n}\n\nfunction loadMetadataKeyring(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n return withMetadataRootReload(\n () => loadMetadataKeyringOnce(input, containerId),\n () => input.apiClient.evictContainerWriterProjection(containerId),\n );\n}\n\nasync function loadMetadataKeyringOnce(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n const projection =\n await input.apiClient.getContainerWriterProjection(containerId);\n if (!projection) return null;\n if (\n projection.containerId !== containerId ||\n projection.organizationId !== input.organizationId\n )\n throw new Error(\"Group metadata container is unavailable\");\n const target = getTargetContainerContext(projection);\n const state = readContainerState(target.manifest);\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n if (\n state.organizationId !== input.organizationId ||\n state.systemSlot !== slot ||\n state.parentContainerId !== null\n )\n throw new Error(\n \"Group metadata requires the signed organization metadata container\",\n );\n await input.verifyMetadataContainer(state);\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keys = await unwrapContainerKekPath({\n ...input,\n projection,\n secretKey: input.targetSecretKey,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keyMaterial = keys.get(target.kek.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata container key is unavailable\");\n return {\n current: {\n organizationId: input.organizationId,\n containerId,\n containerKeyEpochId: target.kek.containerKeyEpochId,\n keyMaterial,\n },\n keys,\n };\n}\n\n/** Uses the shared metadata container's authenticated keyring, including old epochs. */\nexport function createGroupMetadataAccess(input: GroupMetadataAccessInput) {\n const loads = new Map<\n string,\n Promise<{\n current: GroupMetadataKey;\n keys: ReadonlyMap<string, Uint8Array>;\n } | null>\n >();\n function loaded(containerId: string) {\n let pending = loads.get(containerId);\n if (!pending) {\n pending = loadMetadataKeyring(input, containerId);\n loads.set(containerId, pending);\n }\n return pending;\n }\n const readName: GroupPolicyNameReader = async (bundle) => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n if (\"role\" in metadata)\n return metadata.role === \"admins\" ? \"Admins\" : \"Members\";\n if (\n metadata.organizationId !== input.organizationId ||\n metadata.groupId !== bundle.currentState.principalId ||\n bundle.currentState.principalType !== \"group\"\n )\n throw new Error(\"Group metadata scope does not match its signed group\");\n const keyring = await loaded(metadata.containerId);\n const keyMaterial = keyring?.keys.get(metadata.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata historical key is unavailable\");\n try {\n return await decryptGroupMetadata({\n key: { ...metadata, keyMaterial },\n groupId: bundle.currentState.principalId,\n payload: bundle.currentPayload.ciphertext,\n });\n } catch (cause) {\n throw new GroupMetadataUnreadableError(\n bundle.currentState.principalId,\n cause,\n );\n }\n };\n return {\n readName,\n async loadEncryptionKey(): Promise<GroupMetadataKey> {\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n const containers = await loadContainers(input.execSql);\n const container = containers.find(\n (row) =>\n row.organizationId === input.organizationId &&\n row.systemSlot === slot,\n );\n if (!container)\n throw new Error(\n \"Organization metadata container has not been discovered\",\n );\n const keyring = await loaded(container.id);\n if (!keyring) throw new Error(\"Group metadata container is unavailable\");\n return keyring.current;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"groupMetadataAccess.js","sourceRoot":"","sources":["groupMetadataAccess.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerAccessManifestState,\n decryptGroupMetadata,\n type GroupMetadataKey,\n type ReferencedPrincipalHead,\n readGroupMetadata,\n} from \"@tearleads/crypto\";\nimport { deriveOrganizationMetadataContainerSystemSlot } from \"@tearleads/validators/containerSystemSlot\";\nimport type { ContainerWriterProjectionResponse } from \"@tearleads/validators/response\";\nimport {\n getTargetContainerContext,\n readContainerState,\n} from \"../../data/containers/shared/projection\";\nimport { unwrapContainerKekPath } from \"../../data/documents/shared/containerKekPath\";\nimport type {\n ProjectionUserKeyResolver,\n ReferencedPrincipalPolicyWarmer,\n} from \"../../data/keyingProjectionVerification\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadContainers } from \"../../data/persistence/containers/containerPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport { withMetadataRootReload } from \"./groupMetadataErrors\";\nimport type { GroupPolicyNameReader } from \"./principalPolicyRequest\";\n\nexport interface GroupMetadataAccessInput {\n readonly apiClient: {\n evictContainerWriterProjection(containerId: string): void;\n getContainerWriterProjection(\n containerId: string,\n ): Promise<ContainerWriterProjectionResponse | null>;\n };\n readonly verifyMetadataContainer: (\n state: ContainerAccessManifestState,\n organizationReference?: ReferencedPrincipalHead | undefined,\n ) => Promise<void>;\n readonly execSql: ExecSql;\n readonly organizationId: string;\n readonly resolveProjectionUserKey: ProjectionUserKeyResolver;\n readonly targetSecretKey: Uint8Array;\n readonly stillCurrent?: (() => boolean) | undefined;\n readonly warmReferencedPrincipalPolicies?:\n | ReferencedPrincipalPolicyWarmer\n | undefined;\n}\n\n/**\n * A group's signed name does not open under the metadata key it cites, or opens\n * to an invalid name. Every reader holding that key gets the same result. The\n * API checks the citation but can never check the ciphertext, so a dishonest\n * admin can sign such a name; directory walks isolate the group instead of\n * failing every name in the organization.\n */\nexport class GroupMetadataUnreadableError extends Error {\n constructor(groupId: string, cause: unknown) {\n super(`Group ${groupId} metadata cannot be decrypted`, { cause });\n this.name = \"GroupMetadataUnreadableError\";\n }\n}\n\nfunction loadMetadataKeyring(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n return withMetadataRootReload(\n () => loadMetadataKeyringOnce(input, containerId),\n () => input.apiClient.evictContainerWriterProjection(containerId),\n );\n}\n\nasync function loadMetadataKeyringOnce(\n input: GroupMetadataAccessInput,\n containerId: string,\n) {\n const projection =\n await input.apiClient.getContainerWriterProjection(containerId);\n if (!projection) return null;\n if (\n projection.containerId !== containerId ||\n projection.organizationId !== input.organizationId\n )\n throw new Error(\"Group metadata container is unavailable\");\n const target = getTargetContainerContext(projection);\n const state = readContainerState(target.manifest);\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n if (\n state.organizationId !== input.organizationId ||\n state.systemSlot !== slot ||\n state.parentContainerId !== null\n )\n throw new Error(\n \"Group metadata requires the signed organization metadata container\",\n );\n await input.verifyMetadataContainer(\n state,\n projection.policyEvidence.organization?.head,\n );\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keys = await unwrapContainerKekPath({\n ...input,\n projection,\n secretKey: input.targetSecretKey,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n const keyMaterial = keys.get(target.kek.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata container key is unavailable\");\n return {\n current: {\n organizationId: input.organizationId,\n containerId,\n containerKeyEpochId: target.kek.containerKeyEpochId,\n keyMaterial,\n },\n keys,\n };\n}\n\n/** Uses the shared metadata container's authenticated keyring, including old epochs. */\nexport function createGroupMetadataAccess(input: GroupMetadataAccessInput) {\n const loads = new Map<\n string,\n Promise<{\n current: GroupMetadataKey;\n keys: ReadonlyMap<string, Uint8Array>;\n } | null>\n >();\n function loaded(containerId: string) {\n let pending = loads.get(containerId);\n if (!pending) {\n pending = loadMetadataKeyring(input, containerId);\n loads.set(containerId, pending);\n }\n return pending;\n }\n const readName: GroupPolicyNameReader = async (bundle) => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n if (\"role\" in metadata)\n return metadata.role === \"admins\" ? \"Admins\" : \"Members\";\n if (\n metadata.organizationId !== input.organizationId ||\n metadata.groupId !== bundle.currentState.principalId ||\n bundle.currentState.principalType !== \"group\"\n )\n throw new Error(\"Group metadata scope does not match its signed group\");\n const keyring = await loaded(metadata.containerId);\n const keyMaterial = keyring?.keys.get(metadata.containerKeyEpochId);\n if (!keyMaterial)\n throw new Error(\"Group metadata historical key is unavailable\");\n try {\n return await decryptGroupMetadata({\n key: { ...metadata, keyMaterial },\n groupId: bundle.currentState.principalId,\n payload: bundle.currentPayload.ciphertext,\n });\n } catch (cause) {\n throw new GroupMetadataUnreadableError(\n bundle.currentState.principalId,\n cause,\n );\n }\n };\n return {\n readName,\n async loadEncryptionKey(): Promise<GroupMetadataKey> {\n const slot = await deriveOrganizationMetadataContainerSystemSlot({\n organizationId: input.organizationId,\n });\n const containers = await loadContainers(input.execSql);\n const container = containers.find(\n (row) =>\n row.organizationId === input.organizationId &&\n row.systemSlot === slot,\n );\n if (!container)\n throw new Error(\n \"Organization metadata container has not been discovered\",\n );\n const keyring = await loaded(container.id);\n if (!keyring) throw new Error(\"Group metadata container is unavailable\");\n return keyring.current;\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"groupMetadataBinding.js","sourceRoot":"","sources":["groupMetadataBinding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAItD,gFAAgF;AAChF,MAAM,UAAU,0BAA0B,CACxC,MAAqC,EACrC,UAA2C;IAE3C,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC;IAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACrE,MAAM,YAAY,GAChB,OAAO,KAAK,UAAU,CAAC,YAAY;QACjC,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,aAAa;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC;IACb,IACE,MAAM,IAAI,QAAQ;QAChB,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;QAChC,CAAC,CAAC,YAAY,KAAK,IAAI;YACrB,QAAQ,CAAC,cAAc,KAAK,UAAU,CAAC,cAAc;YACrD,QAAQ,CAAC,OAAO,KAAK,OAAO,EAChC,CAAC;QACD,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { readGroupMetadata } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport type { OrganizationAuthorityDescriptor } from \"../../data/principals/organizationAuthorityDescriptor\";\n\n/** Built-in labels derive from the role assigned by the signed organization. */\nexport function assertGroupMetadataBinding(\n bundle: PrincipalPolicyBundleResponse,\n descriptor: OrganizationAuthorityDescriptor,\n): void {\n const groupId = bundle.currentState.principalId;\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n const expectedRole =\n groupId === descriptor.adminGroupId\n ? \"admins\"\n : groupId === descriptor.memberGroupId\n ? \"members\"\n : null;\n if (\n \"role\" in metadata\n ? metadata.role !== expectedRole\n : expectedRole !== null ||\n metadata.organizationId !== descriptor.organizationId ||\n metadata.groupId !== groupId\n ) {\n throw new Error(\n \"Group metadata does not match the signed organization directory\",\n );\n }\n}\n"]}
1
+ {"version":3,"file":"groupMetadataBinding.js","sourceRoot":"","sources":["groupMetadataBinding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAItD,gFAAgF;AAChF,MAAM,UAAU,0BAA0B,CACxC,MAGC,EACD,UAA2C;IAE3C,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC;IAChD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACrE,MAAM,YAAY,GAChB,OAAO,KAAK,UAAU,CAAC,YAAY;QACjC,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,aAAa;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC;IACb,IACE,MAAM,IAAI,QAAQ;QAChB,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;QAChC,CAAC,CAAC,YAAY,KAAK,IAAI;YACrB,QAAQ,CAAC,cAAc,KAAK,UAAU,CAAC,cAAc;YACrD,QAAQ,CAAC,OAAO,KAAK,OAAO,EAChC,CAAC;QACD,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import { readGroupMetadata } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport type { OrganizationAuthorityDescriptor } from \"../../data/principals/organizationAuthorityDescriptor\";\n\n/** Built-in labels derive from the role assigned by the signed organization. */\nexport function assertGroupMetadataBinding(\n bundle: Pick<\n PrincipalPolicyBundleResponse,\n \"currentState\" | \"currentPayload\"\n >,\n descriptor: OrganizationAuthorityDescriptor,\n): void {\n const groupId = bundle.currentState.principalId;\n const metadata = readGroupMetadata(bundle.currentPayload.ciphertext);\n const expectedRole =\n groupId === descriptor.adminGroupId\n ? \"admins\"\n : groupId === descriptor.memberGroupId\n ? \"members\"\n : null;\n if (\n \"role\" in metadata\n ? metadata.role !== expectedRole\n : expectedRole !== null ||\n metadata.organizationId !== descriptor.organizationId ||\n metadata.groupId !== groupId\n ) {\n throw new Error(\n \"Group metadata does not match the signed organization directory\",\n );\n }\n}\n"]}
@@ -1,9 +1,8 @@
1
- import { KeyingVerificationError, } from "../../internal/crypto/index.js";
2
1
  import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
3
2
  import { loadPrincipalPolicyBundle } from "../../data/persistence/principalPolicyPersistence.js";
4
- import { previousStatesIncludeHead, principalHeadMatchesReference, requireOrganizationGroupHead, } from "../../data/principals/organizationAuthorityDescriptor.js";
3
+ import { principalHeadMatchesReference, requireOrganizationGroupHead, } from "../../data/principals/organizationAuthorityDescriptor.js";
5
4
  import { principalPolicyReferenceFromBundle } from "../../data/principals/principalPolicyAdminSigners.js";
6
- import { MetadataRootBehindDirectoryError } from "./groupMetadataErrors.js";
5
+ import { assertMetadataRootBinding } from "./groupMetadataRootBinding.js";
7
6
  import { verifyDirectoryGroup } from "./groupNameUniqueness.js";
8
7
  import { loadGroupNameDirectoryAuthority } from "./organizationGroupNamePolicies.js";
9
8
  /** A public system slot alone cannot bind a self-authorized root to an organization. */
@@ -39,48 +38,4 @@ export function createGroupMetadataContainerVerifier(input) {
39
38
  assertProjectionVerificationCurrent(input.stillCurrent);
40
39
  };
41
40
  }
42
- function assertMetadataRootBinding(input) {
43
- const { authority, state } = input;
44
- const expected = [
45
- {
46
- bundle: authority.adminBundle,
47
- id: authority.adminGroupId,
48
- level: "admin",
49
- grants: authority.adminPolicy.grants,
50
- },
51
- {
52
- bundle: input.members.bundle,
53
- id: authority.memberGroupId,
54
- level: "read",
55
- grants: input.members.policy.grants,
56
- },
57
- ];
58
- const cited = (groupId) => state.referencedPrincipalHeads.find((head) => head.principalType === "group" && head.principalId === groupId);
59
- const unbound = new KeyingVerificationError("object_mismatch", "Organization metadata root is not bound to the reserved group grants");
60
- if (state.organizationId !== input.organizationId ||
61
- state.parentContainerId !== null ||
62
- state.directGrants.length !== 2 ||
63
- state.referencedPrincipalHeads.length !== 2 ||
64
- expected.some((role) => !role.grants.some((grant) => grant.containerId === state.containerId &&
65
- grant.accessLevel === role.level) ||
66
- !state.directGrants.some((grant) => grant.subjectType === "group" &&
67
- grant.subjectId === role.id &&
68
- grant.accessLevel === role.level) ||
69
- !cited(role.id)))
70
- throw unbound;
71
- const superseded = expected.flatMap((role) => {
72
- const head = cited(role.id);
73
- return head &&
74
- !principalHeadMatchesReference(head, requireOrganizationGroupHead(authority.descriptor, role.id))
75
- ? [{ bundle: role.bundle, head }]
76
- : [];
77
- });
78
- if (superseded.length === 0)
79
- return;
80
- // A root read before a reserved-group commit cites a verified predecessor
81
- // of the directory's head; any other head is not an honest race.
82
- if (superseded.every(({ bundle, head }) => previousStatesIncludeHead(bundle, head)))
83
- throw new MetadataRootBehindDirectoryError();
84
- throw unbound;
85
- }
86
41
  //# sourceMappingURL=groupMetadataContainerAuthority.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"groupMetadataContainerAuthority.js","sourceRoot":"","sources":["groupMetadataContainerAuthority.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type ContainerAccessManifestState,\n KeyingVerificationError,\n} from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadPrincipalPolicyBundle } from \"../../data/persistence/principalPolicyPersistence\";\nimport {\n previousStatesIncludeHead,\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport { MetadataRootBehindDirectoryError } from \"./groupMetadataErrors\";\nimport { verifyDirectoryGroup } from \"./groupNameUniqueness\";\nimport { loadGroupNameDirectoryAuthority } from \"./organizationGroupNamePolicies\";\n\ntype Input = Omit<\n Parameters<typeof loadGroupNameDirectoryAuthority>[0],\n \"organizationPolicyReference\"\n> & { readonly reportSecurityIncident: SecurityIncidentReporter };\n\n/** A public system slot alone cannot bind a self-authorized root to an organization. */\nexport function createGroupMetadataContainerVerifier(input: Input) {\n return async (state: ContainerAccessManifestState): Promise<void> => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const cached = await loadPrincipalPolicyBundle(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n let authority = await loadGroupNameDirectoryAuthority({\n ...input,\n organizationPolicyReference: cached\n ? principalPolicyReferenceFromBundle(cached)\n : null,\n });\n if (\n cached &&\n authority &&\n state.referencedPrincipalHeads.some(\n (head) =>\n !authority?.descriptor.groupHeads.some((known) =>\n principalHeadMatchesReference(head, known),\n ),\n )\n ) {\n authority = await loadGroupNameDirectoryAuthority(input);\n }\n if (!authority)\n throw new Error(\"Organization metadata authority is unavailable\");\n const memberHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.memberGroupId,\n );\n const members = await verifyDirectoryGroup(\n {\n ...input,\n descriptor: authority.descriptor,\n externalAuthority: authority.externalAuthority,\n },\n memberHead,\n );\n assertMetadataRootBinding({\n authority,\n members,\n organizationId: input.organizationId,\n state,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n };\n}\n\ntype DirectoryAuthority = NonNullable<\n Awaited<ReturnType<typeof loadGroupNameDirectoryAuthority>>\n>;\n\nfunction assertMetadataRootBinding(input: {\n readonly authority: DirectoryAuthority;\n readonly members: Awaited<ReturnType<typeof verifyDirectoryGroup>>;\n readonly organizationId: string;\n readonly state: ContainerAccessManifestState;\n}): void {\n const { authority, state } = input;\n const expected = [\n {\n bundle: authority.adminBundle,\n id: authority.adminGroupId,\n level: \"admin\",\n grants: authority.adminPolicy.grants,\n },\n {\n bundle: input.members.bundle,\n id: authority.memberGroupId,\n level: \"read\",\n grants: input.members.policy.grants,\n },\n ];\n const cited = (groupId: string) =>\n state.referencedPrincipalHeads.find(\n (head) => head.principalType === \"group\" && head.principalId === groupId,\n );\n const unbound = new KeyingVerificationError(\n \"object_mismatch\",\n \"Organization metadata root is not bound to the reserved group grants\",\n );\n if (\n state.organizationId !== input.organizationId ||\n state.parentContainerId !== null ||\n state.directGrants.length !== 2 ||\n state.referencedPrincipalHeads.length !== 2 ||\n expected.some(\n (role) =>\n !role.grants.some(\n (grant) =>\n grant.containerId === state.containerId &&\n grant.accessLevel === role.level,\n ) ||\n !state.directGrants.some(\n (grant) =>\n grant.subjectType === \"group\" &&\n grant.subjectId === role.id &&\n grant.accessLevel === role.level,\n ) ||\n !cited(role.id),\n )\n )\n throw unbound;\n const superseded = expected.flatMap((role) => {\n const head = cited(role.id);\n return head &&\n !principalHeadMatchesReference(\n head,\n requireOrganizationGroupHead(authority.descriptor, role.id),\n )\n ? [{ bundle: role.bundle, head }]\n : [];\n });\n if (superseded.length === 0) return;\n // A root read before a reserved-group commit cites a verified predecessor\n // of the directory's head; any other head is not an honest race.\n if (\n superseded.every(({ bundle, head }) =>\n previousStatesIncludeHead(bundle, head),\n )\n )\n throw new MetadataRootBehindDirectoryError();\n throw unbound;\n}\n"]}
1
+ {"version":3,"file":"groupMetadataContainerAuthority.js","sourceRoot":"","sources":["groupMetadataContainerAuthority.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAE,MAAM,mDAAmD,CAAC;AAC9F,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,kCAAkC,EAAE,MAAM,mDAAmD,CAAC;AAEvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAOlF,wFAAwF;AACxF,MAAM,UAAU,oCAAoC,CAAC,KAAY;IAC/D,OAAO,KAAK,EAAE,KAAmC,EAAiB,EAAE;QAClE,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,yBAAyB,CAC5C,KAAK,CAAC,OAAO,EACb,cAAc,EACd,KAAK,CAAC,cAAc,CACrB,CAAC;QACF,IAAI,SAAS,GAAG,MAAM,+BAA+B,CAAC;YACpD,GAAG,KAAK;YACR,2BAA2B,EAAE,MAAM;gBACjC,CAAC,CAAC,kCAAkC,CAAC,MAAM,CAAC;gBAC5C,CAAC,CAAC,IAAI;SACT,CAAC,CAAC;QACH,IACE,MAAM;YACN,SAAS;YACT,KAAK,CAAC,wBAAwB,CAAC,IAAI,CACjC,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,SAAS,EAAE,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAC/C,6BAA6B,CAAC,IAAI,EAAE,KAAK,CAAC,CAC3C,CACJ,EACD,CAAC;YACD,SAAS,GAAG,MAAM,+BAA+B,CAAC,KAAK,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,SAAS;YACZ,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,4BAA4B,CAC7C,SAAS,CAAC,UAAU,EACpB,SAAS,CAAC,aAAa,CACxB,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,oBAAoB,CACxC;YACE,GAAG,KAAK;YACR,UAAU,EAAE,SAAS,CAAC,UAAU;YAChC,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;SAC/C,EACD,UAAU,CACX,CAAC;QACF,yBAAyB,CAAC;YACxB,SAAS;YACT,OAAO;YACP,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,KAAK;SACN,CAAC,CAAC;QACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1D,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ContainerAccessManifestState } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { loadPrincipalPolicyBundle } from \"../../data/persistence/principalPolicyPersistence\";\nimport {\n principalHeadMatchesReference,\n requireOrganizationGroupHead,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport { assertMetadataRootBinding } from \"./groupMetadataRootBinding\";\nimport { verifyDirectoryGroup } from \"./groupNameUniqueness\";\nimport { loadGroupNameDirectoryAuthority } from \"./organizationGroupNamePolicies\";\n\ntype Input = Omit<\n Parameters<typeof loadGroupNameDirectoryAuthority>[0],\n \"organizationPolicyReference\"\n> & { readonly reportSecurityIncident: SecurityIncidentReporter };\n\n/** A public system slot alone cannot bind a self-authorized root to an organization. */\nexport function createGroupMetadataContainerVerifier(input: Input) {\n return async (state: ContainerAccessManifestState): Promise<void> => {\n assertProjectionVerificationCurrent(input.stillCurrent);\n const cached = await loadPrincipalPolicyBundle(\n input.execSql,\n \"organization\",\n input.organizationId,\n );\n let authority = await loadGroupNameDirectoryAuthority({\n ...input,\n organizationPolicyReference: cached\n ? principalPolicyReferenceFromBundle(cached)\n : null,\n });\n if (\n cached &&\n authority &&\n state.referencedPrincipalHeads.some(\n (head) =>\n !authority?.descriptor.groupHeads.some((known) =>\n principalHeadMatchesReference(head, known),\n ),\n )\n ) {\n authority = await loadGroupNameDirectoryAuthority(input);\n }\n if (!authority)\n throw new Error(\"Organization metadata authority is unavailable\");\n const memberHead = requireOrganizationGroupHead(\n authority.descriptor,\n authority.memberGroupId,\n );\n const members = await verifyDirectoryGroup(\n {\n ...input,\n descriptor: authority.descriptor,\n externalAuthority: authority.externalAuthority,\n },\n memberHead,\n );\n assertMetadataRootBinding({\n authority,\n members,\n organizationId: input.organizationId,\n state,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n };\n}\n"]}
@@ -0,0 +1,51 @@
1
+ import { KeyingVerificationError, } from "../../internal/crypto/index.js";
2
+ import { principalHeadMatchesReference, requireOrganizationGroupHead, } from "../../data/principals/organizationAuthorityDescriptor.js";
3
+ import { principalPolicyEvidenceEntries } from "../../data/principals/principalPolicyEvidence.js";
4
+ import { MetadataRootBehindDirectoryError } from "./groupMetadataErrors.js";
5
+ /** Root bindings consume verified current grants and explicitly retained predecessor citations. */
6
+ export function assertMetadataRootBinding(input) {
7
+ const { authority, state } = input;
8
+ const expected = [
9
+ {
10
+ policy: authority.adminPolicy,
11
+ id: authority.adminGroupId,
12
+ level: "admin",
13
+ grants: authority.adminPolicy.grants,
14
+ },
15
+ {
16
+ policy: input.members.policy,
17
+ id: authority.memberGroupId,
18
+ level: "read",
19
+ grants: input.members.policy.grants,
20
+ },
21
+ ];
22
+ const cited = (groupId) => state.referencedPrincipalHeads.find((head) => head.principalType === "group" && head.principalId === groupId);
23
+ const unbound = new KeyingVerificationError("object_mismatch", "Organization metadata root is not bound to the reserved group grants");
24
+ if (state.organizationId !== input.organizationId ||
25
+ state.parentContainerId !== null ||
26
+ state.directGrants.length !== 2 ||
27
+ state.referencedPrincipalHeads.length !== 2 ||
28
+ expected.some((role) => !principalHeadMatchesReference(role.policy.state, requireOrganizationGroupHead(authority.descriptor, role.id)) ||
29
+ !role.grants.some((grant) => grant.containerId === state.containerId &&
30
+ grant.accessLevel === role.level) ||
31
+ !state.directGrants.some((grant) => grant.subjectType === "group" &&
32
+ grant.subjectId === role.id &&
33
+ grant.accessLevel === role.level) ||
34
+ !cited(role.id)))
35
+ throw unbound;
36
+ const superseded = expected.flatMap((role) => {
37
+ const head = cited(role.id);
38
+ return head &&
39
+ !principalHeadMatchesReference(head, requireOrganizationGroupHead(authority.descriptor, role.id))
40
+ ? [{ policy: role.policy, head }]
41
+ : [];
42
+ });
43
+ if (superseded.length === 0)
44
+ return;
45
+ // A root read before a reserved-group commit cites a verified predecessor
46
+ // of the directory's head; any other head is not an honest race.
47
+ if (superseded.every(({ policy, head }) => principalPolicyEvidenceEntries(policy).some(({ state }) => principalHeadMatchesReference(state, head))))
48
+ throw new MetadataRootBehindDirectoryError();
49
+ throw unbound;
50
+ }
51
+ //# sourceMappingURL=groupMetadataRootBinding.js.map