@tearleads/client-sdk 0.1.16 → 0.1.17

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 (128) hide show
  1. package/client/index.d.ts +1 -1
  2. package/client/index.js.map +1 -1
  3. package/client/organizations/currentPrincipalMutations.js +96 -0
  4. package/client/organizations/currentPrincipalMutations.js.map +1 -0
  5. package/client/organizations/index.d.ts +1 -1
  6. package/client/organizations/index.js +4 -3
  7. package/client/organizations/index.js.map +1 -1
  8. package/client/organizations/organizationGroupHistory.js +44 -0
  9. package/client/organizations/organizationGroupHistory.js.map +1 -0
  10. package/client/organizations/organizationGroupNameHydration.js +7 -1
  11. package/client/organizations/organizationGroupNameHydration.js.map +1 -1
  12. package/client/organizations/organizationGroupPresentation.d.ts +1 -0
  13. package/client/organizations/organizationGroupPresentation.js +16 -2
  14. package/client/organizations/organizationGroupPresentation.js.map +1 -1
  15. package/client/organizations/organizationReadModels.d.ts +1 -1
  16. package/client/organizations/organizationReadModels.js +11 -1
  17. package/client/organizations/organizationReadModels.js.map +1 -1
  18. package/client/organizations/organizationsTypes.d.ts +8 -5
  19. package/client/organizations/organizationsTypes.js.map +1 -1
  20. package/client/organizations/principalContainerMutations.js +2 -0
  21. package/client/organizations/principalContainerMutations.js.map +1 -1
  22. package/client/organizations/principalMutations.d.ts +4 -308
  23. package/client/organizations/principalMutations.js +165 -87
  24. package/client/organizations/principalMutations.js.map +1 -1
  25. package/data/persistence/principalCurrentAcknowledgementPersistence.d.ts +2 -2
  26. package/data/persistence/principalCurrentAcknowledgementPersistence.js +21 -14
  27. package/data/persistence/principalCurrentAcknowledgementPersistence.js.map +1 -1
  28. package/data/principals/currentPolicyReferenceResolver.js +2 -0
  29. package/data/principals/currentPolicyReferenceResolver.js.map +1 -0
  30. package/data/principals/principalHistoryScopeProtection.js +23 -0
  31. package/data/principals/principalHistoryScopeProtection.js.map +1 -0
  32. package/data/principals/principalPolicyAdminSigners.js.map +1 -1
  33. package/index.d.ts +1 -1
  34. package/index.js.map +1 -1
  35. package/internal/crypto/index.d.ts +1 -1
  36. package/internal/crypto/index.js +1 -1
  37. package/internal/crypto/index.js.map +1 -1
  38. package/internal/crypto/keying/index.d.ts +1 -0
  39. package/internal/crypto/keying/index.js +1 -0
  40. package/internal/crypto/keying/index.js.map +1 -1
  41. package/internal/crypto/keying/principalPolicyCurrentPredecessorReferences.d.ts +8 -0
  42. package/internal/crypto/keying/principalPolicyCurrentPredecessorReferences.js +46 -0
  43. package/internal/crypto/keying/principalPolicyCurrentPredecessorReferences.js.map +1 -0
  44. package/package.json +1 -1
  45. package/workflows/containers/child/carriedDescendantRekeys.js +9 -1
  46. package/workflows/containers/child/carriedDescendantRekeys.js.map +1 -1
  47. package/workflows/containers/child/replacementPrincipalPolicyReferences.js +19 -0
  48. package/workflows/containers/child/replacementPrincipalPolicyReferences.js.map +1 -0
  49. package/workflows/organizations/commitCurrentGroupPolicyMutation.js +72 -0
  50. package/workflows/organizations/commitCurrentGroupPolicyMutation.js.map +1 -0
  51. package/workflows/organizations/createCurrentOrganizationGroup.js +85 -0
  52. package/workflows/organizations/createCurrentOrganizationGroup.js.map +1 -0
  53. package/workflows/organizations/currentGroupMetadataAuthority.js +24 -3
  54. package/workflows/organizations/currentGroupMetadataAuthority.js.map +1 -1
  55. package/workflows/organizations/currentGroupMutationReferences.js +35 -0
  56. package/workflows/organizations/currentGroupMutationReferences.js.map +1 -0
  57. package/workflows/organizations/currentGroupPolicyMutationContext.js +79 -0
  58. package/workflows/organizations/currentGroupPolicyMutationContext.js.map +1 -0
  59. package/workflows/organizations/currentOrganizationGroupNames.js +5 -1
  60. package/workflows/organizations/currentOrganizationGroupNames.js.map +1 -1
  61. package/workflows/organizations/currentOrganizationMutationAuthority.js +16 -0
  62. package/workflows/organizations/currentOrganizationMutationAuthority.js.map +1 -0
  63. package/workflows/organizations/currentOrganizationMutationRetention.js +53 -0
  64. package/workflows/organizations/currentOrganizationMutationRetention.js.map +1 -0
  65. package/workflows/organizations/currentPrincipalMutationLease.js +38 -0
  66. package/workflows/organizations/currentPrincipalMutationLease.js.map +1 -0
  67. package/workflows/organizations/deleteCurrentOrganizationGroup.js +35 -0
  68. package/workflows/organizations/deleteCurrentOrganizationGroup.js.map +1 -0
  69. package/workflows/organizations/deleteOrganizationGroup.d.ts +1 -0
  70. package/workflows/organizations/deleteOrganizationGroup.js +10 -0
  71. package/workflows/organizations/deleteOrganizationGroup.js.map +1 -1
  72. package/workflows/organizations/groupCreation.d.ts +1 -0
  73. package/workflows/organizations/groupCreation.js +8 -0
  74. package/workflows/organizations/groupCreation.js.map +1 -1
  75. package/workflows/organizations/groupMembershipName.d.ts +1 -1
  76. package/workflows/organizations/groupMembershipName.js.map +1 -1
  77. package/workflows/organizations/groupMetadataRuntime.js +14 -9
  78. package/workflows/organizations/groupMetadataRuntime.js.map +1 -1
  79. package/workflows/organizations/groupNameUniqueness.js +1 -1
  80. package/workflows/organizations/groupNameUniqueness.js.map +1 -1
  81. package/workflows/organizations/groupPolicyHistoryPage.js +31 -0
  82. package/workflows/organizations/groupPolicyHistoryPage.js.map +1 -0
  83. package/workflows/organizations/groupPolicyMutationAcknowledgement.js +1 -0
  84. package/workflows/organizations/groupPolicyMutationAcknowledgement.js.map +1 -1
  85. package/workflows/organizations/groupPolicyMutationContext.d.ts +18 -0
  86. package/workflows/organizations/groupPolicyMutationContext.js +1 -1
  87. package/workflows/organizations/groupPolicyMutationContext.js.map +1 -1
  88. package/workflows/organizations/initialPrincipalCurrentPublication.js +83 -0
  89. package/workflows/organizations/initialPrincipalCurrentPublication.js.map +1 -0
  90. package/workflows/organizations/organizationGroupDirectory.js +6 -0
  91. package/workflows/organizations/organizationGroupDirectory.js.map +1 -1
  92. package/workflows/organizations/organizationGroupNames.js.map +1 -1
  93. package/workflows/organizations/policyHistoryTypes.d.ts +2 -0
  94. package/workflows/organizations/policyHistoryTypes.js.map +1 -1
  95. package/workflows/organizations/principalContainerRematerialization.js +12 -2
  96. package/workflows/organizations/principalContainerRematerialization.js.map +1 -1
  97. package/workflows/organizations/principalContainerRematerializationPlanning.js +1 -0
  98. package/workflows/organizations/principalContainerRematerializationPlanning.js.map +1 -1
  99. package/workflows/organizations/principalContainerRematerializationTargets.js.map +1 -1
  100. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.d.ts +2 -0
  101. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js +5 -1
  102. package/workflows/organizations/retainAcknowledgedPrincipalCurrents.js.map +1 -1
  103. package/workflows/organizations/runtimeCurrentGroupMutation.js +85 -0
  104. package/workflows/organizations/runtimeCurrentGroupMutation.js.map +1 -0
  105. package/workflows/organizations/runtimeCurrentOrganizationMutation.js +48 -0
  106. package/workflows/organizations/runtimeCurrentOrganizationMutation.js.map +1 -0
  107. package/workflows/principals/loadRecoveredPrincipalHistoryPage.d.ts +11 -0
  108. package/workflows/principals/loadRecoveredPrincipalHistoryPage.js +120 -0
  109. package/workflows/principals/loadRecoveredPrincipalHistoryPage.js.map +1 -0
  110. package/workflows/principals/principalHistoryRecoveryReferences.js +8 -0
  111. package/workflows/principals/principalHistoryRecoveryReferences.js.map +1 -1
  112. package/workflows/principals/principalRecoveryDirectory.js +3 -8
  113. package/workflows/principals/principalRecoveryDirectory.js.map +1 -1
  114. package/workflows/principals/principalRecoveryLocalPreference.js +3 -2
  115. package/workflows/principals/principalRecoveryLocalPreference.js.map +1 -1
  116. package/workflows/principals/recoverCurrentOrganizationPolicy.js.map +1 -1
  117. package/workflows/principals/recoverPrincipalHistoryPage.js +30 -0
  118. package/workflows/principals/recoverPrincipalHistoryPage.js.map +1 -0
  119. package/workflows/principals/recoverPrincipalPolicyHistory.d.ts +2 -0
  120. package/workflows/principals/recoverPrincipalPolicyHistory.js +14 -2
  121. package/workflows/principals/recoverPrincipalPolicyHistory.js.map +1 -1
  122. package/workflows/principals/recoverScopedPrincipalPolicyHistory.d.ts +6 -0
  123. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js +31 -19
  124. package/workflows/principals/recoverScopedPrincipalPolicyHistory.js.map +1 -1
  125. package/workflows/principals/runtimePolicyRecovery.js +25 -4
  126. package/workflows/principals/runtimePolicyRecovery.js.map +1 -1
  127. package/workflows/principals/runtimePolicyWarmer.js +2 -2
  128. package/workflows/principals/runtimePolicyWarmer.js.map +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currentPrincipalMutationLease.js","sourceRoot":"","sources":["currentPrincipalMutationLease.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,8BAA8B,EAAE,MAAM,+CAA+C,CAAC;AAC/F,OAAO,EAAE,0BAA0B,EAAE,MAAM,uDAAuD,CAAC;AACnG,OAAO,EAAE,kCAAkC,EAAE,MAAM,mDAAmD,CAAC;AACvG,OAAO,EACL,2CAA2C,GAE5C,MAAM,qCAAqC,CAAC;AAsB7C,yFAAyF;AACzF,MAAM,UAAU,mCAAmC,CACjD,OAAuC;IAEvC,MAAM,KAAK,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,oBAAoB,GACxB,2CAA2C,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,uBAAuB,EAAE,IAAI,CAC/D,OAAO,CAAC,SAAS,CAClB,CAAC;IACF,IAAI,CAAC,KAAK,IAAI,CAAC,oBAAoB,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACpE,OAAO,KAAK,EACV,aAA4B,EAC5B,IAAmE,EACvD,EAAE,CACd,KAAK,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,EAAE;QACzD,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,MAAM,IAAI,YAAY,EAAE,IAAI,aAAa,EAAE,CAAC;QACvE,IAAI,CAAC;YACH,mCAAmC,CAAC,YAAY,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC;gBACxB,UAAU;gBACV,YAAY;gBACZ,oBAAoB;gBACpB,SAAS;gBACT,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;oBAC/B,SAAS,EAAE,EAAE,uBAAuB,EAAE,SAAS,EAAE;oBACjD,YAAY,EAAE,kCAAkC,CAAC,OAAO,CAAC;oBACzD,UAAU,EAAE,0BAA0B,CAAC,UAAU,CAAC;oBAClD,0BAA0B,EAAE,OAAO,CAAC,0BAA0B;iBAC/D,CAAC;aACH,CAAC,CAAC;YACH,mCAAmC,CAAC,YAAY,CAAC,CAAC;YAClD,OAAO,MAAM,CAAC;QAChB,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import type { PrincipalPolicyPageCurrent } from \"@tearleads/api-client\";\nimport type { PrincipalPolicyHistoryProgressOptions } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { readPrincipalHistoryProtection } from \"../../data/principals/principalHistoryRuntime\";\nimport { directoryHistoryProtection } from \"../../data/principals/principalHistoryScopeProtection\";\nimport { principalPolicyReferenceFromBundle } from \"../../data/principals/principalPolicyAdminSigners\";\nimport {\n createRuntimePrincipalPolicyCurrentResolver,\n type PrincipalPolicyRecoveryRuntime,\n} from \"../principals/runtimePolicyRecovery\";\nimport type { AcknowledgedPrincipalCurrentInput } from \"./retainAcknowledgedPrincipalCurrents\";\n\ninterface CurrentPrincipalMutationLeaseContext {\n readonly protection: PrincipalPolicyHistoryProgressOptions;\n readonly stillCurrent: () => boolean;\n readonly resolveCurrentPolicy: NonNullable<\n ReturnType<typeof createRuntimePrincipalPolicyCurrentResolver>\n >;\n readonly readPages: NonNullable<\n PrincipalPolicyRecoveryRuntime[\"apiClient\"][\"getPrincipalPolicyPages\"]\n >;\n readonly directoryRecovery: (\n current: PrincipalPolicyPageCurrent,\n ) => AcknowledgedPrincipalCurrentInput[\"recovery\"];\n}\n\ntype CurrentPrincipalMutationLease = <T>(\n callerCurrent: () => boolean,\n work: (context: CurrentPrincipalMutationLeaseContext) => Promise<T>,\n) => Promise<T>;\n\n/** One lifetime fence and private custody boundary for every current-policy mutation. */\nexport function createCurrentPrincipalMutationLease(\n runtime: PrincipalPolicyRecoveryRuntime,\n): CurrentPrincipalMutationLease | undefined {\n const lease = readPrincipalHistoryProtection(runtime);\n const resolveCurrentPolicy =\n createRuntimePrincipalPolicyCurrentResolver(runtime);\n const readPages = runtime.apiClient.getPrincipalPolicyPages?.bind(\n runtime.apiClient,\n );\n if (!lease || !resolveCurrentPolicy || !readPages) return undefined;\n return async <T>(\n callerCurrent: () => boolean,\n work: (context: CurrentPrincipalMutationLeaseContext) => Promise<T>,\n ): Promise<T> =>\n lease(async ({ protection, stillCurrent: leaseCurrent }) => {\n let active = true;\n const stillCurrent = () => active && leaseCurrent() && callerCurrent();\n try {\n assertProjectionVerificationCurrent(stillCurrent);\n const result = await work({\n protection,\n stillCurrent,\n resolveCurrentPolicy,\n readPages,\n directoryRecovery: (current) => ({\n apiClient: { getPrincipalPolicyPages: readPages },\n expectedHead: principalPolicyReferenceFromBundle(current),\n protection: directoryHistoryProtection(protection),\n resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,\n }),\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return result;\n } finally {\n active = false;\n }\n });\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
2
+ import { buildOrganizationGroupDirectoryPolicyRequest, removeOrganizationGroupHead, } from "./organizationGroupDirectory.js";
3
+ import { projectionUserIds, resolveRequiredUserIdentities, } from "./trustedOrganizationUsers.js";
4
+ /** Delete against a verified directory without loading the removed group's history. */
5
+ export async function deleteCurrentOrganizationGroup(input) {
6
+ const { context } = input;
7
+ assertProjectionVerificationCurrent(context.stillCurrent);
8
+ const adminProjection = context.admins.policy.projection;
9
+ const request = await buildOrganizationGroupDirectoryPolicyRequest({
10
+ ...input,
11
+ adminProjection,
12
+ adminUsers: await resolveRequiredUserIdentities({
13
+ resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
14
+ userIds: projectionUserIds(adminProjection),
15
+ }),
16
+ currentPolicy: context.directory.current,
17
+ descriptor: context.descriptor,
18
+ groupHeads: removeOrganizationGroupHead({
19
+ descriptor: context.descriptor,
20
+ groupId: input.groupId,
21
+ }),
22
+ });
23
+ assertProjectionVerificationCurrent(context.stillCurrent);
24
+ const stored = await input.apiClient.deleteOrganizationGroup(input.organizationId, input.groupId, { organizationPolicy: request });
25
+ assertProjectionVerificationCurrent(context.stillCurrent);
26
+ if (!stored)
27
+ throw new Error("Group could not be deleted");
28
+ if (stored.organizationId !== input.organizationId ||
29
+ stored.groupId !== input.groupId)
30
+ throw new Error("Group deletion response target mismatch");
31
+ await context.retainDirectory(request, stored.organizationPolicy);
32
+ assertProjectionVerificationCurrent(context.stillCurrent);
33
+ return stored;
34
+ }
35
+ //# sourceMappingURL=deleteCurrentOrganizationGroup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteCurrentOrganizationGroup.js","sourceRoot":"","sources":["deleteCurrentOrganizationGroup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AAGpG,OAAO,EACL,4CAA4C,EAC5C,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,iBAAiB,EACjB,6BAA6B,GAC9B,MAAM,4BAA4B,CAAC;AAEpC,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,KAYpD;IACC,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1B,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;IACzD,MAAM,OAAO,GAAG,MAAM,4CAA4C,CAAC;QACjE,GAAG,KAAK;QACR,eAAe;QACf,UAAU,EAAE,MAAM,6BAA6B,CAAC;YAC9C,0BAA0B,EAAE,KAAK,CAAC,0BAA0B;YAC5D,OAAO,EAAE,iBAAiB,CAAC,eAAe,CAAC;SAC5C,CAAC;QACF,aAAa,EAAE,OAAO,CAAC,SAAS,CAAC,OAAO;QACxC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,UAAU,EAAE,2BAA2B,CAAC;YACtC,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;KACH,CAAC,CAAC;IACH,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,uBAAuB,CAC1D,KAAK,CAAC,cAAc,EACpB,KAAK,CAAC,OAAO,EACb,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAChC,CAAC;IACF,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC3D,IACE,MAAM,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QAC9C,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;QAEhC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAClE,mCAAmC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { SigningKeyPair } from \"@tearleads/crypto\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport type { deleteOrganizationGroup } from \"./deleteOrganizationGroup\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n removeOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport type { CurrentOrganizationMutationContext } from \"./runtimeCurrentOrganizationMutation\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\n/** Delete against a verified directory without loading the removed group's history. */\nexport async function deleteCurrentOrganizationGroup(input: {\n readonly apiClient: Pick<\n Parameters<typeof deleteOrganizationGroup>[0][\"apiClient\"],\n \"deleteOrganizationGroup\"\n >;\n readonly context: CurrentOrganizationMutationContext;\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}) {\n const { context } = input;\n assertProjectionVerificationCurrent(context.stillCurrent);\n const adminProjection = context.admins.policy.projection;\n const request = await buildOrganizationGroupDirectoryPolicyRequest({\n ...input,\n adminProjection,\n adminUsers: await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n }),\n currentPolicy: context.directory.current,\n descriptor: context.descriptor,\n groupHeads: removeOrganizationGroupHead({\n descriptor: context.descriptor,\n groupId: input.groupId,\n }),\n });\n assertProjectionVerificationCurrent(context.stillCurrent);\n const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: request },\n );\n assertProjectionVerificationCurrent(context.stillCurrent);\n if (!stored) throw new Error(\"Group could not be deleted\");\n if (\n stored.organizationId !== input.organizationId ||\n stored.groupId !== input.groupId\n )\n throw new Error(\"Group deletion response target mismatch\");\n await context.retainDirectory(request, stored.organizationPolicy);\n assertProjectionVerificationCurrent(context.stillCurrent);\n return stored;\n}\n"]}
@@ -15,4 +15,5 @@ export declare function deleteOrganizationGroup(input: {
15
15
  readonly signerUserId: string;
16
16
  readonly signingFingerprint: string;
17
17
  readonly signingKeyPair: SigningKeyPair;
18
+ readonly stillCurrent?: (() => boolean) | undefined;
18
19
  }): Promise<DeleteOrganizationGroupResponse>;
@@ -1,3 +1,4 @@
1
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
1
2
  import { persistLocallyAcknowledgedPrincipalPolicyBundles } from "../../data/persistence/locallyAcknowledgedCheckpointPersistence.js";
2
3
  import { loadOrganizationExternalAdminPolicy } from "../principals/externalAdminPolicy.js";
3
4
  import { acknowledgeGroupPolicyState, buildAcknowledgedGroupPolicyBundle, } from "./groupPolicyMutationAcknowledgement.js";
@@ -5,13 +6,18 @@ import { groupPolicyMutationHead } from "./groupPolicyMutationHead.js";
5
6
  import { buildOrganizationGroupDirectoryPolicyRequest, removeOrganizationGroupHead, } from "./organizationGroupDirectory.js";
6
7
  import { projectionUserIds, resolveRequiredUserIdentities, } from "./trustedOrganizationUsers.js";
7
8
  export async function deleteOrganizationGroup(input) {
9
+ const stillCurrent = input.stillCurrent;
10
+ assertProjectionVerificationCurrent(stillCurrent);
8
11
  await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
12
+ assertProjectionVerificationCurrent(stillCurrent);
9
13
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
10
14
  execSql: input.execSql,
11
15
  getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
12
16
  organizationId: input.organizationId,
13
17
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
18
+ stillCurrent,
14
19
  });
20
+ assertProjectionVerificationCurrent(stillCurrent);
15
21
  if (!externalAdminPolicy?.signerUserIds.includes(input.signerUserId)) {
16
22
  throw new Error("Organization admin authority could not be verified");
17
23
  }
@@ -33,7 +39,9 @@ export async function deleteOrganizationGroup(input) {
33
39
  signingFingerprint: input.signingFingerprint,
34
40
  signingKeyPair: input.signingKeyPair,
35
41
  });
42
+ assertProjectionVerificationCurrent(stillCurrent);
36
43
  const stored = await input.apiClient.deleteOrganizationGroup(input.organizationId, input.groupId, { organizationPolicy: organizationRequest });
44
+ assertProjectionVerificationCurrent(stillCurrent);
37
45
  if (!stored) {
38
46
  throw new Error("Group could not be deleted");
39
47
  }
@@ -59,7 +67,9 @@ export async function deleteOrganizationGroup(input) {
59
67
  execSql: input.execSql,
60
68
  organizationId: input.organizationId,
61
69
  updatedAt: new Date().toISOString(),
70
+ stillCurrent,
62
71
  });
72
+ assertProjectionVerificationCurrent(stillCurrent);
63
73
  return stored;
64
74
  }
65
75
  //# sourceMappingURL=deleteOrganizationGroup.js.map
@@ -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 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"]}
1
+ {"version":3,"file":"deleteOrganizationGroup.js","sourceRoot":"","sources":["deleteOrganizationGroup.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SigningKeyPair } from \"@tearleads/crypto\";\nimport type { DeleteOrganizationGroupRequest } from \"@tearleads/validators/request\";\nimport type { DeleteOrganizationGroupResponse } from \"@tearleads/validators/response\";\nimport { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport { persistLocallyAcknowledgedPrincipalPolicyBundles } from \"../../data/persistence/locallyAcknowledgedCheckpointPersistence\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport {\n acknowledgeGroupPolicyState,\n buildAcknowledgedGroupPolicyBundle,\n} from \"./groupPolicyMutationAcknowledgement\";\nimport type { PrincipalPolicyReadApi } from \"./groupPolicyMutationContext\";\nimport { groupPolicyMutationHead } from \"./groupPolicyMutationHead\";\nimport {\n buildOrganizationGroupDirectoryPolicyRequest,\n removeOrganizationGroupHead,\n} from \"./organizationGroupDirectory\";\nimport {\n projectionUserIds,\n resolveRequiredUserIdentities,\n} from \"./trustedOrganizationUsers\";\n\nexport async function deleteOrganizationGroup(input: {\n readonly apiClient: PrincipalPolicyReadApi & {\n deleteOrganizationGroup: (\n organizationId: string,\n groupId: string,\n request: DeleteOrganizationGroupRequest,\n ) => Promise<DeleteOrganizationGroupResponse | null>;\n };\n readonly execSql: ExecSql;\n readonly groupId: string;\n readonly organizationId: string;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<DeleteOrganizationGroupResponse> {\n const stillCurrent = input.stillCurrent;\n assertProjectionVerificationCurrent(stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(stillCurrent);\n const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n if (!externalAdminPolicy?.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const adminProjection = externalAdminPolicy.adminBundle.currentProjection;\n const adminUsers = await resolveRequiredUserIdentities({\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n userIds: projectionUserIds(adminProjection),\n });\n const organizationRequest =\n await buildOrganizationGroupDirectoryPolicyRequest({\n adminProjection,\n adminUsers,\n currentPolicy: externalAdminPolicy.bundle,\n descriptor: externalAdminPolicy.descriptor,\n groupHeads: removeOrganizationGroupHead({\n descriptor: externalAdminPolicy.descriptor,\n groupId: input.groupId,\n }),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n const stored = await input.apiClient.deleteOrganizationGroup(\n input.organizationId,\n input.groupId,\n { organizationPolicy: organizationRequest },\n );\n assertProjectionVerificationCurrent(stillCurrent);\n if (!stored) {\n throw new Error(\"Group could not be deleted\");\n }\n if (\n stored.organizationId !== input.organizationId ||\n stored.groupId !== input.groupId\n ) {\n throw new Error(\"Group deletion response target mismatch\");\n }\n const organizationHead = await groupPolicyMutationHead(organizationRequest);\n const organizationPolicy = await acknowledgeGroupPolicyState({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy.currentState,\n });\n const organizationBundle = buildAcknowledgedGroupPolicyBundle({\n currentPolicy: externalAdminPolicy.bundle,\n expectedHead: organizationHead,\n request: organizationRequest,\n response: stored.organizationPolicy,\n });\n await persistLocallyAcknowledgedPrincipalPolicyBundles({\n entries: [{ bundle: organizationBundle, policy: organizationPolicy }],\n execSql: input.execSql,\n organizationId: input.organizationId,\n updatedAt: new Date().toISOString(),\n stillCurrent,\n });\n assertProjectionVerificationCurrent(stillCurrent);\n return stored;\n}\n"]}
@@ -17,4 +17,5 @@ export declare function createOrganizationGroup(input: {
17
17
  readonly signerUserId: string;
18
18
  readonly signingFingerprint: string;
19
19
  readonly signingKeyPair: SigningKeyPair;
20
+ readonly stillCurrent?: (() => boolean) | undefined;
20
21
  }): Promise<OrganizationGroupSummary>;
@@ -1,15 +1,20 @@
1
+ import { assertProjectionVerificationCurrent } from "../../data/keyingProjectionVerification/types.js";
1
2
  import { loadOrganizationExternalAdminPolicy } from "../principals/externalAdminPolicy.js";
2
3
  import { cacheGroupPolicy, } from "./groupPolicyMutationContext.js";
3
4
  import { commitCreatedGroupToDirectory } from "./organizationGroupDirectory.js";
4
5
  import { buildInitialGroupPolicyRequest } from "./principalPolicyRequest.js";
5
6
  export async function createOrganizationGroup(input) {
7
+ assertProjectionVerificationCurrent(input.stillCurrent);
6
8
  await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);
9
+ assertProjectionVerificationCurrent(input.stillCurrent);
7
10
  const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({
8
11
  execSql: input.execSql,
9
12
  getCurrentPrincipalPolicy: (principalType, principalId) => input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),
10
13
  organizationId: input.organizationId,
11
14
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
15
+ stillCurrent: input.stillCurrent,
12
16
  });
17
+ assertProjectionVerificationCurrent(input.stillCurrent);
13
18
  if (!externalAdminPolicy) {
14
19
  throw new Error("Organization admin authority could not be verified");
15
20
  }
@@ -33,6 +38,7 @@ export async function createOrganizationGroup(input) {
33
38
  readEncryptedName: input.metadataAccess.readName,
34
39
  request,
35
40
  });
41
+ assertProjectionVerificationCurrent(input.stillCurrent);
36
42
  await cacheGroupPolicy({
37
43
  acknowledgedMemberEnvelopes: {
38
44
  envelopes: request.initialGroupPolicy.memberEnvelopes,
@@ -54,7 +60,9 @@ export async function createOrganizationGroup(input) {
54
60
  },
55
61
  organizationId: input.organizationId,
56
62
  resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,
63
+ stillCurrent: input.stillCurrent,
57
64
  });
65
+ assertProjectionVerificationCurrent(input.stillCurrent);
58
66
  return { ...group, name: input.name.trim(), nameUnreadable: false };
59
67
  }
60
68
  //# sourceMappingURL=groupCreation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"groupCreation.js","sourceRoot":"","sources":["groupCreation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EncapsulationKeyPair, SigningKeyPair } from \"@tearleads/crypto\";\nimport 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
+ {"version":3,"file":"groupCreation.js","sourceRoot":"","sources":["groupCreation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mCAAmC,EAAE,MAAM,+CAA+C,CAAC;AAIpG,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,KAa7C;IACC,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,KAAK,CAAC,SAAS,CAAC,+BAA+B,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9E,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,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;QAC5D,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC,CAAC;IACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,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;IACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,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;QAC5D,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC,CAAC;IACH,mCAAmC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACxD,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 { assertProjectionVerificationCurrent } from \"../../data/keyingProjectionVerification/types\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { loadOrganizationExternalAdminPolicy } from \"../principals/externalAdminPolicy\";\nimport type { createGroupMetadataAccess } from \"./groupMetadataAccess\";\nimport {\n cacheGroupPolicy,\n type OrganizationPrincipalPolicyApi,\n} from \"./groupPolicyMutationContext\";\nimport { commitCreatedGroupToDirectory } from \"./organizationGroupDirectory\";\nimport { buildInitialGroupPolicyRequest } from \"./principalPolicyRequest\";\nimport type { OrganizationGroupSummary } from \"./readModel\";\n\nexport async function createOrganizationGroup(input: {\n readonly metadataAccess: ReturnType<typeof createGroupMetadataAccess>;\n readonly apiClient: OrganizationPrincipalPolicyApi;\n readonly creatorEncapsulationKeyPair: EncapsulationKeyPair;\n readonly execSql: ExecSql;\n readonly name: string;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly signerUserId: string;\n readonly signingFingerprint: string;\n readonly signingKeyPair: SigningKeyPair;\n readonly stillCurrent?: (() => boolean) | undefined;\n}): Promise<OrganizationGroupSummary> {\n assertProjectionVerificationCurrent(input.stillCurrent);\n await input.apiClient.recoverPendingPrincipalMutation?.(input.organizationId);\n assertProjectionVerificationCurrent(input.stillCurrent);\n const externalAdminPolicy = await loadOrganizationExternalAdminPolicy({\n execSql: input.execSql,\n getCurrentPrincipalPolicy: (principalType, principalId) =>\n input.apiClient.getCurrentPrincipalPolicy(principalType, principalId),\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n if (!externalAdminPolicy) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n if (!externalAdminPolicy.signerUserIds.includes(input.signerUserId)) {\n throw new Error(\"Organization admin authority could not be verified\");\n }\n const request = await buildInitialGroupPolicyRequest({\n creatorEncapsulationKeyPair: input.creatorEncapsulationKeyPair,\n externalAuthority: externalAdminPolicy.externalAuthority.currentHead,\n groupId: crypto.randomUUID(),\n includeSignerAsAdmin: false,\n name: input.name,\n metadataKey: await input.metadataAccess.loadEncryptionKey(),\n signerUserId: input.signerUserId,\n signingFingerprint: input.signingFingerprint,\n signingKeyPair: input.signingKeyPair,\n });\n const { group, head: expectedHead } = await commitCreatedGroupToDirectory({\n ...input,\n externalAdminPolicy,\n readEncryptedName: input.metadataAccess.readName,\n request,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n await cacheGroupPolicy({\n acknowledgedMemberEnvelopes: {\n envelopes: request.initialGroupPolicy.memberEnvelopes,\n epoch: request.initialGroupPolicy.state.keyEpoch,\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n },\n apiClient: input.apiClient,\n execSql: input.execSql,\n expectedCurrentHead: expectedHead,\n externalAuthority: externalAdminPolicy.externalAuthority,\n groupId: group.groupId,\n localPolicyCheckpoint: {\n principalId: request.groupId,\n principalType: \"group\",\n stateHash: expectedHead.stateHash,\n version: expectedHead.version,\n },\n organizationId: input.organizationId,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(input.stillCurrent);\n return { ...group, name: input.name.trim(), nameUnreadable: false };\n}\n"]}
@@ -5,4 +5,4 @@ export declare class GroupMembershipNameMismatchError extends KeyingVerification
5
5
  constructor(reason: "forbidden_characters" | "name_mismatch");
6
6
  }
7
7
  /** Check the selected label only after verifying the signed policy bundle. */
8
- export declare function assertGroupMembershipName(verifiedBundle: PrincipalPolicyBundleResponse, expectedGroupName: string, readEncryptedName?: GroupPolicyNameReader): Promise<void>;
8
+ export declare function assertGroupMembershipName(verifiedBundle: Pick<PrincipalPolicyBundleResponse, "currentState" | "currentPayload">, expectedGroupName: string, readEncryptedName?: GroupPolicyNameReader): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"groupMembershipName.js","sourceRoot":"","sources":["groupMembershipName.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAEL,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,OAAO,gCAAiC,SAAQ,uBAAuB;IAC3E,YAAY,MAAgD;QAC1D,KAAK,CACH,iBAAiB,EACjB,MAAM,KAAK,sBAAsB;YAC/B,CAAC,CAAC,uEAAuE;YACzE,CAAC,CAAC,8DAA8D,CACnE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kCAAkC,CAAC;IACjD,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,cAA6C,EAC7C,iBAAyB,EACzB,iBAAyC;IAEzC,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAC5C,cAAc,EACd,iBAAiB,EACjB,iBAAiB,CAClB,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,gCAAgC,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"./principalPolicyRequest\";\n\nexport class GroupMembershipNameMismatchError extends KeyingVerificationError {\n constructor(reason: \"forbidden_characters\" | \"name_mismatch\") {\n super(\n \"object_mismatch\",\n reason === \"forbidden_characters\"\n ? \"Group membership name contains forbidden control or format characters\"\n : \"Group membership name does not match the signed group policy\",\n );\n this.name = \"GroupMembershipNameMismatchError\";\n }\n}\n\n/** Check the selected label only after verifying the signed policy bundle. */\nexport async function assertGroupMembershipName(\n verifiedBundle: PrincipalPolicyBundleResponse,\n expectedGroupName: string,\n readEncryptedName?: GroupPolicyNameReader,\n): Promise<void> {\n const mismatch = await groupPolicyNameMismatch(\n verifiedBundle,\n expectedGroupName,\n readEncryptedName,\n );\n if (mismatch) {\n throw new GroupMembershipNameMismatchError(mismatch);\n }\n}\n"]}
1
+ {"version":3,"file":"groupMembershipName.js","sourceRoot":"","sources":["groupMembershipName.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAEL,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAElC,MAAM,OAAO,gCAAiC,SAAQ,uBAAuB;IAC3E,YAAY,MAAgD;QAC1D,KAAK,CACH,iBAAiB,EACjB,MAAM,KAAK,sBAAsB;YAC/B,CAAC,CAAC,uEAAuE;YACzE,CAAC,CAAC,8DAA8D,CACnE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kCAAkC,CAAC;IACjD,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,cAGC,EACD,iBAAyB,EACzB,iBAAyC;IAEzC,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAC5C,cAAc,EACd,iBAAiB,EACjB,iBAAiB,CAClB,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,gCAAgC,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC;AACH,CAAC","sourcesContent":["import { KeyingVerificationError } from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport {\n type GroupPolicyNameReader,\n groupPolicyNameMismatch,\n} from \"./principalPolicyRequest\";\n\nexport class GroupMembershipNameMismatchError extends KeyingVerificationError {\n constructor(reason: \"forbidden_characters\" | \"name_mismatch\") {\n super(\n \"object_mismatch\",\n reason === \"forbidden_characters\"\n ? \"Group membership name contains forbidden control or format characters\"\n : \"Group membership name does not match the signed group policy\",\n );\n this.name = \"GroupMembershipNameMismatchError\";\n }\n}\n\n/** Check the selected label only after verifying the signed policy bundle. */\nexport async function assertGroupMembershipName(\n verifiedBundle: Pick<\n PrincipalPolicyBundleResponse,\n \"currentState\" | \"currentPayload\"\n >,\n expectedGroupName: string,\n readEncryptedName?: GroupPolicyNameReader,\n): Promise<void> {\n const mismatch = await groupPolicyNameMismatch(\n verifiedBundle,\n expectedGroupName,\n readEncryptedName,\n );\n if (mismatch) {\n throw new GroupMembershipNameMismatchError(mismatch);\n }\n}\n"]}
@@ -4,11 +4,15 @@ import { createRuntimePrincipalPolicyWarmer } from "../principals/runtimePolicyW
4
4
  import { createCurrentGroupMetadataContainerVerifier } from "./currentGroupMetadataAuthority.js";
5
5
  import { createGroupMetadataAccess, } from "./groupMetadataAccess.js";
6
6
  import { createGroupMetadataContainerVerifier } from "./groupMetadataContainerAuthority.js";
7
- export function createRuntimeGroupMetadataAccess(runtime, organizationId, stillCurrent, currentRecovery) {
7
+ export function createRuntimeGroupMetadataAccess(runtime, organizationId, stillCurrent, verifyMetadataContainer, currentRecovery) {
8
8
  const targetSecretKey = runtime.crypto.encapsulationKeyPair?.secretKey;
9
9
  if (!targetSecretKey)
10
10
  throw new Error("Group metadata identity is locked");
11
- const warmer = createRuntimePrincipalPolicyWarmer(runtime);
11
+ // The signed metadata projection selects exact citations. Reuse matching
12
+ // private evidence with its dependencies and pins before bounded online reads.
13
+ const warmer = createRuntimePrincipalPolicyWarmer(runtime, {
14
+ preferLocalCurrent: true,
15
+ });
12
16
  const resolveCurrentPolicy = currentRecovery?.resolveCurrentPolicy ??
13
17
  createRuntimePrincipalPolicyCurrentResolver(runtime);
14
18
  const authorityInput = {
@@ -20,13 +24,14 @@ export function createRuntimeGroupMetadataAccess(runtime, organizationId, stillC
20
24
  stillCurrent: stillCurrent ?? (() => true),
21
25
  };
22
26
  return createGroupMetadataAccess({
23
- verifyMetadataContainer: resolveCurrentPolicy
24
- ? createCurrentGroupMetadataContainerVerifier({
25
- ...authorityInput,
26
- resolveCurrentPolicy,
27
- recoveryBatch: currentRecovery?.recoveryBatch,
28
- })
29
- : createGroupMetadataContainerVerifier(authorityInput),
27
+ verifyMetadataContainer: verifyMetadataContainer ??
28
+ (resolveCurrentPolicy
29
+ ? createCurrentGroupMetadataContainerVerifier({
30
+ ...authorityInput,
31
+ resolveCurrentPolicy,
32
+ recoveryBatch: currentRecovery?.recoveryBatch,
33
+ })
34
+ : createGroupMetadataContainerVerifier(authorityInput)),
30
35
  apiClient: runtime.apiClient,
31
36
  execSql: runtime.infra.execSql,
32
37
  organizationId,
@@ -1 +1 @@
1
- {"version":3,"file":"groupMetadataRuntime.js","sourceRoot":"","sources":["groupMetadataRuntime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,yDAAyD,CAAC;AAI1G,OAAO,EACL,2CAA2C,GAE5C,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,kCAAkC,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,2CAA2C,EAAE,MAAM,iCAAiC,CAAC;AAC9F,OAAO,EACL,yBAAyB,GAE1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oCAAoC,EAAE,MAAM,mCAAmC,CAAC;AAmBzF,MAAM,UAAU,gCAAgC,CAC9C,OAA6B,EAC7B,cAAsB,EACtB,YAA4B,EAC5B,eAKC;IAED,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,SAAS,CAAC;IACvE,IAAI,CAAC,eAAe;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,kCAAkC,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,oBAAoB,GACxB,eAAe,EAAE,oBAAoB;QACrC,2CAA2C,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG;QACrB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;QAC9B,cAAc;QACd,sBAAsB,EAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB;QAC3D,0BAA0B,EAAE,OAAO,CAAC,0BAA0B;QAC9D,YAAY,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;KAC3C,CAAC;IACF,OAAO,yBAAyB,CAAC;QAC/B,uBAAuB,EAAE,oBAAoB;YAC3C,CAAC,CAAC,2CAA2C,CAAC;gBAC1C,GAAG,cAAc;gBACjB,oBAAoB;gBACpB,aAAa,EAAE,eAAe,EAAE,aAAa;aAC9C,CAAC;YACJ,CAAC,CAAC,oCAAoC,CAAC,cAAc,CAAC;QACxD,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;QAC9B,cAAc;QACd,wBAAwB,EAAE,+BAA+B,CAAC,OAAO,CAAC;QAClE,eAAe;QACf,YAAY;QACZ,+BAA+B,EAAE,MAAM;KACxC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { EncapsulationKeyPair } from \"@tearleads/crypto\";\nimport { createProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification/userKeyResolver\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n createRuntimePrincipalPolicyCurrentResolver,\n type PrincipalPolicyRecoveryRuntime,\n} from \"../principals/runtimePolicyRecovery\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport { createCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport {\n createGroupMetadataAccess,\n type GroupMetadataAccessInput,\n} from \"./groupMetadataAccess\";\nimport { createGroupMetadataContainerVerifier } from \"./groupMetadataContainerAuthority\";\nimport type { PrincipalPolicyReadApi } from \"./groupPolicyMutationContext\";\n\ninterface GroupMetadataRuntime {\n readonly state?: PrincipalPolicyRecoveryRuntime[\"state\"];\n readonly apiClient: GroupMetadataAccessInput[\"apiClient\"] &\n PrincipalPolicyReadApi &\n PrincipalPolicyRecoveryRuntime[\"apiClient\"];\n readonly crypto: {\n readonly encapsulationKeyPair: EncapsulationKeyPair | null;\n };\n readonly infra: { readonly execSql: ExecSql };\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly util: {\n readonly log: (message: string) => void;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n };\n}\n\nexport function createRuntimeGroupMetadataAccess(\n runtime: GroupMetadataRuntime,\n organizationId: string,\n stillCurrent?: () => boolean,\n currentRecovery?: {\n readonly resolveCurrentPolicy: ReturnType<\n typeof createRuntimePrincipalPolicyCurrentResolver\n >;\n readonly recoveryBatch: object;\n },\n) {\n const targetSecretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n if (!targetSecretKey) throw new Error(\"Group metadata identity is locked\");\n const warmer = createRuntimePrincipalPolicyWarmer(runtime);\n const resolveCurrentPolicy =\n currentRecovery?.resolveCurrentPolicy ??\n createRuntimePrincipalPolicyCurrentResolver(runtime);\n const authorityInput = {\n apiClient: runtime.apiClient,\n execSql: runtime.infra.execSql,\n organizationId,\n reportSecurityIncident: runtime.util.reportSecurityIncident,\n resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,\n stillCurrent: stillCurrent ?? (() => true),\n };\n return createGroupMetadataAccess({\n verifyMetadataContainer: resolveCurrentPolicy\n ? createCurrentGroupMetadataContainerVerifier({\n ...authorityInput,\n resolveCurrentPolicy,\n recoveryBatch: currentRecovery?.recoveryBatch,\n })\n : createGroupMetadataContainerVerifier(authorityInput),\n apiClient: runtime.apiClient,\n execSql: runtime.infra.execSql,\n organizationId,\n resolveProjectionUserKey: createProjectionUserKeyResolver(runtime),\n targetSecretKey,\n stillCurrent,\n warmReferencedPrincipalPolicies: warmer,\n });\n}\n"]}
1
+ {"version":3,"file":"groupMetadataRuntime.js","sourceRoot":"","sources":["groupMetadataRuntime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,yDAAyD,CAAC;AAI1G,OAAO,EACL,2CAA2C,GAE5C,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,kCAAkC,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,2CAA2C,EAAE,MAAM,iCAAiC,CAAC;AAC9F,OAAO,EACL,yBAAyB,GAE1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oCAAoC,EAAE,MAAM,mCAAmC,CAAC;AAmBzF,MAAM,UAAU,gCAAgC,CAC9C,OAA6B,EAC7B,cAAsB,EACtB,YAA4B,EAC5B,uBAA6E,EAC7E,eAKC;IAED,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,SAAS,CAAC;IACvE,IAAI,CAAC,eAAe;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IAC3E,yEAAyE;IACzE,+EAA+E;IAC/E,MAAM,MAAM,GAAG,kCAAkC,CAAC,OAAO,EAAE;QACzD,kBAAkB,EAAE,IAAI;KACzB,CAAC,CAAC;IACH,MAAM,oBAAoB,GACxB,eAAe,EAAE,oBAAoB;QACrC,2CAA2C,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG;QACrB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;QAC9B,cAAc;QACd,sBAAsB,EAAE,OAAO,CAAC,IAAI,CAAC,sBAAsB;QAC3D,0BAA0B,EAAE,OAAO,CAAC,0BAA0B;QAC9D,YAAY,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;KAC3C,CAAC;IACF,OAAO,yBAAyB,CAAC;QAC/B,uBAAuB,EACrB,uBAAuB;YACvB,CAAC,oBAAoB;gBACnB,CAAC,CAAC,2CAA2C,CAAC;oBAC1C,GAAG,cAAc;oBACjB,oBAAoB;oBACpB,aAAa,EAAE,eAAe,EAAE,aAAa;iBAC9C,CAAC;gBACJ,CAAC,CAAC,oCAAoC,CAAC,cAAc,CAAC,CAAC;QAC3D,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;QAC9B,cAAc;QACd,wBAAwB,EAAE,+BAA+B,CAAC,OAAO,CAAC;QAClE,eAAe;QACf,YAAY;QACZ,+BAA+B,EAAE,MAAM;KACxC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { EncapsulationKeyPair } from \"@tearleads/crypto\";\nimport { createProjectionUserKeyResolver } from \"../../data/keyingProjectionVerification/userKeyResolver\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport {\n createRuntimePrincipalPolicyCurrentResolver,\n type PrincipalPolicyRecoveryRuntime,\n} from \"../principals/runtimePolicyRecovery\";\nimport { createRuntimePrincipalPolicyWarmer } from \"../principals/runtimePolicyWarmer\";\nimport { createCurrentGroupMetadataContainerVerifier } from \"./currentGroupMetadataAuthority\";\nimport {\n createGroupMetadataAccess,\n type GroupMetadataAccessInput,\n} from \"./groupMetadataAccess\";\nimport { createGroupMetadataContainerVerifier } from \"./groupMetadataContainerAuthority\";\nimport type { PrincipalPolicyReadApi } from \"./groupPolicyMutationContext\";\n\ninterface GroupMetadataRuntime {\n readonly state?: PrincipalPolicyRecoveryRuntime[\"state\"];\n readonly apiClient: GroupMetadataAccessInput[\"apiClient\"] &\n PrincipalPolicyReadApi &\n PrincipalPolicyRecoveryRuntime[\"apiClient\"];\n readonly crypto: {\n readonly encapsulationKeyPair: EncapsulationKeyPair | null;\n };\n readonly infra: { readonly execSql: ExecSql };\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n readonly util: {\n readonly log: (message: string) => void;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n };\n}\n\nexport function createRuntimeGroupMetadataAccess(\n runtime: GroupMetadataRuntime,\n organizationId: string,\n stillCurrent?: () => boolean,\n verifyMetadataContainer?: GroupMetadataAccessInput[\"verifyMetadataContainer\"],\n currentRecovery?: {\n readonly resolveCurrentPolicy: ReturnType<\n typeof createRuntimePrincipalPolicyCurrentResolver\n >;\n readonly recoveryBatch: object;\n },\n) {\n const targetSecretKey = runtime.crypto.encapsulationKeyPair?.secretKey;\n if (!targetSecretKey) throw new Error(\"Group metadata identity is locked\");\n // The signed metadata projection selects exact citations. Reuse matching\n // private evidence with its dependencies and pins before bounded online reads.\n const warmer = createRuntimePrincipalPolicyWarmer(runtime, {\n preferLocalCurrent: true,\n });\n const resolveCurrentPolicy =\n currentRecovery?.resolveCurrentPolicy ??\n createRuntimePrincipalPolicyCurrentResolver(runtime);\n const authorityInput = {\n apiClient: runtime.apiClient,\n execSql: runtime.infra.execSql,\n organizationId,\n reportSecurityIncident: runtime.util.reportSecurityIncident,\n resolveTrustedUserIdentity: runtime.resolveTrustedUserIdentity,\n stillCurrent: stillCurrent ?? (() => true),\n };\n return createGroupMetadataAccess({\n verifyMetadataContainer:\n verifyMetadataContainer ??\n (resolveCurrentPolicy\n ? createCurrentGroupMetadataContainerVerifier({\n ...authorityInput,\n resolveCurrentPolicy,\n recoveryBatch: currentRecovery?.recoveryBatch,\n })\n : createGroupMetadataContainerVerifier(authorityInput)),\n apiClient: runtime.apiClient,\n execSql: runtime.infra.execSql,\n organizationId,\n resolveProjectionUserKey: createProjectionUserKeyResolver(runtime),\n targetSecretKey,\n stillCurrent,\n warmReferencedPrincipalPolicies: warmer,\n });\n}\n"]}
@@ -68,7 +68,7 @@ export async function verifyDirectoryGroup(input, head) {
68
68
  * walk isolates it rather than failing every name in the organization, but it
69
69
  * still records the incident: only a dishonest admin signs such a name.
70
70
  */
71
- async function readDirectoryGroupName(input, bundle) {
71
+ export async function readDirectoryGroupName(input, bundle) {
72
72
  try {
73
73
  return await readGroupPolicyPayloadName(bundle, input.readEncryptedName);
74
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"groupNameUniqueness.js","sourceRoot":"","sources":["groupNameUniqueness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../data/keyingProjectionVerification/error\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"../principals/policyVerification\";\nimport { GroupMetadataUnreadableError } from \"./groupMetadataAccess\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport {\n canonicalGroupNameKey,\n type GroupPolicyNameReader,\n readGroupPolicyPayloadName,\n} from \"./principalPolicyRequest\";\n\nexport interface DirectoryGroupWalkInput {\n readonly readEncryptedName?: GroupPolicyNameReader | undefined;\n readonly apiClient: {\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n };\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly execSql: ExecSql;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}\n\ntype DirectoryGroupHead = OrganizationAuthorityDescriptor[\"groupHeads\"][number];\n\ninterface VerifiedDirectoryGroup {\n readonly bundle: PrincipalPolicyBundleResponse;\n /** Null when the signed name does not decrypt for any member. */\n readonly name: string | null;\n readonly nameKey: string | null;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\n/**\n * Loads one directory group at its committed head: from the local retained\n * bundles when they already hold that head, and from the API otherwise.\n */\nasync function loadDirectoryGroupBundle(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n) {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n head.principalId,\n );\n const cached = await loadPrincipalPolicyBundleForReference(\n input.execSql,\n head,\n localCheckpoint,\n );\n const bundle =\n cached ??\n (await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n head.principalId,\n ));\n if (!bundle) {\n throw new Error(\n `Organization directory group ${head.principalId} could not be loaded`,\n );\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n head,\n )\n ) {\n // Head drift is reachable by a concurrent admin mutation, so it is a\n // plain error to retry on, not an incident (as in the share path). The\n // typed errors stay for signature and projection failures.\n throw new Error(\n \"Organization directory group policy does not match the signed organization directory\",\n );\n }\n return { bundle, localCheckpoint };\n}\n\n/**\n * A bundle served from local retention was verified when it was retained, and\n * is verified again here on purpose: the re-check costs local CPU only, and it\n * keeps the name a group vouches for independent of what any earlier caller\n * trusted at retention time.\n */\nexport async function verifyDirectoryGroup(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n): Promise<VerifiedDirectoryGroup> {\n const { bundle, localCheckpoint } = await loadDirectoryGroupBundle(\n input,\n head,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(\n `Organization directory group signer key could not be loaded (${signerPublicKeys.error})`,\n );\n }\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: head,\n loadExternalAuthority: async () => input.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Organization directory group verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, input.descriptor);\n const name = await readDirectoryGroupName(input, bundle);\n return {\n bundle,\n name,\n nameKey: name === null ? null : canonicalGroupNameKey(name),\n policy: verified.value,\n };\n}\n\n/**\n * A verified group whose name no member can open has no name to select by. The\n * walk isolates it rather than failing every name in the organization, but it\n * still records the incident: only a dishonest admin signs such a name.\n */\nasync function readDirectoryGroupName(\n input: DirectoryGroupWalkInput,\n bundle: PrincipalPolicyBundleResponse,\n): Promise<string | null> {\n try {\n return await readGroupPolicyPayloadName(bundle, input.readEncryptedName);\n } catch (error) {\n if (!(error instanceof GroupMetadataUnreadableError)) throw error;\n await input.reportSecurityIncident(\n new KeyingVerificationError(\n \"invalid_shape\",\n \"Signed group name does not open under the cited metadata key\",\n ),\n {\n evidenceHashes: { stateHash: bundle.currentState.stateHash },\n objectId: bundle.currentState.principalId,\n objectKind: \"principal\",\n operation: \"organization.groupName\",\n organizationId: input.organizationId,\n },\n );\n return null;\n }\n}\n\n/**\n * Signed group names are the only authenticated label a member can choose a\n * group by, and the API does not make them unique within an organization. So\n * uniqueness is enforced here, where a name enters: before an admin signs a\n * new group, every group committed in the signed organization directory\n * (the reserved Admins and Members groups included) is loaded, verified\n * against its directory head, and its committed name compared by canonical\n * key. A share then needs only the target's verified name. A compromised\n * server cannot mint a signed group, so it cannot create the duplicate this\n * check refuses.\n *\n * The walk is fail-closed: one group that cannot be loaded or verified blocks\n * the creation, since an unverifiable group could carry any name. A verified\n * group whose name does not decrypt is skipped: no member can open that name,\n * so it cannot collide with one. It runs the\n * loads in parallel and retains every verified bundle, so a later creation\n * finds unchanged groups locally and fetches only those whose head moved.\n */\nconst DIRECTORY_WALK_CONCURRENCY = 4;\n\n/** Runs `work` over `items` with at most `limit` in flight, keeping order. */\nasync function mapWithConcurrency<T, R>(\n items: ReadonlyArray<T>,\n limit: number,\n work: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async () => {\n for (;;) {\n const index = next++;\n const item = items[index];\n if (index >= items.length || item === undefined) return;\n results[index] = await work(item);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n\nexport async function assertGroupNameUniqueInDirectory(\n input: DirectoryGroupWalkInput & { readonly name: string },\n): Promise<void> {\n const nameKey = canonicalGroupNameKey(input.name);\n // Each verified bundle is retained as soon as it verifies, so a failure\n // later in the walk (or a taken name) still leaves the cache warm for the\n // retry. Retention runs in a transaction on the shared connection, so the\n // writes are chained one after another while the loads stay concurrent.\n const retainedAt = new Date().toISOString();\n let retention: Promise<void> = Promise.resolve();\n const verifyAndRetain = async (\n head: DirectoryGroupHead,\n ): Promise<VerifiedDirectoryGroup> => {\n const group = await verifyDirectoryGroup(input, head);\n retention = retention.then(() =>\n retainVerifiedPrincipalPolicyBundle({\n bundle: group.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: group.policy,\n updatedAt: retainedAt,\n }),\n );\n await retention;\n return group;\n };\n const groups = await mapWithConcurrency(\n input.descriptor.groupHeads,\n DIRECTORY_WALK_CONCURRENCY,\n verifyAndRetain,\n );\n // A taken name is user input to correct, not evidence of tampering, so this\n // is a plain error: group creation runs under security-incident reporting,\n // which records every KeyingVerificationError.\n if (groups.some((group) => group.nameKey === nameKey)) {\n throw new Error(\n \"Another signed group in this organization already carries this name\",\n );\n }\n}\n"]}
1
+ {"version":3,"file":"groupNameUniqueness.js","sourceRoot":"","sources":["groupNameUniqueness.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n KeyingVerificationError,\n type PrincipalPolicyExternalAuthority,\n type VerifiedPrincipalPolicy,\n} from \"@tearleads/crypto\";\nimport type { PrincipalPolicyBundleResponse } from \"@tearleads/validators/response\";\nimport { throwKeyingVerificationErrorWithContext } from \"../../data/keyingProjectionVerification/error\";\nimport { loadPrincipalPolicyCheckpoint } from \"../../data/persistence/keyingCheckpointPersistence\";\nimport { loadPrincipalPolicyBundleForReference } from \"../../data/persistence/principalPolicyReferencePersistence\";\nimport { retainVerifiedPrincipalPolicyBundle } from \"../../data/persistence/verifiedPrincipalPolicyRetentionPersistence\";\nimport {\n type OrganizationAuthorityDescriptor,\n principalHeadMatchesReference,\n} from \"../../data/principals/organizationAuthorityDescriptor\";\nimport {\n principalPolicyReferenceFromBundle,\n verifyPrincipalPolicyBundleWithExternalOrganizationAdmins,\n} from \"../../data/principals/principalPolicyAdminSigners\";\nimport type { SecurityIncidentReporter } from \"../../data/securityIncidents\";\nimport type { ExecSql } from \"../../data/sqlite/sqlSchema\";\nimport type { TrustedUserIdentityResolver } from \"../../data/trustedUserIdentity\";\nimport { collectPrincipalPolicySignerPublicKeys } from \"../principals/policyVerification\";\nimport { GroupMetadataUnreadableError } from \"./groupMetadataAccess\";\nimport { assertGroupMetadataBinding } from \"./groupMetadataBinding\";\nimport {\n canonicalGroupNameKey,\n type GroupPolicyNameReader,\n readGroupPolicyPayloadName,\n} from \"./principalPolicyRequest\";\n\nexport interface DirectoryGroupWalkInput {\n readonly readEncryptedName?: GroupPolicyNameReader | undefined;\n readonly apiClient: {\n getCurrentPrincipalPolicy: (\n principalType: \"group\" | \"organization\",\n principalId: string,\n ) => Promise<PrincipalPolicyBundleResponse | null>;\n };\n readonly descriptor: OrganizationAuthorityDescriptor;\n readonly execSql: ExecSql;\n readonly externalAuthority: PrincipalPolicyExternalAuthority;\n readonly organizationId: string;\n readonly reportSecurityIncident: SecurityIncidentReporter;\n readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;\n}\n\ntype DirectoryGroupHead = OrganizationAuthorityDescriptor[\"groupHeads\"][number];\n\ninterface VerifiedDirectoryGroup {\n readonly bundle: PrincipalPolicyBundleResponse;\n /** Null when the signed name does not decrypt for any member. */\n readonly name: string | null;\n readonly nameKey: string | null;\n readonly policy: VerifiedPrincipalPolicy;\n}\n\n/**\n * Loads one directory group at its committed head: from the local retained\n * bundles when they already hold that head, and from the API otherwise.\n */\nasync function loadDirectoryGroupBundle(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n) {\n const localCheckpoint = await loadPrincipalPolicyCheckpoint(\n input.execSql,\n \"group\",\n head.principalId,\n );\n const cached = await loadPrincipalPolicyBundleForReference(\n input.execSql,\n head,\n localCheckpoint,\n );\n const bundle =\n cached ??\n (await input.apiClient.getCurrentPrincipalPolicy(\n \"group\",\n head.principalId,\n ));\n if (!bundle) {\n throw new Error(\n `Organization directory group ${head.principalId} could not be loaded`,\n );\n }\n if (\n !principalHeadMatchesReference(\n principalPolicyReferenceFromBundle(bundle),\n head,\n )\n ) {\n // Head drift is reachable by a concurrent admin mutation, so it is a\n // plain error to retry on, not an incident (as in the share path). The\n // typed errors stay for signature and projection failures.\n throw new Error(\n \"Organization directory group policy does not match the signed organization directory\",\n );\n }\n return { bundle, localCheckpoint };\n}\n\n/**\n * A bundle served from local retention was verified when it was retained, and\n * is verified again here on purpose: the re-check costs local CPU only, and it\n * keeps the name a group vouches for independent of what any earlier caller\n * trusted at retention time.\n */\nexport async function verifyDirectoryGroup(\n input: DirectoryGroupWalkInput,\n head: DirectoryGroupHead,\n): Promise<VerifiedDirectoryGroup> {\n const { bundle, localCheckpoint } = await loadDirectoryGroupBundle(\n input,\n head,\n );\n const signerPublicKeys = await collectPrincipalPolicySignerPublicKeys({\n bundle,\n resolveTrustedUserIdentity: input.resolveTrustedUserIdentity,\n });\n if (\"error\" in signerPublicKeys) {\n throw new Error(\n `Organization directory group signer key could not be loaded (${signerPublicKeys.error})`,\n );\n }\n const verified =\n await verifyPrincipalPolicyBundleWithExternalOrganizationAdmins({\n bundle,\n expectedReference: head,\n loadExternalAuthority: async () => input.externalAuthority,\n localCheckpoint,\n signerPublicKeys: signerPublicKeys.signerPublicKeys,\n });\n if (!verified.ok) {\n throwKeyingVerificationErrorWithContext(\n verified.error,\n \"Organization directory group verification failed\",\n );\n }\n assertGroupMetadataBinding(bundle, input.descriptor);\n const name = await readDirectoryGroupName(input, bundle);\n return {\n bundle,\n name,\n nameKey: name === null ? null : canonicalGroupNameKey(name),\n policy: verified.value,\n };\n}\n\n/**\n * A verified group whose name no member can open has no name to select by. The\n * walk isolates it rather than failing every name in the organization, but it\n * still records the incident: only a dishonest admin signs such a name.\n */\nexport async function readDirectoryGroupName(\n input: Pick<\n DirectoryGroupWalkInput,\n \"readEncryptedName\" | \"reportSecurityIncident\" | \"organizationId\"\n >,\n bundle: Parameters<typeof readGroupPolicyPayloadName>[0],\n): Promise<string | null> {\n try {\n return await readGroupPolicyPayloadName(bundle, input.readEncryptedName);\n } catch (error) {\n if (!(error instanceof GroupMetadataUnreadableError)) throw error;\n await input.reportSecurityIncident(\n new KeyingVerificationError(\n \"invalid_shape\",\n \"Signed group name does not open under the cited metadata key\",\n ),\n {\n evidenceHashes: { stateHash: bundle.currentState.stateHash },\n objectId: bundle.currentState.principalId,\n objectKind: \"principal\",\n operation: \"organization.groupName\",\n organizationId: input.organizationId,\n },\n );\n return null;\n }\n}\n\n/**\n * Signed group names are the only authenticated label a member can choose a\n * group by, and the API does not make them unique within an organization. So\n * uniqueness is enforced here, where a name enters: before an admin signs a\n * new group, every group committed in the signed organization directory\n * (the reserved Admins and Members groups included) is loaded, verified\n * against its directory head, and its committed name compared by canonical\n * key. A share then needs only the target's verified name. A compromised\n * server cannot mint a signed group, so it cannot create the duplicate this\n * check refuses.\n *\n * The walk is fail-closed: one group that cannot be loaded or verified blocks\n * the creation, since an unverifiable group could carry any name. A verified\n * group whose name does not decrypt is skipped: no member can open that name,\n * so it cannot collide with one. It runs the\n * loads in parallel and retains every verified bundle, so a later creation\n * finds unchanged groups locally and fetches only those whose head moved.\n */\nconst DIRECTORY_WALK_CONCURRENCY = 4;\n\n/** Runs `work` over `items` with at most `limit` in flight, keeping order. */\nasync function mapWithConcurrency<T, R>(\n items: ReadonlyArray<T>,\n limit: number,\n work: (item: T) => Promise<R>,\n): Promise<R[]> {\n const results: R[] = [];\n let next = 0;\n const worker = async () => {\n for (;;) {\n const index = next++;\n const item = items[index];\n if (index >= items.length || item === undefined) return;\n results[index] = await work(item);\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, worker),\n );\n return results;\n}\n\nexport async function assertGroupNameUniqueInDirectory(\n input: DirectoryGroupWalkInput & { readonly name: string },\n): Promise<void> {\n const nameKey = canonicalGroupNameKey(input.name);\n // Each verified bundle is retained as soon as it verifies, so a failure\n // later in the walk (or a taken name) still leaves the cache warm for the\n // retry. Retention runs in a transaction on the shared connection, so the\n // writes are chained one after another while the loads stay concurrent.\n const retainedAt = new Date().toISOString();\n let retention: Promise<void> = Promise.resolve();\n const verifyAndRetain = async (\n head: DirectoryGroupHead,\n ): Promise<VerifiedDirectoryGroup> => {\n const group = await verifyDirectoryGroup(input, head);\n retention = retention.then(() =>\n retainVerifiedPrincipalPolicyBundle({\n bundle: group.bundle,\n execSql: input.execSql,\n organizationId: input.organizationId,\n policy: group.policy,\n updatedAt: retainedAt,\n }),\n );\n await retention;\n return group;\n };\n const groups = await mapWithConcurrency(\n input.descriptor.groupHeads,\n DIRECTORY_WALK_CONCURRENCY,\n verifyAndRetain,\n );\n // A taken name is user input to correct, not evidence of tampering, so this\n // is a plain error: group creation runs under security-incident reporting,\n // which records every KeyingVerificationError.\n if (groups.some((group) => group.nameKey === nameKey)) {\n throw new Error(\n \"Another signed group in this organization already carries this name\",\n );\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { diffPrincipalProjectionMembers } from "./policyHistoryReadModel.js";
2
+ /** Keep a page boundary's actual predecessor when describing membership changes. */
3
+ export function buildOrganizationGroupPolicyHistoryPage(input) {
4
+ const { page } = input;
5
+ return {
6
+ groupId: input.groupId,
7
+ organizationId: input.organizationId,
8
+ principalId: input.groupId,
9
+ principalType: "group",
10
+ nextBeforeVersion: page.nextBeforeVersion,
11
+ entries: page.entries
12
+ .map((entry, index) => ({
13
+ changes: diffPrincipalProjectionMembers({
14
+ current: entry.projection,
15
+ previous: (index === 0
16
+ ? page.predecessor?.projection
17
+ : page.entries[index - 1]?.projection) ?? [],
18
+ }),
19
+ createdAt: entry.state.createdAt,
20
+ keyEpoch: entry.state.keyEpoch,
21
+ memberCount: entry.state.memberCount,
22
+ signedAt: entry.state.signedAt,
23
+ signerUserId: entry.state.signerUserId,
24
+ signerUserKeyFingerprint: entry.state.signerUserKeyFingerprint,
25
+ stateHash: entry.state.stateHash,
26
+ version: entry.state.version,
27
+ }))
28
+ .reverse(),
29
+ };
30
+ }
31
+ //# sourceMappingURL=groupPolicyHistoryPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groupPolicyHistoryPage.js","sourceRoot":"","sources":["groupPolicyHistoryPage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAG1E,oFAAoF;AACpF,MAAM,UAAU,uCAAuC,CAAC,KAIvD;IACC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACvB,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,KAAK,CAAC,OAAO;QAC1B,aAAa,EAAE,OAAO;QACtB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,OAAO,EAAE,IAAI,CAAC,OAAO;aAClB,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACtB,OAAO,EAAE,8BAA8B,CAAC;gBACtC,OAAO,EAAE,KAAK,CAAC,UAAU;gBACzB,QAAQ,EACN,CAAC,KAAK,KAAK,CAAC;oBACV,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU;oBAC9B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI,EAAE;aACjD,CAAC;YACF,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;YAChC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;YAC9B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW;YACpC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;YAC9B,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY;YACtC,wBAAwB,EAAE,KAAK,CAAC,KAAK,CAAC,wBAAwB;YAC9D,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;YAChC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO;SAC7B,CAAC,CAAC;aACF,OAAO,EAAE;KACb,CAAC;AACJ,CAAC","sourcesContent":["import type { RecoveredPrincipalHistoryPage } from \"../principals/loadRecoveredPrincipalHistoryPage\";\nimport { diffPrincipalProjectionMembers } from \"./policyHistoryReadModel\";\nimport type { OrganizationGroupPolicyHistory } from \"./policyHistoryTypes\";\n\n/** Keep a page boundary's actual predecessor when describing membership changes. */\nexport function buildOrganizationGroupPolicyHistoryPage(input: {\n readonly groupId: string;\n readonly organizationId: string;\n readonly page: RecoveredPrincipalHistoryPage;\n}): OrganizationGroupPolicyHistory {\n const { page } = input;\n return {\n groupId: input.groupId,\n organizationId: input.organizationId,\n principalId: input.groupId,\n principalType: \"group\",\n nextBeforeVersion: page.nextBeforeVersion,\n entries: page.entries\n .map((entry, index) => ({\n changes: diffPrincipalProjectionMembers({\n current: entry.projection,\n previous:\n (index === 0\n ? page.predecessor?.projection\n : page.entries[index - 1]?.projection) ?? [],\n }),\n createdAt: entry.state.createdAt,\n keyEpoch: entry.state.keyEpoch,\n memberCount: entry.state.memberCount,\n signedAt: entry.state.signedAt,\n signerUserId: entry.state.signerUserId,\n signerUserKeyFingerprint: entry.state.signerUserKeyFingerprint,\n stateHash: entry.state.stateHash,\n version: entry.state.version,\n }))\n .reverse(),\n };\n}\n"]}
@@ -129,6 +129,7 @@ export async function acknowledgeInitialGroupPolicy(input) {
129
129
  input.response.currentState?.stateHash !== input.stateHash ||
130
130
  input.response.currentState.version !== state.version ||
131
131
  input.response.currentState.keyEpoch !== state.keyEpoch ||
132
+ input.response.currentState.keyFingerprint !== state.keyFingerprint ||
132
133
  input.response.currentState.memberCount !== state.memberCount ||
133
134
  state.principalType !== "group" ||
134
135
  state.principalId !== input.request.groupId ||
@@ -1 +1 @@
1
- {"version":3,"file":"groupPolicyMutationAcknowledgement.js","sourceRoot":"","sources":["groupPolicyMutationAcknowledgement.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n computePrincipalContainerGrantRoot,\n computePrincipalMemberEnvelopesRoot,\n computePrincipalMembershipRoot,\n computePrincipalProjectionRoot,\n computePrincipalStatePayloadCiphertextHash,\n makeVerifiedPrincipalPolicy,\n normalizePrincipalProjectionMembers,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type {\n CreateOrganizationGroupRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n OrganizationGroupSummaryResponse,\n PrincipalPolicyBundleResponse,\n PrincipalPolicyMutationResponse,\n PrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { canonicalKeyingJsonString } from \"../../data/keyingCanonicalJson\";\nimport {\n type CurrentPolicyMutationInput,\n verifyCurrentPolicyMutation,\n} from \"./currentPolicyMutationAcknowledgement\";\nimport { assertPrincipalPolicyReceiptArtifacts } from \"./principalPolicyReceiptArtifacts\";\n\nexport { assertGroupPolicyEnvelopesMatchAcknowledgement } from \"./principalPolicyReceiptArtifacts\";\n\nfunction historyWithCurrent(\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyBundleResponse[\"previousStates\"] {\n return [\n ...bundle.previousStates,\n {\n state: bundle.currentState,\n projection: bundle.currentProjection,\n grants: bundle.currentGrants,\n },\n ];\n}\n\nfunction isAuthoredSuccessorConsistent(\n state: Pick<\n PrincipalStateResponse,\n | \"keyEpoch\"\n | \"keyFingerprint\"\n | \"prevStateHash\"\n | \"principalId\"\n | \"principalType\"\n | \"version\"\n >,\n expectedHead: ReferencedPrincipalHead,\n previous: PrincipalStateResponse,\n): boolean {\n return (\n state.principalType === expectedHead.principalType &&\n state.principalId === expectedHead.principalId &&\n state.version === expectedHead.version &&\n state.keyEpoch === expectedHead.keyEpoch &&\n state.keyFingerprint === expectedHead.keyFingerprint &&\n state.principalType === previous.principalType &&\n state.principalId === previous.principalId &&\n state.version === previous.version + 1 &&\n state.prevStateHash === previous.stateHash\n );\n}\n\nexport function buildAcknowledgedGroupPolicyBundle(input: {\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly request: PutPrincipalPolicyRequest;\n readonly response: PrincipalPolicyMutationResponse;\n}): PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse {\n assertPrincipalPolicyReceiptArtifacts(input);\n return {\n ...input.response,\n previousStates: historyWithCurrent(input.currentPolicy),\n };\n}\n\nasync function assertPolicyRequestCommitments(\n request: PutPrincipalPolicyRequest,\n): Promise<void> {\n const projection = normalizePrincipalProjectionMembers(request.projection);\n const members = projection.map((member) => ({ userId: member.userId }));\n const [\n membershipRoot,\n memberEnvelopesRoot,\n projectionRoot,\n payloadCiphertextHash,\n grantRoot,\n ] = await Promise.all([\n computePrincipalMembershipRoot(members),\n computePrincipalMemberEnvelopesRoot(request.memberEnvelopes),\n computePrincipalProjectionRoot(projection),\n computePrincipalStatePayloadCiphertextHash(\n request.encryptedPayload.ciphertext,\n ),\n computePrincipalContainerGrantRoot(request.grants),\n ]);\n if (\n request.state.membershipRoot !== membershipRoot ||\n request.state.memberEnvelopesRoot !== memberEnvelopesRoot ||\n request.state.projectionRoot !== projectionRoot ||\n request.state.memberCount !== projection.length ||\n request.state.grantRoot !== grantRoot ||\n request.state.grantCount !== request.grants.length ||\n request.encryptedPayload.ciphertextHash !== payloadCiphertextHash ||\n request.state.payloadCiphertextHash !== payloadCiphertextHash\n ) {\n throw new Error(\"Authored group policy commitments are invalid\");\n }\n}\n\nfunction verifiedPolicy(input: {\n currentPolicy?: PrincipalPolicyBundleResponse | undefined;\n projection: PutPrincipalPolicyRequest[\"projection\"];\n grants: PutPrincipalPolicyRequest[\"grants\"];\n state: PrincipalStateResponse;\n}): VerifiedPrincipalPolicy {\n const previousStates = input.currentPolicy\n ? historyWithCurrent(input.currentPolicy)\n : [];\n return makeVerifiedPrincipalPolicy({\n checkpoint: {\n principalId: input.state.principalId,\n principalType: input.state.principalType,\n stateHash: input.state.stateHash,\n version: input.state.version,\n },\n history: [\n ...previousStates,\n {\n state: input.state,\n projection: input.projection,\n grants: input.grants,\n },\n ],\n keyEpoch: input.state.keyEpoch,\n principalId: input.state.principalId,\n principalType: input.state.principalType,\n projection: input.projection,\n grants: input.grants,\n state: input.state,\n stateHash: input.state.stateHash,\n version: input.state.version,\n });\n}\n\ninterface FullPolicyMutationInput {\n readonly verifiedCurrentPolicy?: never;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly request: PutPrincipalPolicyRequest;\n}\n\nexport function acknowledgeGroupPolicyState(\n input: CurrentPolicyMutationInput & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicyCurrent>;\nexport function acknowledgeGroupPolicyState(\n input: FullPolicyMutationInput & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicy>;\nexport async function acknowledgeGroupPolicyState(\n input: (FullPolicyMutationInput | CurrentPolicyMutationInput) & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent> {\n if (input.verifiedCurrentPolicy !== undefined)\n return verifyCurrentPolicyMutation(input, input.response);\n await assertPolicyRequestCommitments(input.request);\n const { createdAt: _createdAt, stateHash, ...responseState } = input.response;\n const previous = input.currentPolicy.currentState;\n if (\n stateHash !== input.expectedHead.stateHash ||\n !isAuthoredSuccessorConsistent(\n input.response,\n input.expectedHead,\n previous,\n ) ||\n canonicalKeyingJsonString(responseState, \"stored group policy state\") !==\n canonicalKeyingJsonString(\n input.request.state,\n \"authored group policy state\",\n )\n ) {\n throw new Error(\"Group policy state acknowledgement mismatch\");\n }\n return verifiedPolicy({\n currentPolicy: input.currentPolicy,\n projection: input.request.projection,\n grants: input.request.grants,\n state: input.response,\n });\n}\n\n/**\n * Materialize the locally signed successor before submission so dependent\n * container mutations can wrap to the exact head that will commit with it.\n * The server still performs the authoritative signature, transition, and\n * authorization checks inside the combined transaction.\n */\nexport function prepareAuthoredGroupPolicy(\n input: CurrentPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicyCurrent>;\nexport function prepareAuthoredGroupPolicy(\n input: FullPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicy>;\nexport async function prepareAuthoredGroupPolicy(\n input: FullPolicyMutationInput | CurrentPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent> {\n if (input.verifiedCurrentPolicy !== undefined)\n return verifyCurrentPolicyMutation(input);\n await assertPolicyRequestCommitments(input.request);\n const previous = input.currentPolicy.currentState;\n const state = input.request.state;\n if (!isAuthoredSuccessorConsistent(state, input.expectedHead, previous)) {\n throw new Error(\"Authored group policy successor is inconsistent\");\n }\n return verifiedPolicy({\n currentPolicy: input.currentPolicy,\n projection: input.request.projection,\n grants: input.request.grants,\n state: {\n ...state,\n stateHash: input.expectedHead.stateHash,\n createdAt: state.signedAt,\n },\n });\n}\n\nexport async function acknowledgeInitialGroupPolicy(input: {\n readonly organizationId: string;\n readonly request: CreateOrganizationGroupRequest;\n readonly response: OrganizationGroupSummaryResponse;\n readonly stateHash: string;\n}): Promise<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n}> {\n await assertPolicyRequestCommitments(input.request.initialGroupPolicy);\n const state = input.request.initialGroupPolicy.state;\n if (\n input.response.groupId !== input.request.groupId ||\n input.response.organizationId !== input.organizationId ||\n input.response.isBuiltin ||\n input.response.currentState?.stateHash !== input.stateHash ||\n input.response.currentState.version !== state.version ||\n input.response.currentState.keyEpoch !== state.keyEpoch ||\n input.response.currentState.memberCount !== state.memberCount ||\n state.principalType !== \"group\" ||\n state.principalId !== input.request.groupId ||\n state.version !== 1 ||\n state.prevStateHash !== null\n ) {\n throw new Error(\"Created group policy acknowledgement mismatch\");\n }\n const storedState = {\n ...state,\n createdAt: input.response.createdAt,\n stateHash: input.stateHash,\n };\n const bundle: PrincipalPolicyBundleResponse = {\n currentMemberEnvelopes: {\n envelopes: input.request.initialGroupPolicy.memberEnvelopes,\n epoch: state.keyEpoch,\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: input.stateHash,\n },\n currentPayload: {\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: input.stateHash,\n ...input.request.initialGroupPolicy.encryptedPayload,\n createdAt: input.response.createdAt,\n },\n currentProjection: input.request.initialGroupPolicy.projection,\n currentGrants: input.request.initialGroupPolicy.grants,\n currentState: storedState,\n previousStates: [],\n };\n return {\n bundle,\n policy: verifiedPolicy({\n projection: input.request.initialGroupPolicy.projection,\n grants: input.request.initialGroupPolicy.grants,\n state: storedState,\n }),\n };\n}\n"]}
1
+ {"version":3,"file":"groupPolicyMutationAcknowledgement.js","sourceRoot":"","sources":["groupPolicyMutationAcknowledgement.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,EAClC,mCAAmC,EACnC,8BAA8B,EAC9B,8BAA8B,EAC9B,0CAA0C,EAC1C,2BAA2B,EAC3B,mCAAmC,GAIpC,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAEL,2BAA2B,GAC5B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qCAAqC,EAAE,MAAM,mCAAmC,CAAC;AAE1F,OAAO,EAAE,8CAA8C,EAAE,MAAM,mCAAmC,CAAC;AAEnG,SAAS,kBAAkB,CACzB,MAAqC;IAErC,OAAO;QACL,GAAG,MAAM,CAAC,cAAc;QACxB;YACE,KAAK,EAAE,MAAM,CAAC,YAAY;YAC1B,UAAU,EAAE,MAAM,CAAC,iBAAiB;YACpC,MAAM,EAAE,MAAM,CAAC,aAAa;SAC7B;KACF,CAAC;AACJ,CAAC;AAED,SAAS,6BAA6B,CACpC,KAQC,EACD,YAAqC,EACrC,QAAgC;IAEhC,OAAO,CACL,KAAK,CAAC,aAAa,KAAK,YAAY,CAAC,aAAa;QAClD,KAAK,CAAC,WAAW,KAAK,YAAY,CAAC,WAAW;QAC9C,KAAK,CAAC,OAAO,KAAK,YAAY,CAAC,OAAO;QACtC,KAAK,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ;QACxC,KAAK,CAAC,cAAc,KAAK,YAAY,CAAC,cAAc;QACpD,KAAK,CAAC,aAAa,KAAK,QAAQ,CAAC,aAAa;QAC9C,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,WAAW;QAC1C,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,OAAO,GAAG,CAAC;QACtC,KAAK,CAAC,aAAa,KAAK,QAAQ,CAAC,SAAS,CAC3C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,KAKlD;IACC,qCAAqC,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO;QACL,GAAG,KAAK,CAAC,QAAQ;QACjB,cAAc,EAAE,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,8BAA8B,CAC3C,OAAkC;IAElC,MAAM,UAAU,GAAG,mCAAmC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxE,MAAM,CACJ,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,SAAS,EACV,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpB,8BAA8B,CAAC,OAAO,CAAC;QACvC,mCAAmC,CAAC,OAAO,CAAC,eAAe,CAAC;QAC5D,8BAA8B,CAAC,UAAU,CAAC;QAC1C,0CAA0C,CACxC,OAAO,CAAC,gBAAgB,CAAC,UAAU,CACpC;QACD,kCAAkC,CAAC,OAAO,CAAC,MAAM,CAAC;KACnD,CAAC,CAAC;IACH,IACE,OAAO,CAAC,KAAK,CAAC,cAAc,KAAK,cAAc;QAC/C,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,mBAAmB;QACzD,OAAO,CAAC,KAAK,CAAC,cAAc,KAAK,cAAc;QAC/C,OAAO,CAAC,KAAK,CAAC,WAAW,KAAK,UAAU,CAAC,MAAM;QAC/C,OAAO,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS;QACrC,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM;QAClD,OAAO,CAAC,gBAAgB,CAAC,cAAc,KAAK,qBAAqB;QACjE,OAAO,CAAC,KAAK,CAAC,qBAAqB,KAAK,qBAAqB,EAC7D,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAKvB;IACC,MAAM,cAAc,GAAG,KAAK,CAAC,aAAa;QACxC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC;QACzC,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,2BAA2B,CAAC;QACjC,UAAU,EAAE;YACV,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW;YACpC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa;YACxC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;YAChC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO;SAC7B;QACD,OAAO,EAAE;YACP,GAAG,cAAc;YACjB;gBACE,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;gBAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB;SACF;QACD,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ;QAC9B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW;QACpC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,aAAa;QACxC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;QAChC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO;KAC7B,CAAC,CAAC;AACL,CAAC;AAmBD,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,KAEC;IAED,IAAI,KAAK,CAAC,qBAAqB,KAAK,SAAS;QAC3C,OAAO,2BAA2B,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5D,MAAM,8BAA8B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,aAAa,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC9E,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC;IAClD,IACE,SAAS,KAAK,KAAK,CAAC,YAAY,CAAC,SAAS;QAC1C,CAAC,6BAA6B,CAC5B,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,YAAY,EAClB,QAAQ,CACT;QACD,yBAAyB,CAAC,aAAa,EAAE,2BAA2B,CAAC;YACnE,yBAAyB,CACvB,KAAK,CAAC,OAAO,CAAC,KAAK,EACnB,6BAA6B,CAC9B,EACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,cAAc,CAAC;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU;QACpC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;QAC5B,KAAK,EAAE,KAAK,CAAC,QAAQ;KACtB,CAAC,CAAC;AACL,CAAC;AAcD,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAA2D;IAE3D,IAAI,KAAK,CAAC,qBAAqB,KAAK,SAAS;QAC3C,OAAO,2BAA2B,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,8BAA8B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IAClC,IAAI,CAAC,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,cAAc,CAAC;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU;QACpC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;QAC5B,KAAK,EAAE;YACL,GAAG,KAAK;YACR,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS;YACvC,SAAS,EAAE,KAAK,CAAC,QAAQ;SAC1B;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAKnD;IAIC,MAAM,8BAA8B,CAAC,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC;IACrD,IACE,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,OAAO;QAChD,KAAK,CAAC,QAAQ,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QACtD,KAAK,CAAC,QAAQ,CAAC,SAAS;QACxB,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,KAAK,KAAK,CAAC,SAAS;QAC1D,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;QACrD,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;QACvD,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc;QACnE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW;QAC7D,KAAK,CAAC,aAAa,KAAK,OAAO;QAC/B,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,OAAO,CAAC,OAAO;QAC3C,KAAK,CAAC,OAAO,KAAK,CAAC;QACnB,KAAK,CAAC,aAAa,KAAK,IAAI,EAC5B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,WAAW,GAAG;QAClB,GAAG,KAAK;QACR,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;QACnC,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;IACF,MAAM,MAAM,GAAkC;QAC5C,sBAAsB,EAAE;YACtB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,eAAe;YAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ;YACrB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B;QACD,cAAc,EAAE;YACd,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,gBAAgB;YACpD,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,SAAS;SACpC;QACD,iBAAiB,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,UAAU;QAC9D,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM;QACtD,YAAY,EAAE,WAAW;QACzB,cAAc,EAAE,EAAE;KACnB,CAAC;IACF,OAAO;QACL,MAAM;QACN,MAAM,EAAE,cAAc,CAAC;YACrB,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,UAAU;YACvD,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM;YAC/C,KAAK,EAAE,WAAW;SACnB,CAAC;KACH,CAAC;AACJ,CAAC","sourcesContent":["import {\n computePrincipalContainerGrantRoot,\n computePrincipalMemberEnvelopesRoot,\n computePrincipalMembershipRoot,\n computePrincipalProjectionRoot,\n computePrincipalStatePayloadCiphertextHash,\n makeVerifiedPrincipalPolicy,\n normalizePrincipalProjectionMembers,\n type ReferencedPrincipalHead,\n type VerifiedPrincipalPolicy,\n type VerifiedPrincipalPolicyCurrent,\n} from \"@tearleads/crypto\";\nimport type {\n CreateOrganizationGroupRequest,\n PutPrincipalPolicyRequest,\n} from \"@tearleads/validators/request\";\nimport type {\n OrganizationGroupSummaryResponse,\n PrincipalPolicyBundleResponse,\n PrincipalPolicyMutationResponse,\n PrincipalStateResponse,\n} from \"@tearleads/validators/response\";\nimport { canonicalKeyingJsonString } from \"../../data/keyingCanonicalJson\";\nimport {\n type CurrentPolicyMutationInput,\n verifyCurrentPolicyMutation,\n} from \"./currentPolicyMutationAcknowledgement\";\nimport { assertPrincipalPolicyReceiptArtifacts } from \"./principalPolicyReceiptArtifacts\";\n\nexport { assertGroupPolicyEnvelopesMatchAcknowledgement } from \"./principalPolicyReceiptArtifacts\";\n\nfunction historyWithCurrent(\n bundle: PrincipalPolicyBundleResponse,\n): PrincipalPolicyBundleResponse[\"previousStates\"] {\n return [\n ...bundle.previousStates,\n {\n state: bundle.currentState,\n projection: bundle.currentProjection,\n grants: bundle.currentGrants,\n },\n ];\n}\n\nfunction isAuthoredSuccessorConsistent(\n state: Pick<\n PrincipalStateResponse,\n | \"keyEpoch\"\n | \"keyFingerprint\"\n | \"prevStateHash\"\n | \"principalId\"\n | \"principalType\"\n | \"version\"\n >,\n expectedHead: ReferencedPrincipalHead,\n previous: PrincipalStateResponse,\n): boolean {\n return (\n state.principalType === expectedHead.principalType &&\n state.principalId === expectedHead.principalId &&\n state.version === expectedHead.version &&\n state.keyEpoch === expectedHead.keyEpoch &&\n state.keyFingerprint === expectedHead.keyFingerprint &&\n state.principalType === previous.principalType &&\n state.principalId === previous.principalId &&\n state.version === previous.version + 1 &&\n state.prevStateHash === previous.stateHash\n );\n}\n\nexport function buildAcknowledgedGroupPolicyBundle(input: {\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly request: PutPrincipalPolicyRequest;\n readonly response: PrincipalPolicyMutationResponse;\n}): PrincipalPolicyBundleResponse & PrincipalPolicyMutationResponse {\n assertPrincipalPolicyReceiptArtifacts(input);\n return {\n ...input.response,\n previousStates: historyWithCurrent(input.currentPolicy),\n };\n}\n\nasync function assertPolicyRequestCommitments(\n request: PutPrincipalPolicyRequest,\n): Promise<void> {\n const projection = normalizePrincipalProjectionMembers(request.projection);\n const members = projection.map((member) => ({ userId: member.userId }));\n const [\n membershipRoot,\n memberEnvelopesRoot,\n projectionRoot,\n payloadCiphertextHash,\n grantRoot,\n ] = await Promise.all([\n computePrincipalMembershipRoot(members),\n computePrincipalMemberEnvelopesRoot(request.memberEnvelopes),\n computePrincipalProjectionRoot(projection),\n computePrincipalStatePayloadCiphertextHash(\n request.encryptedPayload.ciphertext,\n ),\n computePrincipalContainerGrantRoot(request.grants),\n ]);\n if (\n request.state.membershipRoot !== membershipRoot ||\n request.state.memberEnvelopesRoot !== memberEnvelopesRoot ||\n request.state.projectionRoot !== projectionRoot ||\n request.state.memberCount !== projection.length ||\n request.state.grantRoot !== grantRoot ||\n request.state.grantCount !== request.grants.length ||\n request.encryptedPayload.ciphertextHash !== payloadCiphertextHash ||\n request.state.payloadCiphertextHash !== payloadCiphertextHash\n ) {\n throw new Error(\"Authored group policy commitments are invalid\");\n }\n}\n\nfunction verifiedPolicy(input: {\n currentPolicy?: PrincipalPolicyBundleResponse | undefined;\n projection: PutPrincipalPolicyRequest[\"projection\"];\n grants: PutPrincipalPolicyRequest[\"grants\"];\n state: PrincipalStateResponse;\n}): VerifiedPrincipalPolicy {\n const previousStates = input.currentPolicy\n ? historyWithCurrent(input.currentPolicy)\n : [];\n return makeVerifiedPrincipalPolicy({\n checkpoint: {\n principalId: input.state.principalId,\n principalType: input.state.principalType,\n stateHash: input.state.stateHash,\n version: input.state.version,\n },\n history: [\n ...previousStates,\n {\n state: input.state,\n projection: input.projection,\n grants: input.grants,\n },\n ],\n keyEpoch: input.state.keyEpoch,\n principalId: input.state.principalId,\n principalType: input.state.principalType,\n projection: input.projection,\n grants: input.grants,\n state: input.state,\n stateHash: input.state.stateHash,\n version: input.state.version,\n });\n}\n\ninterface FullPolicyMutationInput {\n readonly verifiedCurrentPolicy?: never;\n readonly currentPolicy: PrincipalPolicyBundleResponse;\n readonly expectedHead: ReferencedPrincipalHead;\n readonly request: PutPrincipalPolicyRequest;\n}\n\nexport function acknowledgeGroupPolicyState(\n input: CurrentPolicyMutationInput & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicyCurrent>;\nexport function acknowledgeGroupPolicyState(\n input: FullPolicyMutationInput & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicy>;\nexport async function acknowledgeGroupPolicyState(\n input: (FullPolicyMutationInput | CurrentPolicyMutationInput) & {\n readonly response: PrincipalStateResponse;\n },\n): Promise<VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent> {\n if (input.verifiedCurrentPolicy !== undefined)\n return verifyCurrentPolicyMutation(input, input.response);\n await assertPolicyRequestCommitments(input.request);\n const { createdAt: _createdAt, stateHash, ...responseState } = input.response;\n const previous = input.currentPolicy.currentState;\n if (\n stateHash !== input.expectedHead.stateHash ||\n !isAuthoredSuccessorConsistent(\n input.response,\n input.expectedHead,\n previous,\n ) ||\n canonicalKeyingJsonString(responseState, \"stored group policy state\") !==\n canonicalKeyingJsonString(\n input.request.state,\n \"authored group policy state\",\n )\n ) {\n throw new Error(\"Group policy state acknowledgement mismatch\");\n }\n return verifiedPolicy({\n currentPolicy: input.currentPolicy,\n projection: input.request.projection,\n grants: input.request.grants,\n state: input.response,\n });\n}\n\n/**\n * Materialize the locally signed successor before submission so dependent\n * container mutations can wrap to the exact head that will commit with it.\n * The server still performs the authoritative signature, transition, and\n * authorization checks inside the combined transaction.\n */\nexport function prepareAuthoredGroupPolicy(\n input: CurrentPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicyCurrent>;\nexport function prepareAuthoredGroupPolicy(\n input: FullPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicy>;\nexport async function prepareAuthoredGroupPolicy(\n input: FullPolicyMutationInput | CurrentPolicyMutationInput,\n): Promise<VerifiedPrincipalPolicy | VerifiedPrincipalPolicyCurrent> {\n if (input.verifiedCurrentPolicy !== undefined)\n return verifyCurrentPolicyMutation(input);\n await assertPolicyRequestCommitments(input.request);\n const previous = input.currentPolicy.currentState;\n const state = input.request.state;\n if (!isAuthoredSuccessorConsistent(state, input.expectedHead, previous)) {\n throw new Error(\"Authored group policy successor is inconsistent\");\n }\n return verifiedPolicy({\n currentPolicy: input.currentPolicy,\n projection: input.request.projection,\n grants: input.request.grants,\n state: {\n ...state,\n stateHash: input.expectedHead.stateHash,\n createdAt: state.signedAt,\n },\n });\n}\n\nexport async function acknowledgeInitialGroupPolicy(input: {\n readonly organizationId: string;\n readonly request: CreateOrganizationGroupRequest;\n readonly response: OrganizationGroupSummaryResponse;\n readonly stateHash: string;\n}): Promise<{\n readonly bundle: PrincipalPolicyBundleResponse;\n readonly policy: VerifiedPrincipalPolicy;\n}> {\n await assertPolicyRequestCommitments(input.request.initialGroupPolicy);\n const state = input.request.initialGroupPolicy.state;\n if (\n input.response.groupId !== input.request.groupId ||\n input.response.organizationId !== input.organizationId ||\n input.response.isBuiltin ||\n input.response.currentState?.stateHash !== input.stateHash ||\n input.response.currentState.version !== state.version ||\n input.response.currentState.keyEpoch !== state.keyEpoch ||\n input.response.currentState.keyFingerprint !== state.keyFingerprint ||\n input.response.currentState.memberCount !== state.memberCount ||\n state.principalType !== \"group\" ||\n state.principalId !== input.request.groupId ||\n state.version !== 1 ||\n state.prevStateHash !== null\n ) {\n throw new Error(\"Created group policy acknowledgement mismatch\");\n }\n const storedState = {\n ...state,\n createdAt: input.response.createdAt,\n stateHash: input.stateHash,\n };\n const bundle: PrincipalPolicyBundleResponse = {\n currentMemberEnvelopes: {\n envelopes: input.request.initialGroupPolicy.memberEnvelopes,\n epoch: state.keyEpoch,\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: input.stateHash,\n },\n currentPayload: {\n principalId: state.principalId,\n principalType: state.principalType,\n stateHash: input.stateHash,\n ...input.request.initialGroupPolicy.encryptedPayload,\n createdAt: input.response.createdAt,\n },\n currentProjection: input.request.initialGroupPolicy.projection,\n currentGrants: input.request.initialGroupPolicy.grants,\n currentState: storedState,\n previousStates: [],\n };\n return {\n bundle,\n policy: verifiedPolicy({\n projection: input.request.initialGroupPolicy.projection,\n grants: input.request.initialGroupPolicy.grants,\n state: storedState,\n }),\n };\n}\n"]}
@@ -66,6 +66,24 @@ export declare function loadGroupPolicyMutationContext(input: {
66
66
  readonly signingFingerprint: string;
67
67
  readonly signingKeyPair: SigningKeyPair;
68
68
  }): Promise<LoadedGroupPolicyMutationContext>;
69
+ /**
70
+ * Commit, answering one refusal: a rematerialized rekey or revoke that would
71
+ * strand a level above a directly granted container is refused with the
72
+ * descendant rekeys it must carry. The refused attempt rolled back whole, so
73
+ * the served heads still stand; the batch is signed again with the named
74
+ * levels woven in, replaces the container mutations, and the commit is retried
75
+ * once. A second refusal means the tree moved underneath, and the caller's own
76
+ * retry starts from a fresh policy.
77
+ */
78
+ export declare function submitGroupPolicyCommit(input: {
79
+ readonly apiClient: PrincipalPolicyReadWriteApi;
80
+ readonly carryDescendantRekeys?: ((requiredContainerIds: readonly string[]) => Promise<readonly ContainerMutationRequest[]>) | undefined;
81
+ readonly groupId: string;
82
+ readonly organizationId: string;
83
+ readonly organizationRequest: PutPrincipalPolicyRequest;
84
+ readonly request: PutPrincipalPolicyRequest;
85
+ readonly stillCurrent?: (() => boolean) | undefined;
86
+ }): Promise<CommitOrganizationGroupPolicyResponse | null>;
69
87
  export declare function commitGroupPolicyMutation(input: {
70
88
  readonly retiredContainerIds?: (() => readonly string[]) | undefined;
71
89
  readonly apiClient: PrincipalPolicyReadWriteApi;
@@ -120,7 +120,7 @@ export async function loadGroupPolicyMutationContext(input) {
120
120
  * once. A second refusal means the tree moved underneath, and the caller's own
121
121
  * retry starts from a fresh policy.
122
122
  */
123
- async function submitGroupPolicyCommit(input) {
123
+ export async function submitGroupPolicyCommit(input) {
124
124
  const body = () => ({
125
125
  groupPolicy: input.request,
126
126
  organizationPolicy: input.organizationRequest,