@tearleads/client-sdk 0.1.105 → 0.1.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Tearleads.js +5 -3
- package/Tearleads.js.map +1 -1
- package/client/blobs/index.d.ts +4 -0
- package/client/blobs/index.js +26 -7
- package/client/blobs/index.js.map +1 -1
- package/client/identity/index.d.ts +11 -2
- package/client/identity/index.js +80 -72
- package/client/identity/index.js.map +1 -1
- package/client/organizations/index.js +5 -2
- package/client/organizations/index.js.map +1 -1
- package/client/organizations/organizationMemberships.js +26 -18
- package/client/organizations/organizationMemberships.js.map +1 -1
- package/client/organizations/organizationReadModels.d.ts +9 -4
- package/client/organizations/organizationReadModels.js +20 -9
- package/client/organizations/organizationReadModels.js.map +1 -1
- package/client/session/index.js +6 -3
- package/client/session/index.js.map +1 -1
- package/client/session/sessionTypes.d.ts +1 -1
- package/client/session/sessionTypes.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/internal/api-client/ApiClient.d.ts +7 -0
- package/internal/crypto/organizationLeaveRequest.d.ts +1 -1
- package/internal/crypto/organizationLeaveRequest.js +9 -2
- package/internal/crypto/organizationLeaveRequest.js.map +1 -1
- package/internal/validators/operation/organizationMemberships.d.ts +7 -0
- package/internal/validators/operation/organizations.d.ts +56 -0
- package/internal/validators/response/organization.d.ts +91 -0
- package/internal/validators/response/organizationReadModel.d.ts +70 -0
- package/internal/validators/util/index.d.ts +1 -1
- package/internal/validators/util/index.js.map +1 -1
- package/internal/validators/util/organizationLeaveRequest.d.ts +25 -2
- package/internal/validators/util/organizationLeaveRequest.js +19 -3
- package/internal/validators/util/organizationLeaveRequest.js.map +1 -1
- package/package.json +1 -1
- package/workflows/organizations/memberships/leaveRequest.d.ts +6 -2
- package/workflows/organizations/memberships/leaveRequest.js +20 -5
- package/workflows/organizations/memberships/leaveRequest.js.map +1 -1
- package/workflows/organizations/memberships/leaveRequestMembersEvidence.js +80 -0
- package/workflows/organizations/memberships/leaveRequestMembersEvidence.js.map +1 -0
- package/workflows/organizations/memberships/leaveRequestMembership.d.ts +24 -0
- package/workflows/organizations/memberships/leaveRequestMembership.js +65 -0
- package/workflows/organizations/memberships/leaveRequestMembership.js.map +1 -0
- package/workflows/organizations/memberships/leaveRequestVerification.d.ts +15 -7
- package/workflows/organizations/memberships/leaveRequestVerification.js +18 -11
- package/workflows/organizations/memberships/leaveRequestVerification.js.map +1 -1
|
@@ -15,6 +15,13 @@ declare const OrganizationReadModelDirectoryResponseSchema: z.ZodObject<{
|
|
|
15
15
|
organizationId: string;
|
|
16
16
|
userId: string;
|
|
17
17
|
requestedAt: string;
|
|
18
|
+
membersHead: {
|
|
19
|
+
principalId: string;
|
|
20
|
+
version: number;
|
|
21
|
+
keyEpoch: number;
|
|
22
|
+
stateHash: string;
|
|
23
|
+
keyFingerprint: string;
|
|
24
|
+
};
|
|
18
25
|
signature: string;
|
|
19
26
|
} | null;
|
|
20
27
|
profileDocumentId: string | null;
|
|
@@ -36,6 +43,13 @@ declare const OrganizationReadModelDirectoryResponseSchema: z.ZodObject<{
|
|
|
36
43
|
organizationId: string;
|
|
37
44
|
userId: string;
|
|
38
45
|
requestedAt: string;
|
|
46
|
+
membersHead: {
|
|
47
|
+
principalId: string;
|
|
48
|
+
version: number;
|
|
49
|
+
keyEpoch: number;
|
|
50
|
+
stateHash: string;
|
|
51
|
+
keyFingerprint: string;
|
|
52
|
+
};
|
|
39
53
|
signature: string;
|
|
40
54
|
} | null;
|
|
41
55
|
profileDocumentId: string | null;
|
|
@@ -156,6 +170,13 @@ declare const OrganizationReadModelSnapshotResponseSchema: z.ZodObject<{
|
|
|
156
170
|
organizationId: string;
|
|
157
171
|
userId: string;
|
|
158
172
|
requestedAt: string;
|
|
173
|
+
membersHead: {
|
|
174
|
+
principalId: string;
|
|
175
|
+
version: number;
|
|
176
|
+
keyEpoch: number;
|
|
177
|
+
stateHash: string;
|
|
178
|
+
keyFingerprint: string;
|
|
179
|
+
};
|
|
159
180
|
signature: string;
|
|
160
181
|
} | null;
|
|
161
182
|
profileDocumentId: string | null;
|
|
@@ -177,6 +198,13 @@ declare const OrganizationReadModelSnapshotResponseSchema: z.ZodObject<{
|
|
|
177
198
|
organizationId: string;
|
|
178
199
|
userId: string;
|
|
179
200
|
requestedAt: string;
|
|
201
|
+
membersHead: {
|
|
202
|
+
principalId: string;
|
|
203
|
+
version: number;
|
|
204
|
+
keyEpoch: number;
|
|
205
|
+
stateHash: string;
|
|
206
|
+
keyFingerprint: string;
|
|
207
|
+
};
|
|
180
208
|
signature: string;
|
|
181
209
|
} | null;
|
|
182
210
|
profileDocumentId: string | null;
|
|
@@ -309,6 +337,13 @@ declare const OrganizationReadModelDeltaResponseSchema: z.ZodObject<{
|
|
|
309
337
|
organizationId: string;
|
|
310
338
|
userId: string;
|
|
311
339
|
requestedAt: string;
|
|
340
|
+
membersHead: {
|
|
341
|
+
principalId: string;
|
|
342
|
+
version: number;
|
|
343
|
+
keyEpoch: number;
|
|
344
|
+
stateHash: string;
|
|
345
|
+
keyFingerprint: string;
|
|
346
|
+
};
|
|
312
347
|
signature: string;
|
|
313
348
|
} | null;
|
|
314
349
|
profileDocumentId: string | null;
|
|
@@ -330,6 +365,13 @@ declare const OrganizationReadModelDeltaResponseSchema: z.ZodObject<{
|
|
|
330
365
|
organizationId: string;
|
|
331
366
|
userId: string;
|
|
332
367
|
requestedAt: string;
|
|
368
|
+
membersHead: {
|
|
369
|
+
principalId: string;
|
|
370
|
+
version: number;
|
|
371
|
+
keyEpoch: number;
|
|
372
|
+
stateHash: string;
|
|
373
|
+
keyFingerprint: string;
|
|
374
|
+
};
|
|
333
375
|
signature: string;
|
|
334
376
|
} | null;
|
|
335
377
|
profileDocumentId: string | null;
|
|
@@ -463,6 +505,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
|
|
|
463
505
|
organizationId: string;
|
|
464
506
|
userId: string;
|
|
465
507
|
requestedAt: string;
|
|
508
|
+
membersHead: {
|
|
509
|
+
principalId: string;
|
|
510
|
+
version: number;
|
|
511
|
+
keyEpoch: number;
|
|
512
|
+
stateHash: string;
|
|
513
|
+
keyFingerprint: string;
|
|
514
|
+
};
|
|
466
515
|
signature: string;
|
|
467
516
|
} | null;
|
|
468
517
|
profileDocumentId: string | null;
|
|
@@ -484,6 +533,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
|
|
|
484
533
|
organizationId: string;
|
|
485
534
|
userId: string;
|
|
486
535
|
requestedAt: string;
|
|
536
|
+
membersHead: {
|
|
537
|
+
principalId: string;
|
|
538
|
+
version: number;
|
|
539
|
+
keyEpoch: number;
|
|
540
|
+
stateHash: string;
|
|
541
|
+
keyFingerprint: string;
|
|
542
|
+
};
|
|
487
543
|
signature: string;
|
|
488
544
|
} | null;
|
|
489
545
|
profileDocumentId: string | null;
|
|
@@ -616,6 +672,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
|
|
|
616
672
|
organizationId: string;
|
|
617
673
|
userId: string;
|
|
618
674
|
requestedAt: string;
|
|
675
|
+
membersHead: {
|
|
676
|
+
principalId: string;
|
|
677
|
+
version: number;
|
|
678
|
+
keyEpoch: number;
|
|
679
|
+
stateHash: string;
|
|
680
|
+
keyFingerprint: string;
|
|
681
|
+
};
|
|
619
682
|
signature: string;
|
|
620
683
|
} | null;
|
|
621
684
|
profileDocumentId: string | null;
|
|
@@ -637,6 +700,13 @@ export declare const OrganizationReadModelResponseSchema: z.ZodDiscriminatedUnio
|
|
|
637
700
|
organizationId: string;
|
|
638
701
|
userId: string;
|
|
639
702
|
requestedAt: string;
|
|
703
|
+
membersHead: {
|
|
704
|
+
principalId: string;
|
|
705
|
+
version: number;
|
|
706
|
+
keyEpoch: number;
|
|
707
|
+
stateHash: string;
|
|
708
|
+
keyFingerprint: string;
|
|
709
|
+
};
|
|
640
710
|
signature: string;
|
|
641
711
|
} | null;
|
|
642
712
|
profileDocumentId: string | null;
|
|
@@ -6,7 +6,7 @@ export { MAX_CONTAINER_PATH_LENGTH } from "./containerLimits.js";
|
|
|
6
6
|
export { MAX_DOCUMENT_SYNC_AUTHORIZATION_PATH_REFS, MAX_DOCUMENT_SYNC_AUTHORIZATION_PATHS, MAX_DOCUMENT_SYNC_CONTENT_KEY_TARGETS, MAX_DOCUMENT_SYNC_OUTGOING_UPDATES, MAX_DOCUMENT_SYNC_PULL_CURSOR_LENGTH, MAX_DOCUMENT_SYNC_REQUEST_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_ENVELOPE_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_PAGE_BYTES, MAX_DOCUMENT_SYNC_RESPONSE_PAGE_UPDATES, MAX_DOCUMENT_SYNC_RESPONSE_UPDATE_PAGE_BYTES, } from "./documentSyncLimits.js";
|
|
7
7
|
export { isSerializedKeyEnvelope, isSerializedKeyEnvelopeArray, type SerializedKeyEnvelope, } from "./keyEnvelope.js";
|
|
8
8
|
export { KNOWN_MANIFEST_HINT_LIMIT, KnownManifestsHeadersSchema, parseKnownManifestHints, } from "./knownManifestWire.js";
|
|
9
|
-
export { isOrganizationLeaveRequest, type OrganizationLeaveRequest, OrganizationLeaveRequestSchema, } from "./organizationLeaveRequest.js";
|
|
9
|
+
export { isOrganizationLeaveRequest, type OrganizationLeaveRequest, type OrganizationLeaveRequestMembersHead, OrganizationLeaveRequestSchema, } from "./organizationLeaveRequest.js";
|
|
10
10
|
export { isOrganizationReplacementAuthorization, type OrganizationReplacementAuthorization, OrganizationReplacementAuthorizationSchema, } from "./organizationReplacement.js";
|
|
11
11
|
export { PRINCIPAL_POLICY_HISTORY_PAGE_LIMIT, PRINCIPAL_POLICY_REPAIR_HEAD_LIMIT, } from "./principalHistoryWire.js";
|
|
12
12
|
export { hasArrayProperty, hasBooleanProperty, hasNonEmptyStringProperty, hasNullableNumberProperty, hasNullableStringProperty, hasNumberProperty, hasObjectProperty, hasOptionalStringProperty, hasPositiveIntegerProperty, hasPropertyValue, hasStringProperty, isNonEmptyStringArray, isOptionalRecordArray, isOptionalRecordArrayArray, isRecordArray, isRecordArrayArray, isStringArray, } from "./properties.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mCAAmC,EAGnC,sCAAsC,EACtC,8BAA8B,EAC9B,+BAA+B,EAC/B,0BAA0B,EAC1B,+BAA+B,EAC/B,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAE9B,iCAAiC,EAEjC,6BAA6B,EAE7B,mCAAmC,EACnC,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,4BAA4B,EAC5B,uCAAuC,EACvC,mCAAmC,EACnC,mCAAmC,EACnC,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,8CAA8C,EAC9C,gDAAgD,EAChD,wCAAwC,EACxC,yCAAyC,EACzC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,qCAAqC,EACrC,kCAAkC,EAClC,oCAAoC,EACpC,+BAA+B,EAC/B,yCAAyC,EACzC,qCAAqC,EACrC,uCAAuC,EACvC,4CAA4C,GAC7C,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAE7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mCAAmC,EAGnC,sCAAsC,EACtC,8BAA8B,EAC9B,+BAA+B,EAC/B,0BAA0B,EAC1B,+BAA+B,EAC/B,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAE9B,iCAAiC,EAEjC,6BAA6B,EAE7B,mCAAmC,EACnC,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,4BAA4B,EAC5B,uCAAuC,EACvC,mCAAmC,EACnC,mCAAmC,EACnC,+BAA+B,EAC/B,uBAAuB,EACvB,2BAA2B,EAC3B,6BAA6B,EAC7B,8CAA8C,EAC9C,gDAAgD,EAChD,wCAAwC,EACxC,yCAAyC,EACzC,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,yCAAyC,EACzC,qCAAqC,EACrC,qCAAqC,EACrC,kCAAkC,EAClC,oCAAoC,EACpC,+BAA+B,EAC/B,yCAAyC,EACzC,qCAAqC,EACrC,uCAAuC,EACvC,4CAA4C,GAC7C,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,uBAAuB,EACvB,4BAA4B,GAE7B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAG1B,8BAA8B,GAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,sCAAsC,EAEtC,0CAA0C,GAC3C,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,mCAAmC,EACnC,kCAAkC,GACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export {\n type AccessEventBundleWireResponse,\n AccessEventBundleWireResponseSchema,\n type AccessManifestBundleWire,\n type AccessManifestBundleWireResponse,\n AccessManifestBundleWireResponseSchema,\n AccessManifestBundleWireSchema,\n isAccessEventBundleWireResponse,\n isAccessManifestBundleWire,\n isAccessManifestBundleWireArray,\n isAccessManifestBundleWireResponse,\n isOptionalAccessManifestBundleWireArray,\n} from \"./accessManifestBundle\";\nexport {\n MAX_INITIATE_PART_UPLOADS,\n MAX_MULTIPART_BLOB_PART_BYTES,\n} from \"./blob\";\nexport {\n blobPartContentMd5Schema,\n MAX_MULTIPART_BLOB_PART_NUMBER,\n type MultipartBlobPartDescriptor,\n MultipartBlobPartDescriptorSchema,\n type MultipartBlobPartUpload,\n MultipartBlobPartUploadSchema,\n type MultipartBlobPartUploadSource,\n MultipartBlobPartUploadSourceSchema,\n signedBlobUrlExpirySchema,\n signedBlobUrlSchema,\n} from \"./blobTransferSchemas\";\nexport {\n CONTAINER_KEK_LOG_PAGE_LIMIT,\n CONTAINER_KEK_LOG_PRINCIPAL_SCOPE_LIMIT,\n CONTAINER_KEK_WRAPS_PER_EPOCH_LIMIT,\n ContainerKekKeyringWireRecordSchema,\n isContainerKekKeyringWireRecord,\n MAX_CONTAINER_KEY_EPOCH,\n MAX_INLINE_CONTAINER_REKEYS,\n MAX_ROTATION_CONTAINER_REKEYS,\n MAX_SEALED_CONTAINER_KEK_KEYRING_BASE64_LENGTH,\n SEALED_CONTAINER_KEK_KEYRING_AEAD_OVERHEAD_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_ENTRY_BYTES,\n SEALED_CONTAINER_KEK_KEYRING_HEADER_BYTES,\n sealedContainerKekKeyringBytes,\n} from \"./containerKekKeyringWire\";\nexport { MAX_CONTAINER_PATH_LENGTH } from \"./containerLimits\";\nexport {\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATH_REFS,\n MAX_DOCUMENT_SYNC_AUTHORIZATION_PATHS,\n MAX_DOCUMENT_SYNC_CONTENT_KEY_TARGETS,\n MAX_DOCUMENT_SYNC_OUTGOING_UPDATES,\n MAX_DOCUMENT_SYNC_PULL_CURSOR_LENGTH,\n MAX_DOCUMENT_SYNC_REQUEST_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_ENVELOPE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_BYTES,\n MAX_DOCUMENT_SYNC_RESPONSE_PAGE_UPDATES,\n MAX_DOCUMENT_SYNC_RESPONSE_UPDATE_PAGE_BYTES,\n} from \"./documentSyncLimits\";\nexport {\n isSerializedKeyEnvelope,\n isSerializedKeyEnvelopeArray,\n type SerializedKeyEnvelope,\n} from \"./keyEnvelope\";\nexport {\n KNOWN_MANIFEST_HINT_LIMIT,\n KnownManifestsHeadersSchema,\n parseKnownManifestHints,\n} from \"./knownManifestWire\";\nexport {\n isOrganizationLeaveRequest,\n type OrganizationLeaveRequest,\n type OrganizationLeaveRequestMembersHead,\n OrganizationLeaveRequestSchema,\n} from \"./organizationLeaveRequest\";\nexport {\n isOrganizationReplacementAuthorization,\n type OrganizationReplacementAuthorization,\n OrganizationReplacementAuthorizationSchema,\n} from \"./organizationReplacement\";\nexport {\n PRINCIPAL_POLICY_HISTORY_PAGE_LIMIT,\n PRINCIPAL_POLICY_REPAIR_HEAD_LIMIT,\n} from \"./principalHistoryWire\";\nexport {\n hasArrayProperty,\n hasBooleanProperty,\n hasNonEmptyStringProperty,\n hasNullableNumberProperty,\n hasNullableStringProperty,\n hasNumberProperty,\n hasObjectProperty,\n hasOptionalStringProperty,\n hasPositiveIntegerProperty,\n hasPropertyValue,\n hasStringProperty,\n isNonEmptyStringArray,\n isOptionalRecordArray,\n isOptionalRecordArrayArray,\n isRecordArray,\n isRecordArrayArray,\n isStringArray,\n} from \"./properties\";\nexport {\n AUTH_CHALLENGE_HEX_LENGTH,\n isAuthChallengeHexString,\n isByteArray,\n isByteArrayOfLength,\n isSha256HexString,\n ML_DSA87_PUBLIC_KEY_BYTES,\n ML_DSA87_SIGNATURE_BYTES,\n ML_KEM1024_PUBLIC_KEY_BYTES,\n SHA256_HEX_LENGTH,\n} from \"./protocol\";\nexport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n MAX_SIGNED_AT_YEAR,\n MIN_SIGNED_AT_YEAR,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\nexport { isUuidV4String } from \"./uuid\";\nexport { isWalLsnString, parseWalLsn } from \"./walLsn\";\n"]}
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The exact signed head of the organization's reserved Members group that the
|
|
4
|
+
* member saw when asking. Removing anyone from Members rotates its key epoch,
|
|
5
|
+
* so a request stays current only while the member has stayed in Members
|
|
6
|
+
* since this head: a request from an earlier membership never is.
|
|
7
|
+
*/
|
|
8
|
+
export declare const OrganizationLeaveRequestMembersHeadSchema: z.ZodObject<{
|
|
9
|
+
principalId: z.ZodCustom<string, string>;
|
|
10
|
+
version: z.ZodCustom<number, number>;
|
|
11
|
+
keyEpoch: z.ZodCustom<number, number>;
|
|
12
|
+
stateHash: z.ZodString;
|
|
13
|
+
keyFingerprint: z.ZodString;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type OrganizationLeaveRequestMembersHead = z.infer<typeof OrganizationLeaveRequestMembersHeadSchema>;
|
|
2
16
|
/**
|
|
3
17
|
* A member's signed request to leave an organization. Signed with the member's
|
|
4
18
|
* identity key so an administrator can tell the request came from the member,
|
|
5
|
-
* not from the server
|
|
6
|
-
*
|
|
19
|
+
* not from the server, and bound to the Members head the member saw so it
|
|
20
|
+
* cannot outlive their membership. It authorizes nothing by itself: leaving
|
|
21
|
+
* still needs an administrator to sign the member's removal from the
|
|
22
|
+
* organization's groups.
|
|
7
23
|
*/
|
|
8
24
|
export declare const OrganizationLeaveRequestSchema: z.ZodObject<{
|
|
9
25
|
organizationId: z.ZodCustom<string, string>;
|
|
10
26
|
userId: z.ZodCustom<string, string>;
|
|
11
27
|
requestedAt: z.ZodString;
|
|
28
|
+
membersHead: z.ZodObject<{
|
|
29
|
+
principalId: z.ZodCustom<string, string>;
|
|
30
|
+
version: z.ZodCustom<number, number>;
|
|
31
|
+
keyEpoch: z.ZodCustom<number, number>;
|
|
32
|
+
stateHash: z.ZodString;
|
|
33
|
+
keyFingerprint: z.ZodString;
|
|
34
|
+
}, z.core.$strict>;
|
|
12
35
|
signature: z.ZodString;
|
|
13
36
|
}, z.core.$strict>;
|
|
14
37
|
export type OrganizationLeaveRequest = z.infer<typeof OrganizationLeaveRequestSchema>;
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerJsonSchemaFragment } from "../jsonSchema.js";
|
|
3
|
-
import { nonEmptyStringSchema, uuidV4StringSchema } from "../schema.js";
|
|
3
|
+
import { boundedPositiveIntegerSchema, nonEmptyStringSchema, uuidV4StringSchema, } from "../schema.js";
|
|
4
4
|
import { CANONICAL_SIGNED_AT_PATTERN, isCanonicalSignedAt, SIGNED_AT_CONTRACT, } from "./signedAt.js";
|
|
5
|
+
/**
|
|
6
|
+
* The exact signed head of the organization's reserved Members group that the
|
|
7
|
+
* member saw when asking. Removing anyone from Members rotates its key epoch,
|
|
8
|
+
* so a request stays current only while the member has stayed in Members
|
|
9
|
+
* since this head: a request from an earlier membership never is.
|
|
10
|
+
*/
|
|
11
|
+
export const OrganizationLeaveRequestMembersHeadSchema = z.strictObject({
|
|
12
|
+
principalId: uuidV4StringSchema,
|
|
13
|
+
version: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),
|
|
14
|
+
keyEpoch: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),
|
|
15
|
+
stateHash: nonEmptyStringSchema,
|
|
16
|
+
keyFingerprint: nonEmptyStringSchema,
|
|
17
|
+
});
|
|
5
18
|
/**
|
|
6
19
|
* A member's signed request to leave an organization. Signed with the member's
|
|
7
20
|
* identity key so an administrator can tell the request came from the member,
|
|
8
|
-
* not from the server
|
|
9
|
-
*
|
|
21
|
+
* not from the server, and bound to the Members head the member saw so it
|
|
22
|
+
* cannot outlive their membership. It authorizes nothing by itself: leaving
|
|
23
|
+
* still needs an administrator to sign the member's removal from the
|
|
24
|
+
* organization's groups.
|
|
10
25
|
*/
|
|
11
26
|
export const OrganizationLeaveRequestSchema = z.strictObject({
|
|
12
27
|
organizationId: uuidV4StringSchema,
|
|
@@ -14,6 +29,7 @@ export const OrganizationLeaveRequestSchema = z.strictObject({
|
|
|
14
29
|
requestedAt: registerJsonSchemaFragment(z.string().refine(isCanonicalSignedAt, {
|
|
15
30
|
message: `requestedAt must be ${SIGNED_AT_CONTRACT}`,
|
|
16
31
|
}), { pattern: CANONICAL_SIGNED_AT_PATTERN.source, type: "string" }),
|
|
32
|
+
membersHead: OrganizationLeaveRequestMembersHeadSchema,
|
|
17
33
|
signature: nonEmptyStringSchema,
|
|
18
34
|
});
|
|
19
35
|
export function isOrganizationLeaveRequest(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organizationLeaveRequest.js","sourceRoot":"","sources":["organizationLeaveRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,
|
|
1
|
+
{"version":3,"file":"organizationLeaveRequest.js","sourceRoot":"","sources":["organizationLeaveRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yCAAyC,GAAG,CAAC,CAAC,YAAY,CAAC;IACtE,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,4BAA4B,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC9D,QAAQ,EAAE,4BAA4B,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC/D,SAAS,EAAE,oBAAoB;IAC/B,cAAc,EAAE,oBAAoB;CACrC,CAAC,CAAC;AAMH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,YAAY,CAAC;IAC3D,cAAc,EAAE,kBAAkB;IAClC,MAAM,EAAE,kBAAkB;IAC1B,WAAW,EAAE,0BAA0B,CACrC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,mBAAmB,EAAE;QACrC,OAAO,EAAE,uBAAuB,kBAAkB,EAAE;KACrD,CAAC,EACF,EAAE,OAAO,EAAE,2BAA2B,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAChE;IACD,WAAW,EAAE,yCAAyC;IACtD,SAAS,EAAE,oBAAoB;CAChC,CAAC,CAAC;AAMH,MAAM,UAAU,0BAA0B,CACxC,KAAc;IAEd,OAAO,8BAA8B,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;AACjE,CAAC","sourcesContent":["import { z } from \"zod\";\nimport { registerJsonSchemaFragment } from \"../jsonSchema\";\nimport {\n boundedPositiveIntegerSchema,\n nonEmptyStringSchema,\n uuidV4StringSchema,\n} from \"../schema\";\nimport {\n CANONICAL_SIGNED_AT_PATTERN,\n isCanonicalSignedAt,\n SIGNED_AT_CONTRACT,\n} from \"./signedAt\";\n\n/**\n * The exact signed head of the organization's reserved Members group that the\n * member saw when asking. Removing anyone from Members rotates its key epoch,\n * so a request stays current only while the member has stayed in Members\n * since this head: a request from an earlier membership never is.\n */\nexport const OrganizationLeaveRequestMembersHeadSchema = z.strictObject({\n principalId: uuidV4StringSchema,\n version: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),\n keyEpoch: boundedPositiveIntegerSchema(Number.MAX_SAFE_INTEGER),\n stateHash: nonEmptyStringSchema,\n keyFingerprint: nonEmptyStringSchema,\n});\n\nexport type OrganizationLeaveRequestMembersHead = z.infer<\n typeof OrganizationLeaveRequestMembersHeadSchema\n>;\n\n/**\n * A member's signed request to leave an organization. Signed with the member's\n * identity key so an administrator can tell the request came from the member,\n * not from the server, and bound to the Members head the member saw so it\n * cannot outlive their membership. It authorizes nothing by itself: leaving\n * still needs an administrator to sign the member's removal from the\n * organization's groups.\n */\nexport const OrganizationLeaveRequestSchema = z.strictObject({\n organizationId: uuidV4StringSchema,\n userId: uuidV4StringSchema,\n requestedAt: registerJsonSchemaFragment(\n z.string().refine(isCanonicalSignedAt, {\n message: `requestedAt must be ${SIGNED_AT_CONTRACT}`,\n }),\n { pattern: CANONICAL_SIGNED_AT_PATTERN.source, type: \"string\" },\n ),\n membersHead: OrganizationLeaveRequestMembersHeadSchema,\n signature: nonEmptyStringSchema,\n});\n\nexport type OrganizationLeaveRequest = z.infer<\n typeof OrganizationLeaveRequestSchema\n>;\n\nexport function isOrganizationLeaveRequest(\n value: unknown,\n): value is OrganizationLeaveRequest {\n return OrganizationLeaveRequestSchema.safeParse(value).success;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RequestResult } from "../../../internal/api-client/index.js";
|
|
2
2
|
import { type SigningKeyPair } from "../../../internal/crypto/index.js";
|
|
3
3
|
import type { OrganizationMembershipResponse } from "../../../internal/validators/response/index.js";
|
|
4
|
-
import type { OrganizationLeaveRequest } from "../../../internal/validators/util/index.js";
|
|
4
|
+
import type { OrganizationLeaveRequest, OrganizationLeaveRequestMembersHead } from "../../../internal/validators/util/index.js";
|
|
5
5
|
import type { OrganizationMembership } from "./organizationMemberships.js";
|
|
6
6
|
export interface OrganizationLeaveRequestApi {
|
|
7
7
|
setOrganizationLeaveRequest(organizationId: string, request: OrganizationLeaveRequest | null): Promise<RequestResult<OrganizationMembershipResponse>>;
|
|
@@ -17,10 +17,14 @@ export type OrganizationLeaveRequestResult = {
|
|
|
17
17
|
/**
|
|
18
18
|
* Files or withdraws the user's request to leave one organization. Filing
|
|
19
19
|
* signs the request with the user's identity key, so administrators can tell
|
|
20
|
-
* the member made it
|
|
20
|
+
* the member made it, and binds it to the organization's Members head as this
|
|
21
|
+
* device verified it, so it cannot outlive this membership. It removes nothing
|
|
22
|
+
* until an administrator approves it.
|
|
21
23
|
*/
|
|
22
24
|
export declare function setOrganizationLeaveRequested(input: {
|
|
23
25
|
readonly apiClient: OrganizationLeaveRequestApi;
|
|
26
|
+
/** The organization's current Members head, freshly verified. */
|
|
27
|
+
readonly loadMembersHead: () => Promise<OrganizationLeaveRequestMembersHead>;
|
|
24
28
|
readonly organizationId: string;
|
|
25
29
|
readonly requested: boolean;
|
|
26
30
|
readonly signer: {
|
|
@@ -2,16 +2,31 @@ import { signOrganizationLeaveRequest, } from "../../../internal/crypto/index.js
|
|
|
2
2
|
/**
|
|
3
3
|
* Files or withdraws the user's request to leave one organization. Filing
|
|
4
4
|
* signs the request with the user's identity key, so administrators can tell
|
|
5
|
-
* the member made it
|
|
5
|
+
* the member made it, and binds it to the organization's Members head as this
|
|
6
|
+
* device verified it, so it cannot outlive this membership. It removes nothing
|
|
7
|
+
* until an administrator approves it.
|
|
6
8
|
*/
|
|
7
9
|
export async function setOrganizationLeaveRequested(input) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
let request = null;
|
|
11
|
+
if (input.requested) {
|
|
12
|
+
let membersHead;
|
|
13
|
+
try {
|
|
14
|
+
membersHead = await input.loadMembersHead();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {
|
|
18
|
+
httpStatus: null,
|
|
19
|
+
message: "Could not confirm your current membership. Try again.",
|
|
20
|
+
status: "failed",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
request = signOrganizationLeaveRequest({
|
|
24
|
+
membersHead,
|
|
10
25
|
organizationId: input.organizationId,
|
|
11
26
|
requestedAt: (input.now?.() ?? new Date()).toISOString(),
|
|
12
27
|
userId: input.signer.userId,
|
|
13
|
-
}, input.signer.signingKeyPair)
|
|
14
|
-
|
|
28
|
+
}, input.signer.signingKeyPair);
|
|
29
|
+
}
|
|
15
30
|
const result = await input.apiClient.setOrganizationLeaveRequest(input.organizationId, request);
|
|
16
31
|
return result.ok
|
|
17
32
|
? { membership: result.data, status: "updated" }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"leaveRequest.js","sourceRoot":"","sources":["leaveRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"leaveRequest.js","sourceRoot":"","sources":["leaveRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,4BAA4B,GAC7B,MAAM,mBAAmB,CAAC;AAuB3B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,KAWnD;IACC,IAAI,OAAO,GAAoC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,IAAI,WAAgD,CAAC;QACrD,IAAI,CAAC;YACH,WAAW,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,uDAAuD;gBAChE,MAAM,EAAE,QAAQ;aACjB,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,4BAA4B,CACpC;YACE,WAAW;YACX,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;YACxD,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;SAC5B,EACD,KAAK,CAAC,MAAM,CAAC,cAAc,CAC5B,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,2BAA2B,CAC9D,KAAK,CAAC,cAAc,EACpB,OAAO,CACR,CAAC;IACF,OAAO,MAAM,CAAC,EAAE;QACd,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE;QAChD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC/E,CAAC","sourcesContent":["import type { RequestResult } from \"@tearleads/api-client\";\nimport {\n type SigningKeyPair,\n signOrganizationLeaveRequest,\n} from \"@tearleads/crypto\";\nimport type { OrganizationMembershipResponse } from \"@tearleads/validators/response\";\nimport type {\n OrganizationLeaveRequest,\n OrganizationLeaveRequestMembersHead,\n} from \"@tearleads/validators/util\";\nimport type { OrganizationMembership } from \"./organizationMemberships\";\n\nexport interface OrganizationLeaveRequestApi {\n setOrganizationLeaveRequest(\n organizationId: string,\n request: OrganizationLeaveRequest | null,\n ): Promise<RequestResult<OrganizationMembershipResponse>>;\n}\n\nexport type OrganizationLeaveRequestResult =\n | { readonly status: \"updated\"; readonly membership: OrganizationMembership }\n | {\n readonly status: \"failed\";\n readonly httpStatus: number | null;\n readonly message: string;\n };\n\n/**\n * Files or withdraws the user's request to leave one organization. Filing\n * signs the request with the user's identity key, so administrators can tell\n * the member made it, and binds it to the organization's Members head as this\n * device verified it, so it cannot outlive this membership. It removes nothing\n * until an administrator approves it.\n */\nexport async function setOrganizationLeaveRequested(input: {\n readonly apiClient: OrganizationLeaveRequestApi;\n /** The organization's current Members head, freshly verified. */\n readonly loadMembersHead: () => Promise<OrganizationLeaveRequestMembersHead>;\n readonly organizationId: string;\n readonly requested: boolean;\n readonly signer: {\n readonly signingKeyPair: Pick<SigningKeyPair, \"signingPrivateKey\">;\n readonly userId: string;\n };\n readonly now?: () => Date;\n}): Promise<OrganizationLeaveRequestResult> {\n let request: OrganizationLeaveRequest | null = null;\n if (input.requested) {\n let membersHead: OrganizationLeaveRequestMembersHead;\n try {\n membersHead = await input.loadMembersHead();\n } catch {\n return {\n httpStatus: null,\n message: \"Could not confirm your current membership. Try again.\",\n status: \"failed\",\n };\n }\n request = signOrganizationLeaveRequest(\n {\n membersHead,\n organizationId: input.organizationId,\n requestedAt: (input.now?.() ?? new Date()).toISOString(),\n userId: input.signer.userId,\n },\n input.signer.signingKeyPair,\n );\n }\n const result = await input.apiClient.setOrganizationLeaveRequest(\n input.organizationId,\n request,\n );\n return result.ok\n ? { membership: result.data, status: \"updated\" }\n : { httpStatus: result.status, message: result.message, status: \"failed\" };\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ProjectionDependencyUnavailableError } from "../../../data/keyingProjectionVerification/dependencyUnavailable.js";
|
|
2
|
+
import { assertProjectionVerificationCurrent } from "../../../data/keyingProjectionVerification/types.js";
|
|
3
|
+
import { requireOrganizationGroupHead } from "../../../data/principals/organizationAuthorityDescriptor.js";
|
|
4
|
+
import { createRuntimePrincipalPolicyCurrentResolver, } from "../../principals/runtimePolicyRecovery.js";
|
|
5
|
+
import { loadCurrentOrganizationAuthority } from "../currentOrganizationAuthority.js";
|
|
6
|
+
import { loadLocalOrganizationPolicyReference } from "../readModel/localReadModelDetails.js";
|
|
7
|
+
import { createLeaveRequestMembershipCheck, } from "./leaveRequestMembership.js";
|
|
8
|
+
/**
|
|
9
|
+
* The organization's current Members head as the member's own device
|
|
10
|
+
* discovers and verifies it now, for a leave request to cite.
|
|
11
|
+
*/
|
|
12
|
+
export async function loadCurrentMembersHead(input) {
|
|
13
|
+
const resolveCurrentPolicy = createRuntimePrincipalPolicyCurrentResolver(input.runtime);
|
|
14
|
+
if (!resolveCurrentPolicy)
|
|
15
|
+
throw new ProjectionDependencyUnavailableError("Asking to leave requires private paged recovery");
|
|
16
|
+
const authority = await loadCurrentOrganizationAuthority({
|
|
17
|
+
execSql: input.runtime.infra.execSql,
|
|
18
|
+
organizationId: input.organizationId,
|
|
19
|
+
resolveCurrentPolicy,
|
|
20
|
+
stillCurrent: input.stillCurrent,
|
|
21
|
+
});
|
|
22
|
+
assertProjectionVerificationCurrent(authority.stillCurrent);
|
|
23
|
+
const head = requireOrganizationGroupHead(authority.descriptor, authority.descriptor.memberGroupId);
|
|
24
|
+
return {
|
|
25
|
+
keyEpoch: head.keyEpoch,
|
|
26
|
+
keyFingerprint: head.keyFingerprint,
|
|
27
|
+
principalId: head.principalId,
|
|
28
|
+
stateHash: head.stateHash,
|
|
29
|
+
version: head.version,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Checks leave requests against the Members head in the organization policy
|
|
34
|
+
* this device's read model selected, verified exactly through the private
|
|
35
|
+
* paged verifier (from local evidence when it has it), and walks verified
|
|
36
|
+
* Members history the same way when a request cites an earlier key epoch.
|
|
37
|
+
*/
|
|
38
|
+
export function createRuntimeLeaveRequestMembershipCheck(input) {
|
|
39
|
+
const resolve = createRuntimePrincipalPolicyCurrentResolver(input.runtime);
|
|
40
|
+
return createLeaveRequestMembershipCheck({
|
|
41
|
+
async loadMembersHead() {
|
|
42
|
+
if (!resolve)
|
|
43
|
+
return null;
|
|
44
|
+
const organizationReference = await loadLocalOrganizationPolicyReference({
|
|
45
|
+
currentUserId: input.currentUserId,
|
|
46
|
+
execSql: input.runtime.infra.execSql,
|
|
47
|
+
organizationId: input.organizationId,
|
|
48
|
+
principalId: input.organizationId,
|
|
49
|
+
principalType: "organization",
|
|
50
|
+
});
|
|
51
|
+
if (!organizationReference)
|
|
52
|
+
return null;
|
|
53
|
+
const authority = await loadCurrentOrganizationAuthority({
|
|
54
|
+
execSql: input.runtime.infra.execSql,
|
|
55
|
+
organizationId: input.organizationId,
|
|
56
|
+
organizationReference,
|
|
57
|
+
resolveCurrentPolicy: resolve,
|
|
58
|
+
stillCurrent: input.stillCurrent,
|
|
59
|
+
});
|
|
60
|
+
assertProjectionVerificationCurrent(authority.stillCurrent);
|
|
61
|
+
return requireOrganizationGroupHead(authority.descriptor, authority.descriptor.memberGroupId);
|
|
62
|
+
},
|
|
63
|
+
loadHistoryPage: resolve
|
|
64
|
+
? async (head, beforeVersion) => {
|
|
65
|
+
const resolved = await resolve({
|
|
66
|
+
organizationId: input.organizationId,
|
|
67
|
+
reference: head,
|
|
68
|
+
preferLocalCurrent: true,
|
|
69
|
+
stillCurrent: input.stillCurrent,
|
|
70
|
+
historyPage: beforeVersion === undefined ? {} : { beforeVersion },
|
|
71
|
+
});
|
|
72
|
+
assertProjectionVerificationCurrent(resolved.stillCurrent);
|
|
73
|
+
if (!resolved.historyPage)
|
|
74
|
+
throw new Error("Verified Members history page is missing");
|
|
75
|
+
return resolved.historyPage;
|
|
76
|
+
}
|
|
77
|
+
: undefined,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=leaveRequestMembersEvidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaveRequestMembersEvidence.js","sourceRoot":"","sources":["leaveRequestMembersEvidence.ts"],"names":[],"mappings":"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","sourcesContent":["import type { OrganizationLeaveRequestMembersHead } from \"@tearleads/validators/util\";\nimport { ProjectionDependencyUnavailableError } from \"../../../data/keyingProjectionVerification/dependencyUnavailable\";\nimport { assertProjectionVerificationCurrent } from \"../../../data/keyingProjectionVerification/types\";\nimport { requireOrganizationGroupHead } from \"../../../data/principals/organizationAuthorityDescriptor\";\nimport {\n createRuntimePrincipalPolicyCurrentResolver,\n type PrincipalPolicyRecoveryRuntime,\n} from \"../../principals/runtimePolicyRecovery\";\nimport { loadCurrentOrganizationAuthority } from \"../currentOrganizationAuthority\";\nimport { loadLocalOrganizationPolicyReference } from \"../readModel/localReadModelDetails\";\nimport {\n createLeaveRequestMembershipCheck,\n type LeaveRequestMembershipCheck,\n} from \"./leaveRequestMembership\";\n\ninterface MembersEvidenceInput {\n readonly runtime: PrincipalPolicyRecoveryRuntime;\n readonly organizationId: string;\n readonly stillCurrent: () => boolean;\n}\n\n/**\n * The organization's current Members head as the member's own device\n * discovers and verifies it now, for a leave request to cite.\n */\nexport async function loadCurrentMembersHead(\n input: MembersEvidenceInput,\n): Promise<OrganizationLeaveRequestMembersHead> {\n const resolveCurrentPolicy = createRuntimePrincipalPolicyCurrentResolver(\n input.runtime,\n );\n if (!resolveCurrentPolicy)\n throw new ProjectionDependencyUnavailableError(\n \"Asking to leave requires private paged recovery\",\n );\n const authority = await loadCurrentOrganizationAuthority({\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n resolveCurrentPolicy,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(authority.stillCurrent);\n const head = requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.memberGroupId,\n );\n return {\n keyEpoch: head.keyEpoch,\n keyFingerprint: head.keyFingerprint,\n principalId: head.principalId,\n stateHash: head.stateHash,\n version: head.version,\n };\n}\n\n/**\n * Checks leave requests against the Members head in the organization policy\n * this device's read model selected, verified exactly through the private\n * paged verifier (from local evidence when it has it), and walks verified\n * Members history the same way when a request cites an earlier key epoch.\n */\nexport function createRuntimeLeaveRequestMembershipCheck(\n input: MembersEvidenceInput & { readonly currentUserId: string },\n): LeaveRequestMembershipCheck {\n const resolve = createRuntimePrincipalPolicyCurrentResolver(input.runtime);\n return createLeaveRequestMembershipCheck({\n async loadMembersHead() {\n if (!resolve) return null;\n const organizationReference = await loadLocalOrganizationPolicyReference({\n currentUserId: input.currentUserId,\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n principalId: input.organizationId,\n principalType: \"organization\",\n });\n if (!organizationReference) return null;\n const authority = await loadCurrentOrganizationAuthority({\n execSql: input.runtime.infra.execSql,\n organizationId: input.organizationId,\n organizationReference,\n resolveCurrentPolicy: resolve,\n stillCurrent: input.stillCurrent,\n });\n assertProjectionVerificationCurrent(authority.stillCurrent);\n return requireOrganizationGroupHead(\n authority.descriptor,\n authority.descriptor.memberGroupId,\n );\n },\n loadHistoryPage: resolve\n ? async (head, beforeVersion) => {\n const resolved = await resolve({\n organizationId: input.organizationId,\n reference: head,\n preferLocalCurrent: true,\n stillCurrent: input.stillCurrent,\n historyPage: beforeVersion === undefined ? {} : { beforeVersion },\n });\n assertProjectionVerificationCurrent(resolved.stillCurrent);\n if (!resolved.historyPage)\n throw new Error(\"Verified Members history page is missing\");\n return resolved.historyPage;\n }\n : undefined,\n });\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReferencedPrincipalHead } from "../../../internal/crypto/index.js";
|
|
2
|
+
import type { OrganizationLeaveRequest } from "../../../internal/validators/util/index.js";
|
|
3
|
+
import type { RecoveredPrincipalHistoryPage } from "../../principals/historyRecovery/loadRecoveredPrincipalHistoryPage.js";
|
|
4
|
+
export interface LeaveRequestMembershipEvidence {
|
|
5
|
+
/** The Members head this device has verified, or null when it has none. */
|
|
6
|
+
readonly loadMembersHead: () => Promise<ReferencedPrincipalHead | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Verified Members history ending at `head`, one page at a time and in
|
|
9
|
+
* ascending order. Absent where this device cannot read history.
|
|
10
|
+
*/
|
|
11
|
+
readonly loadHistoryPage?: ((head: ReferencedPrincipalHead, beforeVersion: number | undefined) => Promise<RecoveredPrincipalHistoryPage>) | undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Whether a leave request still belongs to its member's current membership. */
|
|
14
|
+
export type LeaveRequestMembershipCheck = (request: OrganizationLeaveRequest) => Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Checks each leave request against this device's verified Members head.
|
|
17
|
+
* Removing anyone from Members rotates its key epoch and key, so a request
|
|
18
|
+
* citing the head's own epoch and key has seen no removal since. One citing an earlier epoch
|
|
19
|
+
* stands only if verified history shows its member in every Members state
|
|
20
|
+
* from the cited one on; a request from a membership a removal ended never
|
|
21
|
+
* does, whatever the server serves beside it. Display only: a request this
|
|
22
|
+
* device cannot place is shown as no request.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createLeaveRequestMembershipCheck(evidence: LeaveRequestMembershipEvidence): LeaveRequestMembershipCheck;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// How far back one request's check walks verified Members history: a request
|
|
2
|
+
// stays pending for days, not for hundreds of Members changes.
|
|
3
|
+
const MAX_MEMBERS_HISTORY_PAGES = 8;
|
|
4
|
+
function citesState(request, state) {
|
|
5
|
+
const cited = request.membersHead;
|
|
6
|
+
return (state.version === cited.version &&
|
|
7
|
+
state.stateHash === cited.stateHash &&
|
|
8
|
+
state.keyEpoch === cited.keyEpoch &&
|
|
9
|
+
state.keyFingerprint === cited.keyFingerprint);
|
|
10
|
+
}
|
|
11
|
+
// Walks verified Members history back from the head to the cited state,
|
|
12
|
+
// requiring the member in every state on the way.
|
|
13
|
+
async function memberStayedSinceCitedState(request, head, loadHistoryPage) {
|
|
14
|
+
let expectedVersion = head.version;
|
|
15
|
+
let beforeVersion;
|
|
16
|
+
for (let pages = 0; pages < MAX_MEMBERS_HISTORY_PAGES; pages += 1) {
|
|
17
|
+
const page = await loadHistoryPage(head, beforeVersion);
|
|
18
|
+
for (const entry of [...page.entries].reverse()) {
|
|
19
|
+
const { state } = entry;
|
|
20
|
+
if (state.principalType !== head.principalType ||
|
|
21
|
+
state.principalId !== head.principalId ||
|
|
22
|
+
state.version !== expectedVersion ||
|
|
23
|
+
(state.version === head.version && state.stateHash !== head.stateHash))
|
|
24
|
+
return false;
|
|
25
|
+
if (!entry.projection.some((member) => member.userId === request.userId))
|
|
26
|
+
return false;
|
|
27
|
+
if (state.version === request.membersHead.version)
|
|
28
|
+
return citesState(request, state);
|
|
29
|
+
expectedVersion -= 1;
|
|
30
|
+
}
|
|
31
|
+
if (page.nextBeforeVersion === null || page.entries.length === 0)
|
|
32
|
+
return false;
|
|
33
|
+
beforeVersion = page.nextBeforeVersion;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks each leave request against this device's verified Members head.
|
|
39
|
+
* Removing anyone from Members rotates its key epoch and key, so a request
|
|
40
|
+
* citing the head's own epoch and key has seen no removal since. One citing an earlier epoch
|
|
41
|
+
* stands only if verified history shows its member in every Members state
|
|
42
|
+
* from the cited one on; a request from a membership a removal ended never
|
|
43
|
+
* does, whatever the server serves beside it. Display only: a request this
|
|
44
|
+
* device cannot place is shown as no request.
|
|
45
|
+
*/
|
|
46
|
+
export function createLeaveRequestMembershipCheck(evidence) {
|
|
47
|
+
let membersHead;
|
|
48
|
+
return async (request) => {
|
|
49
|
+
membersHead ??= evidence.loadMembersHead().catch(() => null);
|
|
50
|
+
const head = await membersHead;
|
|
51
|
+
const cited = request.membersHead;
|
|
52
|
+
if (head?.principalType !== "group" ||
|
|
53
|
+
cited.principalId !== head.principalId ||
|
|
54
|
+
cited.keyEpoch > head.keyEpoch)
|
|
55
|
+
return false;
|
|
56
|
+
// One signed chain holds one key per epoch, so a matching epoch with a
|
|
57
|
+
// different key can only come from a fork, never from this history.
|
|
58
|
+
if (cited.keyEpoch === head.keyEpoch)
|
|
59
|
+
return cited.keyFingerprint === head.keyFingerprint;
|
|
60
|
+
if (!evidence.loadHistoryPage || cited.version >= head.version)
|
|
61
|
+
return false;
|
|
62
|
+
return memberStayedSinceCitedState(request, head, evidence.loadHistoryPage).catch(() => false);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=leaveRequestMembership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leaveRequestMembership.js","sourceRoot":"","sources":["leaveRequestMembership.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReferencedPrincipalHead } from \"@tearleads/crypto\";\nimport type { OrganizationLeaveRequest } from \"@tearleads/validators/util\";\nimport type { RecoveredPrincipalHistoryPage } from \"../../principals/historyRecovery/loadRecoveredPrincipalHistoryPage\";\n\n// How far back one request's check walks verified Members history: a request\n// stays pending for days, not for hundreds of Members changes.\nconst MAX_MEMBERS_HISTORY_PAGES = 8;\n\nexport interface LeaveRequestMembershipEvidence {\n /** The Members head this device has verified, or null when it has none. */\n readonly loadMembersHead: () => Promise<ReferencedPrincipalHead | null>;\n /**\n * Verified Members history ending at `head`, one page at a time and in\n * ascending order. Absent where this device cannot read history.\n */\n readonly loadHistoryPage?:\n | ((\n head: ReferencedPrincipalHead,\n beforeVersion: number | undefined,\n ) => Promise<RecoveredPrincipalHistoryPage>)\n | undefined;\n}\n\n/** Whether a leave request still belongs to its member's current membership. */\nexport type LeaveRequestMembershipCheck = (\n request: OrganizationLeaveRequest,\n) => Promise<boolean>;\n\nfunction citesState(\n request: OrganizationLeaveRequest,\n state: RecoveredPrincipalHistoryPage[\"entries\"][number][\"state\"],\n): boolean {\n const cited = request.membersHead;\n return (\n state.version === cited.version &&\n state.stateHash === cited.stateHash &&\n state.keyEpoch === cited.keyEpoch &&\n state.keyFingerprint === cited.keyFingerprint\n );\n}\n\n// Walks verified Members history back from the head to the cited state,\n// requiring the member in every state on the way.\nasync function memberStayedSinceCitedState(\n request: OrganizationLeaveRequest,\n head: ReferencedPrincipalHead,\n loadHistoryPage: NonNullable<\n LeaveRequestMembershipEvidence[\"loadHistoryPage\"]\n >,\n): Promise<boolean> {\n let expectedVersion = head.version;\n let beforeVersion: number | undefined;\n for (let pages = 0; pages < MAX_MEMBERS_HISTORY_PAGES; pages += 1) {\n const page = await loadHistoryPage(head, beforeVersion);\n for (const entry of [...page.entries].reverse()) {\n const { state } = entry;\n if (\n state.principalType !== head.principalType ||\n state.principalId !== head.principalId ||\n state.version !== expectedVersion ||\n (state.version === head.version && state.stateHash !== head.stateHash)\n )\n return false;\n if (!entry.projection.some((member) => member.userId === request.userId))\n return false;\n if (state.version === request.membersHead.version)\n return citesState(request, state);\n expectedVersion -= 1;\n }\n if (page.nextBeforeVersion === null || page.entries.length === 0)\n return false;\n beforeVersion = page.nextBeforeVersion;\n }\n return false;\n}\n\n/**\n * Checks each leave request against this device's verified Members head.\n * Removing anyone from Members rotates its key epoch and key, so a request\n * citing the head's own epoch and key has seen no removal since. One citing an earlier epoch\n * stands only if verified history shows its member in every Members state\n * from the cited one on; a request from a membership a removal ended never\n * does, whatever the server serves beside it. Display only: a request this\n * device cannot place is shown as no request.\n */\nexport function createLeaveRequestMembershipCheck(\n evidence: LeaveRequestMembershipEvidence,\n): LeaveRequestMembershipCheck {\n let membersHead: Promise<ReferencedPrincipalHead | null> | undefined;\n return async (request) => {\n membersHead ??= evidence.loadMembersHead().catch(() => null);\n const head = await membersHead;\n const cited = request.membersHead;\n if (\n head?.principalType !== \"group\" ||\n cited.principalId !== head.principalId ||\n cited.keyEpoch > head.keyEpoch\n )\n return false;\n // One signed chain holds one key per epoch, so a matching epoch with a\n // different key can only come from a fork, never from this history.\n if (cited.keyEpoch === head.keyEpoch)\n return cited.keyFingerprint === head.keyFingerprint;\n if (!evidence.loadHistoryPage || cited.version >= head.version)\n return false;\n return memberStayedSinceCitedState(\n request,\n head,\n evidence.loadHistoryPage,\n ).catch(() => false);\n };\n}\n"]}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import type { TrustedUserIdentityResolver } from "../../../data/trustedUserIdentity/index.js";
|
|
2
2
|
import type { OrganizationDirectoryAndGroups, OrganizationDirectoryUser, OrganizationUserDetail } from "../readModel/index.js";
|
|
3
|
+
import type { LeaveRequestMembershipCheck } from "./leaveRequestMembership.js";
|
|
4
|
+
/** What a shown leave request is checked against. */
|
|
5
|
+
export interface LeaveRequestVerifier {
|
|
6
|
+
readonly resolveTrustedUserIdentity: TrustedUserIdentityResolver;
|
|
7
|
+
readonly membershipIsCurrent: LeaveRequestMembershipCheck;
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
10
|
* The user as shown, with its leave request kept only when the member signed
|
|
5
|
-
* it. Every directory user a client presents
|
|
6
|
-
* read model or a mutation response carried
|
|
11
|
+
* it during their current membership. Every directory user a client presents
|
|
12
|
+
* passes through here, whether a read model or a mutation response carried
|
|
13
|
+
* it.
|
|
7
14
|
*/
|
|
8
|
-
export declare function withVerifiedDirectoryUser(organizationId: string, user: OrganizationDirectoryUser,
|
|
15
|
+
export declare function withVerifiedDirectoryUser(organizationId: string, user: OrganizationDirectoryUser, verifier: LeaveRequestVerifier): Promise<OrganizationDirectoryUser>;
|
|
9
16
|
/**
|
|
10
|
-
* Keeps only leave requests their member signed
|
|
11
|
-
*
|
|
17
|
+
* Keeps only leave requests their member signed during their current
|
|
18
|
+
* membership, checked against the member's trusted identity and this device's
|
|
19
|
+
* verified Members history; any other is shown as no request at all.
|
|
12
20
|
*/
|
|
13
|
-
export declare function withVerifiedLeaveRequests<Result extends OrganizationDirectoryAndGroups | null | undefined>(result: Result,
|
|
21
|
+
export declare function withVerifiedLeaveRequests<Result extends OrganizationDirectoryAndGroups | null | undefined>(result: Result, verifier: LeaveRequestVerifier): Promise<Result>;
|
|
14
22
|
/** The user detail counterpart of withVerifiedLeaveRequests. */
|
|
15
|
-
export declare function withVerifiedUserLeaveRequest(detail: OrganizationUserDetail | null, organizationId: string,
|
|
23
|
+
export declare function withVerifiedUserLeaveRequest(detail: OrganizationUserDetail | null, organizationId: string, verifier: LeaveRequestVerifier): Promise<OrganizationUserDetail | null>;
|