@dxos/credentials 0.5.1-next.638da00 → 0.5.1-next.65aaa36
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/dist/lib/browser/index.mjs +167 -740
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +144 -713
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/credentials/credential-factory.d.ts +1 -3
- package/dist/types/src/credentials/credential-factory.d.ts.map +1 -1
- package/dist/types/src/credentials/credential-generator.d.ts +2 -12
- package/dist/types/src/credentials/credential-generator.d.ts.map +1 -1
- package/dist/types/src/credentials/verifier.d.ts +1 -1
- package/dist/types/src/credentials/verifier.d.ts.map +1 -1
- package/dist/types/src/state-machine/member-state-machine.d.ts +12 -17
- package/dist/types/src/state-machine/member-state-machine.d.ts.map +1 -1
- package/dist/types/src/state-machine/space-state-machine.d.ts +3 -3
- package/dist/types/src/state-machine/space-state-machine.d.ts.map +1 -1
- package/package.json +16 -16
- package/src/credentials/credential-factory.ts +1 -9
- package/src/credentials/credential-generator.ts +2 -15
- package/src/credentials/verifier.test.ts +0 -25
- package/src/credentials/verifier.ts +1 -5
- package/src/state-machine/invitation-state-machine.test.ts +1 -1
- package/src/state-machine/member-state-machine.ts +53 -111
- package/src/state-machine/space-state-machine.test.ts +16 -20
- package/src/state-machine/space-state-machine.ts +33 -12
- package/dist/types/src/graph/credential-graph.d.ts +0 -152
- package/dist/types/src/graph/credential-graph.d.ts.map +0 -1
- package/dist/types/src/state-machine/member-state-machine.test.d.ts +0 -2
- package/dist/types/src/state-machine/member-state-machine.test.d.ts.map +0 -1
- package/src/graph/credential-graph.ts +0 -557
- package/src/state-machine/member-state-machine.test.ts +0 -580
|
@@ -112,9 +112,6 @@ import { PublicKey as PublicKey2 } from "@dxos/keys";
|
|
|
112
112
|
import { verifySignature } from "@dxos/crypto";
|
|
113
113
|
var SIGNATURE_TYPE_ED25519 = "ED25519Signature";
|
|
114
114
|
var verifyCredential = async (credential) => {
|
|
115
|
-
if (credential.parentCredentialIds?.length === 0) {
|
|
116
|
-
delete credential.parentCredentialIds;
|
|
117
|
-
}
|
|
118
115
|
if (!credential.issuer.equals(credential.proof.signer)) {
|
|
119
116
|
if (!credential.proof.chain) {
|
|
120
117
|
return {
|
|
@@ -179,10 +176,10 @@ var verifyChain = async (chain, authority, subject) => {
|
|
|
179
176
|
|
|
180
177
|
// packages/core/halo/credentials/src/credentials/credential-factory.ts
|
|
181
178
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/credentials/credential-factory.ts";
|
|
182
|
-
var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce
|
|
179
|
+
var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce }) => {
|
|
183
180
|
invariant(assertion["@type"], "Invalid assertion.", {
|
|
184
181
|
F: __dxlog_file,
|
|
185
|
-
L:
|
|
182
|
+
L: 45,
|
|
186
183
|
S: void 0,
|
|
187
184
|
A: [
|
|
188
185
|
"assertion['@type']",
|
|
@@ -191,7 +188,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
191
188
|
});
|
|
192
189
|
invariant(!!signingKey === !!chain, "Chain must be provided if and only if the signing key differs from the issuer.", {
|
|
193
190
|
F: __dxlog_file,
|
|
194
|
-
L:
|
|
191
|
+
L: 46,
|
|
195
192
|
S: void 0,
|
|
196
193
|
A: [
|
|
197
194
|
"!!signingKey === !!chain",
|
|
@@ -202,7 +199,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
202
199
|
const result = await verifyChain(chain, issuer, signingKey);
|
|
203
200
|
invariant(result.kind === "pass", "Invalid chain.", {
|
|
204
201
|
F: __dxlog_file,
|
|
205
|
-
L:
|
|
202
|
+
L: 49,
|
|
206
203
|
S: void 0,
|
|
207
204
|
A: [
|
|
208
205
|
"result.kind === 'pass'",
|
|
@@ -225,9 +222,6 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
225
222
|
nonce
|
|
226
223
|
}
|
|
227
224
|
};
|
|
228
|
-
if ((parentCredentialIds?.length ?? 0) > 0) {
|
|
229
|
-
credential.parentCredentialIds = parentCredentialIds;
|
|
230
|
-
}
|
|
231
225
|
const signedPayload = getCredentialProofPayload(credential);
|
|
232
226
|
credential.proof.value = await signer.sign(signingKey ?? issuer, signedPayload);
|
|
233
227
|
if (chain) {
|
|
@@ -244,13 +238,12 @@ var createCredentialMessage = (credential) => {
|
|
|
244
238
|
};
|
|
245
239
|
var createCredentialSignerWithKey = (signer, issuer) => ({
|
|
246
240
|
getIssuer: () => issuer,
|
|
247
|
-
createCredential: ({ subject, assertion, nonce
|
|
241
|
+
createCredential: ({ subject, assertion, nonce }) => createCredential({
|
|
248
242
|
signer,
|
|
249
243
|
issuer,
|
|
250
244
|
subject,
|
|
251
245
|
assertion,
|
|
252
|
-
nonce
|
|
253
|
-
parentCredentialIds
|
|
246
|
+
nonce
|
|
254
247
|
})
|
|
255
248
|
});
|
|
256
249
|
var createCredentialSignerWithChain = (signer, chain, signingKey) => ({
|
|
@@ -318,7 +311,7 @@ var CredentialGenerator = class {
|
|
|
318
311
|
assertion: {
|
|
319
312
|
"@type": "dxos.halo.credentials.SpaceMember",
|
|
320
313
|
spaceKey,
|
|
321
|
-
role: SpaceMember.Role.
|
|
314
|
+
role: SpaceMember.Role.MEMBER,
|
|
322
315
|
genesisFeedKey
|
|
323
316
|
}
|
|
324
317
|
}),
|
|
@@ -413,15 +406,14 @@ var createDeviceAuthorization = async (signer, identityKey, deviceKey) => {
|
|
|
413
406
|
credential
|
|
414
407
|
}));
|
|
415
408
|
};
|
|
416
|
-
var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey,
|
|
409
|
+
var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey, profile, invitationCredentialId) => {
|
|
417
410
|
const credentials = await Promise.all([
|
|
418
411
|
await signer.createCredential({
|
|
419
412
|
subject: identityKey,
|
|
420
|
-
parentCredentialIds: membershipChainHeads,
|
|
421
413
|
assertion: {
|
|
422
414
|
"@type": "dxos.halo.credentials.SpaceMember",
|
|
423
415
|
spaceKey,
|
|
424
|
-
role,
|
|
416
|
+
role: SpaceMember.Role.ADMIN,
|
|
425
417
|
profile,
|
|
426
418
|
genesisFeedKey,
|
|
427
419
|
invitationCredentialId
|
|
@@ -579,10 +571,10 @@ var verifyPresentationSignature = async (presentation, proof) => {
|
|
|
579
571
|
// packages/core/halo/credentials/src/state-machine/space-state-machine.ts
|
|
580
572
|
import { runInContextAsync } from "@dxos/async";
|
|
581
573
|
import { Context } from "@dxos/context";
|
|
582
|
-
import { PublicKey as
|
|
583
|
-
import { log as
|
|
584
|
-
import { SpaceMember as
|
|
585
|
-
import { Callback as Callback4, ComplexMap as
|
|
574
|
+
import { PublicKey as PublicKey6 } from "@dxos/keys";
|
|
575
|
+
import { log as log2 } from "@dxos/log";
|
|
576
|
+
import { SpaceMember as SpaceMember2 } from "@dxos/protocols/proto/dxos/halo/credentials";
|
|
577
|
+
import { Callback as Callback4, ComplexMap as ComplexMap4, ComplexSet as ComplexSet2 } from "@dxos/util";
|
|
586
578
|
|
|
587
579
|
// packages/core/halo/credentials/src/state-machine/feed-state-machine.ts
|
|
588
580
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
@@ -706,587 +698,25 @@ var InvitationStateMachine = class {
|
|
|
706
698
|
};
|
|
707
699
|
|
|
708
700
|
// packages/core/halo/credentials/src/state-machine/member-state-machine.ts
|
|
709
|
-
import { invariant as invariant4 } from "@dxos/invariant";
|
|
710
|
-
import { PublicKey as PublicKey6 } from "@dxos/keys";
|
|
711
|
-
import { log as log2 } from "@dxos/log";
|
|
712
|
-
import { SpaceMember as SpaceMember2 } from "@dxos/protocols/proto/dxos/halo/credentials";
|
|
713
|
-
import { ComplexMap as ComplexMap4 } from "@dxos/util";
|
|
714
|
-
|
|
715
|
-
// packages/core/halo/credentials/src/graph/credential-graph.ts
|
|
716
701
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
717
702
|
import { PublicKey as PublicKey5 } from "@dxos/keys";
|
|
718
703
|
import { log } from "@dxos/log";
|
|
719
|
-
import { Callback as Callback3, ComplexMap as ComplexMap3
|
|
720
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/
|
|
721
|
-
var CredentialGraph = class {
|
|
722
|
-
constructor(_stateHandler) {
|
|
723
|
-
this._stateHandler = _stateHandler;
|
|
724
|
-
this._vertexIdGenerator = 1;
|
|
725
|
-
this._root = {
|
|
726
|
-
id: -1,
|
|
727
|
-
parents: [],
|
|
728
|
-
children: []
|
|
729
|
-
};
|
|
730
|
-
this._sentinel = {
|
|
731
|
-
id: -2,
|
|
732
|
-
parents: [],
|
|
733
|
-
children: []
|
|
734
|
-
};
|
|
735
|
-
this._vertexByCredentialId = new ComplexMap3(PublicKey5.hash);
|
|
736
|
-
this._subjectToVertex = new ComplexMap3(PublicKey5.hash);
|
|
737
|
-
this._subjectToState = new ComplexMap3(PublicKey5.hash);
|
|
738
|
-
this.onSubjectStateChanged = new Callback3();
|
|
739
|
-
}
|
|
740
|
-
getSubjectState(subjectId) {
|
|
741
|
-
return this._subjectToState.get(subjectId);
|
|
742
|
-
}
|
|
743
|
-
getState() {
|
|
744
|
-
return this._subjectToState;
|
|
745
|
-
}
|
|
746
|
-
getLeafIds() {
|
|
747
|
-
return this._sentinel.parents.map((v) => v.credential.id);
|
|
748
|
-
}
|
|
749
|
-
getGlobalStateScope() {
|
|
750
|
-
return {
|
|
751
|
-
state: this._subjectToVertex
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
addVertex(credential, assertion) {
|
|
755
|
-
const newVertex = {
|
|
756
|
-
id: this._vertexIdGenerator++,
|
|
757
|
-
credential,
|
|
758
|
-
assertion,
|
|
759
|
-
parents: [],
|
|
760
|
-
children: []
|
|
761
|
-
};
|
|
762
|
-
this._vertexByCredentialId.set(credential.id, newVertex);
|
|
763
|
-
const parentIds = credential.parentCredentialIds ?? [];
|
|
764
|
-
if (parentIds.length === 0) {
|
|
765
|
-
this._root.children.push(newVertex);
|
|
766
|
-
newVertex.parents.push(this._root);
|
|
767
|
-
} else {
|
|
768
|
-
for (const parentId of parentIds) {
|
|
769
|
-
const parentVertex = this._vertexByCredentialId.get(parentId);
|
|
770
|
-
if (parentVertex == null) {
|
|
771
|
-
log.error("credential skipped because of the unknown parent", {
|
|
772
|
-
credential,
|
|
773
|
-
parentId
|
|
774
|
-
}, {
|
|
775
|
-
F: __dxlog_file3,
|
|
776
|
-
L: 72,
|
|
777
|
-
S: this,
|
|
778
|
-
C: (f, a) => f(...a)
|
|
779
|
-
});
|
|
780
|
-
continue;
|
|
781
|
-
}
|
|
782
|
-
parentVertex.children.push(newVertex);
|
|
783
|
-
newVertex.parents.push(parentVertex);
|
|
784
|
-
this._removeSentinelConnection(parentVertex);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
newVertex.children.push(this._sentinel);
|
|
788
|
-
this._sentinel.parents.push(newVertex);
|
|
789
|
-
return this._onVertexInserted(newVertex);
|
|
790
|
-
}
|
|
791
|
-
_removeSentinelConnection(vertex) {
|
|
792
|
-
const sentinelIdx = vertex.children.indexOf(this._sentinel);
|
|
793
|
-
if (sentinelIdx >= 0) {
|
|
794
|
-
vertex.children.splice(sentinelIdx, 1);
|
|
795
|
-
const vertexInSentinelIdx = this._sentinel.parents.indexOf(vertex);
|
|
796
|
-
invariant3(vertexInSentinelIdx >= 0, void 0, {
|
|
797
|
-
F: __dxlog_file3,
|
|
798
|
-
L: 90,
|
|
799
|
-
S: this,
|
|
800
|
-
A: [
|
|
801
|
-
"vertexInSentinelIdx >= 0",
|
|
802
|
-
""
|
|
803
|
-
]
|
|
804
|
-
});
|
|
805
|
-
this._sentinel.parents.splice(vertexInSentinelIdx, 1);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
async _onVertexInserted(newVertex) {
|
|
809
|
-
const { credential, assertion } = newVertex;
|
|
810
|
-
invariant3(credential, void 0, {
|
|
811
|
-
F: __dxlog_file3,
|
|
812
|
-
L: 97,
|
|
813
|
-
S: this,
|
|
814
|
-
A: [
|
|
815
|
-
"credential",
|
|
816
|
-
""
|
|
817
|
-
]
|
|
818
|
-
});
|
|
819
|
-
let changedSubjects = [];
|
|
820
|
-
const isUpdateAppliedOnTopOfThePreviousState = this._sentinel.parents.length === 1;
|
|
821
|
-
if (isUpdateAppliedOnTopOfThePreviousState) {
|
|
822
|
-
const subjectId = credential.subject.id;
|
|
823
|
-
if (this._stateHandler.isUpdateAllowed(this.getGlobalStateScope(), credential, assertion)) {
|
|
824
|
-
const newSubjectState = this._stateHandler.createState(credential, newVertex.assertion);
|
|
825
|
-
const prevSubjectState = this._subjectToState.get(subjectId);
|
|
826
|
-
this._subjectToState.set(subjectId, newSubjectState);
|
|
827
|
-
this._subjectToVertex.set(subjectId, newVertex);
|
|
828
|
-
if (this._stateHandler.hasStateChanged(newSubjectState, prevSubjectState)) {
|
|
829
|
-
changedSubjects.push(newSubjectState);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
} else {
|
|
833
|
-
changedSubjects = this._recomputeState();
|
|
834
|
-
}
|
|
835
|
-
if (changedSubjects.length > 0) {
|
|
836
|
-
await this.onSubjectStateChanged.callIfSet(changedSubjects);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* DFS the graph from root to sentinel pausing on merge points (nodes with multiple parents).
|
|
841
|
-
* Continue after all paths leading to a merge point converge by merging their states.
|
|
842
|
-
* In case of a concurrent update paths are replayed taking into account the state set
|
|
843
|
-
* by the winning branch.
|
|
844
|
-
*/
|
|
845
|
-
_recomputeState() {
|
|
846
|
-
const pendingPaths = /* @__PURE__ */ new Map();
|
|
847
|
-
const paths = [
|
|
848
|
-
this._createRootPath()
|
|
849
|
-
];
|
|
850
|
-
let lastPath = null;
|
|
851
|
-
while (lastPath == null) {
|
|
852
|
-
const path = paths.pop();
|
|
853
|
-
log("visit vertex", {
|
|
854
|
-
id: path.head.id
|
|
855
|
-
}, {
|
|
856
|
-
F: __dxlog_file3,
|
|
857
|
-
L: 132,
|
|
858
|
-
S: this,
|
|
859
|
-
C: (f, a) => f(...a)
|
|
860
|
-
});
|
|
861
|
-
this._updatePathState(path);
|
|
862
|
-
const convergedPaths = this._handleMergePoint(paths, pendingPaths, path);
|
|
863
|
-
if (convergedPaths == null) {
|
|
864
|
-
log("waiting for other paths", void 0, {
|
|
865
|
-
F: __dxlog_file3,
|
|
866
|
-
L: 136,
|
|
867
|
-
S: this,
|
|
868
|
-
C: (f, a) => f(...a)
|
|
869
|
-
});
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
const mergeResult = this._mergePaths(convergedPaths);
|
|
873
|
-
if (mergeResult.type === "replay_required") {
|
|
874
|
-
this._replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths);
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
const merged = mergeResult.path;
|
|
878
|
-
if (merged.head.children.length === 0) {
|
|
879
|
-
lastPath = merged;
|
|
880
|
-
} else if (merged.head.children.length === 1) {
|
|
881
|
-
merged.head = merged.head.children[0];
|
|
882
|
-
paths.push(merged);
|
|
883
|
-
} else {
|
|
884
|
-
this._forkTraversal(paths, merged);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
if (paths.length > 0) {
|
|
888
|
-
log.error("traversal finished while there were active paths", {
|
|
889
|
-
paths: paths.map((p) => ({
|
|
890
|
-
path: toChosenPath(p),
|
|
891
|
-
head: p.head.id
|
|
892
|
-
}))
|
|
893
|
-
}, {
|
|
894
|
-
F: __dxlog_file3,
|
|
895
|
-
L: 155,
|
|
896
|
-
S: this,
|
|
897
|
-
C: (f, a) => f(...a)
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
return this._setCurrentState(lastPath);
|
|
901
|
-
}
|
|
902
|
-
_replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths) {
|
|
903
|
-
paths.push(...mergeResult.replay.map((path) => {
|
|
904
|
-
const stateOverrides = path.stateOverrides ?? new ComplexMap3(PublicKey5.hash);
|
|
905
|
-
mergeResult.stateOverrides.forEach((value, key) => stateOverrides.set(key, value));
|
|
906
|
-
return {
|
|
907
|
-
...mergeResult.from,
|
|
908
|
-
chosenPath: path.chosenPath,
|
|
909
|
-
stateOverrides
|
|
910
|
-
};
|
|
911
|
-
}));
|
|
912
|
-
log("replay paths", () => ({
|
|
913
|
-
count: paths.length,
|
|
914
|
-
paths: paths.map((path) => ({
|
|
915
|
-
from: mergeResult.from.head.id,
|
|
916
|
-
path: toChosenPath(path),
|
|
917
|
-
overrides: path?.stateOverrides?.mapValues((v) => this._stateHandler.toLogString(v.assertion))
|
|
918
|
-
}))
|
|
919
|
-
}), {
|
|
920
|
-
F: __dxlog_file3,
|
|
921
|
-
L: 175,
|
|
922
|
-
S: this,
|
|
923
|
-
C: (f, a) => f(...a)
|
|
924
|
-
});
|
|
925
|
-
const clearedPending = convergedPaths.filter((l) => !mergeResult.replay.includes(l));
|
|
926
|
-
pendingPaths.set(convergedPaths[0].head.id, clearedPending);
|
|
927
|
-
}
|
|
928
|
-
_handleMergePoint(paths, pendingPaths, path) {
|
|
929
|
-
const pendingList = pendingPaths.get(path.head.id) ?? [];
|
|
930
|
-
pendingPaths.set(path.head.id, pendingList);
|
|
931
|
-
pendingList.push(path);
|
|
932
|
-
if (pendingList.length < path.head.parents.length) {
|
|
933
|
-
return null;
|
|
934
|
-
}
|
|
935
|
-
if (path.head.id === this._sentinel.id && paths.length > 0) {
|
|
936
|
-
log("waiting for all the active paths to converge on the sentinel", void 0, {
|
|
937
|
-
F: __dxlog_file3,
|
|
938
|
-
L: 199,
|
|
939
|
-
S: this,
|
|
940
|
-
C: (f, a) => f(...a)
|
|
941
|
-
});
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
pendingPaths.delete(path.head.id);
|
|
945
|
-
return pendingList;
|
|
946
|
-
}
|
|
947
|
-
_updatePathState(path) {
|
|
948
|
-
const headCredential = path.head.credential;
|
|
949
|
-
if (headCredential == null) {
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
const updatedSubject = headCredential.subject.id;
|
|
953
|
-
path.credentials.add(headCredential.id);
|
|
954
|
-
let isUpdateAllowed = this._stateHandler.isUpdateAllowed(path, headCredential, path.head.assertion);
|
|
955
|
-
if (!isUpdateAllowed && path.head.parents[0]?.id === this._root.id) {
|
|
956
|
-
const globalState = this.getGlobalStateScope();
|
|
957
|
-
isUpdateAllowed = this._stateHandler.isUpdateAllowed(globalState, headCredential, path.head.assertion);
|
|
958
|
-
}
|
|
959
|
-
if (isUpdateAllowed) {
|
|
960
|
-
path.forkChangedSubjects.add(updatedSubject);
|
|
961
|
-
path.forkIssuers.add(headCredential.issuer);
|
|
962
|
-
path.state.set(updatedSubject, path.head);
|
|
963
|
-
log("path state updated", () => ({
|
|
964
|
-
subject: updatedSubject,
|
|
965
|
-
newState: this._stateHandler.toLogString(path.head.assertion)
|
|
966
|
-
}), {
|
|
967
|
-
F: __dxlog_file3,
|
|
968
|
-
L: 223,
|
|
969
|
-
S: this,
|
|
970
|
-
C: (f, a) => f(...a)
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
_forkTraversal(paths, path) {
|
|
975
|
-
const replayChoice = path.chosenPath?.[path.head.id];
|
|
976
|
-
const choices = replayChoice ?? path.head.children;
|
|
977
|
-
for (const choice of choices) {
|
|
978
|
-
log("edge traversal", {
|
|
979
|
-
from: path.head.id,
|
|
980
|
-
to: choice.id
|
|
981
|
-
}, {
|
|
982
|
-
F: __dxlog_file3,
|
|
983
|
-
L: 234,
|
|
984
|
-
S: this,
|
|
985
|
-
C: (f, a) => f(...a)
|
|
986
|
-
});
|
|
987
|
-
const fork = {
|
|
988
|
-
forkPoint: path,
|
|
989
|
-
chosenPath: {
|
|
990
|
-
...path.chosenPath,
|
|
991
|
-
[path.head.id]: [
|
|
992
|
-
choice
|
|
993
|
-
]
|
|
994
|
-
},
|
|
995
|
-
head: choice,
|
|
996
|
-
credentials: new ComplexSet2(PublicKey5.hash, path.credentials),
|
|
997
|
-
state: new ComplexMap3(PublicKey5.hash, [
|
|
998
|
-
...path.state.entries()
|
|
999
|
-
]),
|
|
1000
|
-
forkIssuers: new ComplexSet2(PublicKey5.hash),
|
|
1001
|
-
forkChangedSubjects: new ComplexSet2(PublicKey5.hash),
|
|
1002
|
-
stateOverrides: path.stateOverrides
|
|
1003
|
-
};
|
|
1004
|
-
paths.push(fork);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
/**
|
|
1008
|
-
* Updates the current graph state.
|
|
1009
|
-
* @returns changed states.
|
|
1010
|
-
*/
|
|
1011
|
-
_setCurrentState(path) {
|
|
1012
|
-
const changedSubjects = [];
|
|
1013
|
-
const newStateMap = new ComplexMap3(PublicKey5.hash);
|
|
1014
|
-
const newVertexMap = new ComplexMap3(PublicKey5.hash);
|
|
1015
|
-
for (const [subjectKey, subjectVertex] of path.state.entries()) {
|
|
1016
|
-
const newState = this._stateHandler.createState(subjectVertex.credential, subjectVertex.assertion);
|
|
1017
|
-
const prevState = this._subjectToState.get(subjectKey);
|
|
1018
|
-
newStateMap.set(subjectKey, newState);
|
|
1019
|
-
newVertexMap.set(subjectKey, subjectVertex);
|
|
1020
|
-
if (this._stateHandler.hasStateChanged(newState, prevState)) {
|
|
1021
|
-
changedSubjects.push(newState);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
this._subjectToState = newStateMap;
|
|
1025
|
-
this._subjectToVertex = newVertexMap;
|
|
1026
|
-
return changedSubjects;
|
|
1027
|
-
}
|
|
1028
|
-
/*
|
|
1029
|
-
* Walk up all the fork points and return the first one present in all the paths.
|
|
1030
|
-
* We use local id to determine vertex position in the graph, because nodes can't
|
|
1031
|
-
* be inserted in the middle (between a parent and a child) and ids are monotonically increasing.
|
|
1032
|
-
*/
|
|
1033
|
-
_leastCommonAncestor(paths) {
|
|
1034
|
-
const uniqueForkPoints = paths.reduce((acc, path) => {
|
|
1035
|
-
let it = path.forkPoint;
|
|
1036
|
-
while (it) {
|
|
1037
|
-
acc.set(it.head.id, it);
|
|
1038
|
-
it = it.forkPoint;
|
|
1039
|
-
}
|
|
1040
|
-
return acc;
|
|
1041
|
-
}, /* @__PURE__ */ new Map());
|
|
1042
|
-
let maxId = this._root.id;
|
|
1043
|
-
let maxState = null;
|
|
1044
|
-
for (const [id, state] of uniqueForkPoints.entries()) {
|
|
1045
|
-
const headCredential = state.head.credential;
|
|
1046
|
-
if (headCredential != null) {
|
|
1047
|
-
const isPointInEveryPath = paths.every((p) => p.credentials.has(headCredential.id));
|
|
1048
|
-
if (isPointInEveryPath && id > maxId) {
|
|
1049
|
-
maxId = id;
|
|
1050
|
-
maxState = state;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
return maxState ?? this._createRootPath();
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* We might be merging paths where some of them had fork points after the initial forking.
|
|
1058
|
-
* We need all the paths to point to the least common fork point and contain all the changes
|
|
1059
|
-
* that happened after it.
|
|
1060
|
-
*/
|
|
1061
|
-
_moveUpToForkPoint(forkPoint, path) {
|
|
1062
|
-
const isForkPointInPath = path.chosenPath[forkPoint.head.id] == null || path.forkPoint == null;
|
|
1063
|
-
if (isForkPointInPath) {
|
|
1064
|
-
return path;
|
|
1065
|
-
}
|
|
1066
|
-
if (forkPoint.head.id === path.forkPoint?.head.id) {
|
|
1067
|
-
return path;
|
|
1068
|
-
}
|
|
1069
|
-
let it = path.forkPoint;
|
|
1070
|
-
while (it.head.id !== forkPoint.head.id) {
|
|
1071
|
-
it.forkIssuers.forEach((iss) => path.forkIssuers.add(iss));
|
|
1072
|
-
it.forkChangedSubjects.forEach((m) => path.forkChangedSubjects.add(m));
|
|
1073
|
-
it = it.forkPoint;
|
|
1074
|
-
path.forkPoint = it;
|
|
1075
|
-
}
|
|
1076
|
-
return path;
|
|
1077
|
-
}
|
|
1078
|
-
_mergePaths(convergedPaths) {
|
|
1079
|
-
invariant3(convergedPaths.length >= 1, void 0, {
|
|
1080
|
-
F: __dxlog_file3,
|
|
1081
|
-
L: 324,
|
|
1082
|
-
S: this,
|
|
1083
|
-
A: [
|
|
1084
|
-
"convergedPaths.length >= 1",
|
|
1085
|
-
""
|
|
1086
|
-
]
|
|
1087
|
-
});
|
|
1088
|
-
if (convergedPaths.length === 1) {
|
|
1089
|
-
return {
|
|
1090
|
-
type: "merged",
|
|
1091
|
-
path: convergedPaths[0]
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
const forkPoint = this._leastCommonAncestor(convergedPaths);
|
|
1095
|
-
log("merging paths", () => ({
|
|
1096
|
-
forkPointId: forkPoint.head.id,
|
|
1097
|
-
pathCount: convergedPaths.length,
|
|
1098
|
-
forkPoints: convergedPaths.map((fp) => fp.forkPoint?.head.id)
|
|
1099
|
-
}), {
|
|
1100
|
-
F: __dxlog_file3,
|
|
1101
|
-
L: 329,
|
|
1102
|
-
S: this,
|
|
1103
|
-
C: (f, a) => f(...a)
|
|
1104
|
-
});
|
|
1105
|
-
const paths = convergedPaths.map((p) => this._moveUpToForkPoint(forkPoint, p));
|
|
1106
|
-
invariant3(forkPoint, void 0, {
|
|
1107
|
-
F: __dxlog_file3,
|
|
1108
|
-
L: 335,
|
|
1109
|
-
S: this,
|
|
1110
|
-
A: [
|
|
1111
|
-
"forkPoint",
|
|
1112
|
-
""
|
|
1113
|
-
]
|
|
1114
|
-
});
|
|
1115
|
-
const result = {
|
|
1116
|
-
forkPoint: forkPoint.forkPoint,
|
|
1117
|
-
chosenPath: {
|
|
1118
|
-
...forkPoint.chosenPath,
|
|
1119
|
-
[forkPoint.head.id]: []
|
|
1120
|
-
},
|
|
1121
|
-
stateOverrides: forkPoint.stateOverrides,
|
|
1122
|
-
credentials: new ComplexSet2(PublicKey5.hash, forkPoint.credentials),
|
|
1123
|
-
forkIssuers: new ComplexSet2(PublicKey5.hash, forkPoint.forkIssuers),
|
|
1124
|
-
forkChangedSubjects: new ComplexSet2(PublicKey5.hash, forkPoint.forkChangedSubjects),
|
|
1125
|
-
state: forkPoint.state.mapValues((v) => v),
|
|
1126
|
-
head: paths[0].head
|
|
1127
|
-
};
|
|
1128
|
-
const subjectToBranch = new ComplexMap3(PublicKey5.hash);
|
|
1129
|
-
for (const path of paths) {
|
|
1130
|
-
log("processing a path", () => ({
|
|
1131
|
-
choices: toChosenPath(path),
|
|
1132
|
-
modified: path.forkChangedSubjects,
|
|
1133
|
-
forkIssuers: path.forkIssuers,
|
|
1134
|
-
state: path.state.mapValues((v) => this._stateHandler.toLogString(v.assertion))
|
|
1135
|
-
}), {
|
|
1136
|
-
F: __dxlog_file3,
|
|
1137
|
-
L: 348,
|
|
1138
|
-
S: this,
|
|
1139
|
-
C: (f, a) => f(...a)
|
|
1140
|
-
});
|
|
1141
|
-
path.forkIssuers.forEach((iss) => result.forkIssuers.add(iss));
|
|
1142
|
-
path.credentials.forEach((cred) => result.credentials.add(cred));
|
|
1143
|
-
result.chosenPath[forkPoint.head.id].push(...path.chosenPath[forkPoint.head.id] ?? []);
|
|
1144
|
-
for (const modifiedSubject of path.forkChangedSubjects) {
|
|
1145
|
-
const existingBranch = subjectToBranch.get(modifiedSubject);
|
|
1146
|
-
if (existingBranch == null || this._shouldOverrideCredential(existingBranch, path, modifiedSubject)) {
|
|
1147
|
-
subjectToBranch.set(modifiedSubject, path);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
const replayPaths = /* @__PURE__ */ new Set();
|
|
1152
|
-
const addReplayPath = replayPaths.add.bind(replayPaths);
|
|
1153
|
-
for (const [subject, branch] of subjectToBranch.entries()) {
|
|
1154
|
-
result.forkChangedSubjects.add(subject);
|
|
1155
|
-
const vertex = branch.state.get(subject);
|
|
1156
|
-
result.state.set(subject, vertex);
|
|
1157
|
-
log("set subject state", () => ({
|
|
1158
|
-
subject,
|
|
1159
|
-
state: this._stateHandler.toLogString(vertex.assertion)
|
|
1160
|
-
}), {
|
|
1161
|
-
F: __dxlog_file3,
|
|
1162
|
-
L: 370,
|
|
1163
|
-
S: this,
|
|
1164
|
-
C: (f, a) => f(...a)
|
|
1165
|
-
});
|
|
1166
|
-
const otherPaths = paths.filter((p) => p !== branch);
|
|
1167
|
-
this._stateHandler.getConflictingPaths(otherPaths, vertex).forEach(addReplayPath);
|
|
1168
|
-
}
|
|
1169
|
-
if (replayPaths.size > 0) {
|
|
1170
|
-
return {
|
|
1171
|
-
type: "replay_required",
|
|
1172
|
-
replay: [
|
|
1173
|
-
...replayPaths.values()
|
|
1174
|
-
],
|
|
1175
|
-
from: forkPoint,
|
|
1176
|
-
stateOverrides: subjectToBranch.mapValues((v, key) => v.state.get(key))
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
return {
|
|
1180
|
-
type: "merged",
|
|
1181
|
-
path: result
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
/**
|
|
1185
|
-
* A candidate credential is preferred over the existing credential if:
|
|
1186
|
-
* 1. It is the merge-point, because it's the last credential that was issued in awareness of all
|
|
1187
|
-
* the previously existing ones.
|
|
1188
|
-
* 2. A path where candidate was set contains existing credential in it, which means that the candidate
|
|
1189
|
-
* was issued after the existing credential by a legitimate issuer.
|
|
1190
|
-
* 3. A state-specific logic (_stateHandler) is able to justify using the candidate credential.
|
|
1191
|
-
* 4. The path where candidate was set has more issuers than the existing path (longer branch).
|
|
1192
|
-
* 5. The issuance time of the candidate is after the issuance time of the existing credential (LWW).
|
|
1193
|
-
*/
|
|
1194
|
-
_shouldOverrideCredential(existing, candidate, modifiedSubject) {
|
|
1195
|
-
const candidateVertex = candidate.state.get(modifiedSubject);
|
|
1196
|
-
const currentVertex = existing.state.get(modifiedSubject);
|
|
1197
|
-
if (candidateVertex.id === currentVertex.id) {
|
|
1198
|
-
return false;
|
|
1199
|
-
}
|
|
1200
|
-
const mergePointId = existing.head.id;
|
|
1201
|
-
if (candidateVertex.id === mergePointId || currentVertex.id === mergePointId) {
|
|
1202
|
-
log("merge point chosen to break the tie", {
|
|
1203
|
-
mergePointId: existing.head.id
|
|
1204
|
-
}, {
|
|
1205
|
-
F: __dxlog_file3,
|
|
1206
|
-
L: 408,
|
|
1207
|
-
S: this,
|
|
1208
|
-
C: (f, a) => f(...a)
|
|
1209
|
-
});
|
|
1210
|
-
return mergePointId === candidateVertex.id;
|
|
1211
|
-
}
|
|
1212
|
-
const candidateCredential = candidateVertex.credential;
|
|
1213
|
-
const currentCredential = currentVertex.credential;
|
|
1214
|
-
if (existing.credentials.has(candidateCredential.id) !== candidate.credentials.has(currentCredential.id)) {
|
|
1215
|
-
log("one of the credentials was overridden in another branch", {
|
|
1216
|
-
current: currentVertex.id,
|
|
1217
|
-
candidate: candidateVertex.id
|
|
1218
|
-
}, {
|
|
1219
|
-
F: __dxlog_file3,
|
|
1220
|
-
L: 415,
|
|
1221
|
-
S: this,
|
|
1222
|
-
C: (f, a) => f(...a)
|
|
1223
|
-
});
|
|
1224
|
-
return candidate.credentials.has(currentCredential.id);
|
|
1225
|
-
}
|
|
1226
|
-
const winningCredential = this._stateHandler.tryPickWinningUpdate(existing, currentCredential, candidate, candidateCredential);
|
|
1227
|
-
if (winningCredential != null) {
|
|
1228
|
-
return winningCredential === candidateCredential;
|
|
1229
|
-
}
|
|
1230
|
-
if (candidate.forkIssuers.size !== existing.forkIssuers.size) {
|
|
1231
|
-
log("longer issuers branch used to break the tie", {
|
|
1232
|
-
issuerCount: [
|
|
1233
|
-
existing.forkIssuers.size,
|
|
1234
|
-
candidate.forkIssuers.size
|
|
1235
|
-
]
|
|
1236
|
-
}, {
|
|
1237
|
-
F: __dxlog_file3,
|
|
1238
|
-
L: 432,
|
|
1239
|
-
S: this,
|
|
1240
|
-
C: (f, a) => f(...a)
|
|
1241
|
-
});
|
|
1242
|
-
return candidate.forkIssuers.size > existing.forkIssuers.size;
|
|
1243
|
-
}
|
|
1244
|
-
log("issuance date used to break the tie", void 0, {
|
|
1245
|
-
F: __dxlog_file3,
|
|
1246
|
-
L: 437,
|
|
1247
|
-
S: this,
|
|
1248
|
-
C: (f, a) => f(...a)
|
|
1249
|
-
});
|
|
1250
|
-
return candidateCredential.issuanceDate.getTime() > currentCredential.issuanceDate.getTime();
|
|
1251
|
-
}
|
|
1252
|
-
_createRootPath() {
|
|
1253
|
-
return {
|
|
1254
|
-
head: this._root,
|
|
1255
|
-
chosenPath: {},
|
|
1256
|
-
forkIssuers: new ComplexSet2(PublicKey5.hash),
|
|
1257
|
-
forkChangedSubjects: new ComplexSet2(PublicKey5.hash),
|
|
1258
|
-
state: new ComplexMap3(PublicKey5.hash),
|
|
1259
|
-
credentials: new ComplexSet2(PublicKey5.hash)
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
|
-
};
|
|
1263
|
-
var toChosenPath = (path) => {
|
|
1264
|
-
return Object.fromEntries(Object.entries(path.chosenPath).map(([k, vs]) => [
|
|
1265
|
-
k,
|
|
1266
|
-
vs.map((v) => v.id)
|
|
1267
|
-
]));
|
|
1268
|
-
};
|
|
1269
|
-
|
|
1270
|
-
// packages/core/halo/credentials/src/state-machine/member-state-machine.ts
|
|
1271
|
-
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
|
|
704
|
+
import { Callback as Callback3, ComplexMap as ComplexMap3 } from "@dxos/util";
|
|
705
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
|
|
1272
706
|
var MemberStateMachine = class {
|
|
1273
707
|
constructor(_spaceKey) {
|
|
1274
708
|
this._spaceKey = _spaceKey;
|
|
1275
|
-
this.
|
|
1276
|
-
this.
|
|
1277
|
-
this.onMemberRoleChanged = this._hashgraph.onSubjectStateChanged;
|
|
709
|
+
this._members = new ComplexMap3(PublicKey5.hash);
|
|
710
|
+
this.onMemberAdmitted = new Callback3();
|
|
1278
711
|
}
|
|
1279
712
|
get creator() {
|
|
1280
|
-
return this.
|
|
713
|
+
return this._creator;
|
|
1281
714
|
}
|
|
1282
715
|
get members() {
|
|
1283
|
-
return this.
|
|
1284
|
-
}
|
|
1285
|
-
get membershipChainHeads() {
|
|
1286
|
-
return this._hashgraph.getLeafIds();
|
|
716
|
+
return this._members;
|
|
1287
717
|
}
|
|
1288
718
|
getRole(member) {
|
|
1289
|
-
return this.
|
|
719
|
+
return this._members.get(member)?.assertion.role;
|
|
1290
720
|
}
|
|
1291
721
|
/**
|
|
1292
722
|
* Processes the SpaceMember credential.
|
|
@@ -1296,125 +726,87 @@ var MemberStateMachine = class {
|
|
|
1296
726
|
const assertion = getCredentialAssertion(credential);
|
|
1297
727
|
switch (assertion["@type"]) {
|
|
1298
728
|
case "dxos.halo.credentials.SpaceMember": {
|
|
1299
|
-
|
|
1300
|
-
F:
|
|
1301
|
-
L:
|
|
729
|
+
invariant3(assertion.spaceKey.equals(this._spaceKey), void 0, {
|
|
730
|
+
F: __dxlog_file3,
|
|
731
|
+
L: 58,
|
|
1302
732
|
S: this,
|
|
1303
733
|
A: [
|
|
1304
734
|
"assertion.spaceKey.equals(this._spaceKey)",
|
|
1305
735
|
""
|
|
1306
736
|
]
|
|
1307
737
|
});
|
|
1308
|
-
if (this.
|
|
1309
|
-
|
|
738
|
+
if (this._members.has(credential.subject.id)) {
|
|
739
|
+
return;
|
|
1310
740
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
741
|
+
const info = {
|
|
742
|
+
key: credential.subject.id,
|
|
743
|
+
credential,
|
|
744
|
+
assertion,
|
|
745
|
+
removed: false,
|
|
746
|
+
profile: assertion.profile
|
|
747
|
+
};
|
|
748
|
+
if (!this._creator) {
|
|
749
|
+
this._creator = info;
|
|
1313
750
|
}
|
|
1314
|
-
|
|
751
|
+
this._members.set(credential.subject.id, info);
|
|
752
|
+
log("member added", {
|
|
753
|
+
member: credential.subject.id,
|
|
754
|
+
space: this._spaceKey,
|
|
755
|
+
role: assertion.role,
|
|
756
|
+
profile: assertion.profile
|
|
757
|
+
}, {
|
|
758
|
+
F: __dxlog_file3,
|
|
759
|
+
L: 79,
|
|
760
|
+
S: this,
|
|
761
|
+
C: (f, a) => f(...a)
|
|
762
|
+
});
|
|
763
|
+
await this.onMemberAdmitted.callIfSet(info);
|
|
1315
764
|
break;
|
|
1316
765
|
}
|
|
1317
766
|
case "dxos.halo.credentials.MemberProfile": {
|
|
1318
|
-
const member = this.
|
|
1319
|
-
if (member) {
|
|
1320
|
-
|
|
1321
|
-
} else {
|
|
1322
|
-
log2.warn("Member not found", {
|
|
767
|
+
const member = this._members.get(credential.subject.id);
|
|
768
|
+
if (!member) {
|
|
769
|
+
log.warn("Member not found", {
|
|
1323
770
|
id: credential.subject.id
|
|
1324
771
|
}, {
|
|
1325
|
-
F:
|
|
1326
|
-
L:
|
|
772
|
+
F: __dxlog_file3,
|
|
773
|
+
L: 93,
|
|
1327
774
|
S: this,
|
|
1328
775
|
C: (f, a) => f(...a)
|
|
1329
776
|
});
|
|
777
|
+
} else {
|
|
778
|
+
member.profile = assertion.profile;
|
|
1330
779
|
}
|
|
1331
|
-
this._memberProfiles.set(credential.subject.id, assertion.profile);
|
|
1332
780
|
break;
|
|
1333
781
|
}
|
|
1334
782
|
default:
|
|
1335
783
|
throw new Error("Invalid assertion type");
|
|
1336
784
|
}
|
|
1337
785
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
const issuer = credential.issuer;
|
|
1353
|
-
const isChangingOwnRole = issuer.equals(credential.subject.id);
|
|
1354
|
-
if (isChangingOwnRole) {
|
|
1355
|
-
return false;
|
|
1356
|
-
}
|
|
1357
|
-
if (issuer.equals(assertion.spaceKey)) {
|
|
1358
|
-
return true;
|
|
1359
|
-
}
|
|
1360
|
-
const issuerRole = this._getRole(scope, issuer);
|
|
1361
|
-
return issuerRole === SpaceMember2.Role.ADMIN || issuerRole === SpaceMember2.Role.OWNER;
|
|
1362
|
-
}
|
|
1363
|
-
getConflictingPaths(paths, update) {
|
|
1364
|
-
if (update.assertion.role !== SpaceMember2.Role.REMOVED && update.assertion.role !== SpaceMember2.Role.EDITOR) {
|
|
1365
|
-
return [];
|
|
1366
|
-
}
|
|
1367
|
-
const memberId = update.credential.subject.id;
|
|
1368
|
-
return paths.filter((p) => p.forkIssuers.has(memberId));
|
|
1369
|
-
}
|
|
1370
|
-
tryPickWinningUpdate(scope1, update1, scope2, update2) {
|
|
1371
|
-
const path1IssuerRole = this._getRole(scope1, update1.issuer);
|
|
1372
|
-
const path2IssuerRole = this._getRole(scope2, update2.issuer);
|
|
1373
|
-
if (path2IssuerRole === SpaceMember2.Role.OWNER !== (path1IssuerRole === SpaceMember2.Role.OWNER)) {
|
|
1374
|
-
log2("owner decision used to break the tie", void 0, {
|
|
1375
|
-
F: __dxlog_file4,
|
|
1376
|
-
L: 139,
|
|
1377
|
-
S: this,
|
|
1378
|
-
C: (f, a) => f(...a)
|
|
1379
|
-
});
|
|
1380
|
-
return path1IssuerRole === SpaceMember2.Role.OWNER ? update1 : update2;
|
|
1381
|
-
}
|
|
1382
|
-
return null;
|
|
1383
|
-
}
|
|
1384
|
-
toLogString(assertion) {
|
|
1385
|
-
const role = assertion?.role ?? SpaceMember2.Role.REMOVED;
|
|
1386
|
-
return Object.entries(SpaceMember2.Role).find(([_, value]) => value === role)[0];
|
|
1387
|
-
}
|
|
1388
|
-
hasStateChanged(s1, s2) {
|
|
1389
|
-
return s1?.role !== s2?.role;
|
|
1390
|
-
}
|
|
1391
|
-
_getRole(scope, memberId) {
|
|
1392
|
-
if (this._ownerKey?.equals(memberId)) {
|
|
1393
|
-
return SpaceMember2.Role.OWNER;
|
|
1394
|
-
}
|
|
1395
|
-
const realRole = scope.state.get(memberId)?.assertion?.role ?? SpaceMember2.Role.REMOVED;
|
|
1396
|
-
if (scope.stateOverrides != null) {
|
|
1397
|
-
const override = scope.stateOverrides.get(memberId);
|
|
1398
|
-
if (override != null) {
|
|
1399
|
-
log2("member role overridden in path", () => ({
|
|
1400
|
-
headId: scope.head?.id,
|
|
1401
|
-
roleOverride: this.toLogString(override.assertion),
|
|
1402
|
-
realRole: this.toLogString(scope.state.get(memberId)?.assertion)
|
|
1403
|
-
}), {
|
|
1404
|
-
F: __dxlog_file4,
|
|
1405
|
-
L: 162,
|
|
1406
|
-
S: this,
|
|
1407
|
-
C: (f, a) => f(...a)
|
|
1408
|
-
});
|
|
1409
|
-
return override.assertion.role;
|
|
786
|
+
async onRevoked(revoked, revocation) {
|
|
787
|
+
invariant3(revoked.id, void 0, {
|
|
788
|
+
F: __dxlog_file3,
|
|
789
|
+
L: 106,
|
|
790
|
+
S: this,
|
|
791
|
+
A: [
|
|
792
|
+
"revoked.id",
|
|
793
|
+
""
|
|
794
|
+
]
|
|
795
|
+
});
|
|
796
|
+
for (const member of this._members.values()) {
|
|
797
|
+
if (member.credential.id?.equals(revoked.id)) {
|
|
798
|
+
member.removed = true;
|
|
799
|
+
break;
|
|
1410
800
|
}
|
|
1411
801
|
}
|
|
1412
|
-
return realRole;
|
|
1413
802
|
}
|
|
1414
803
|
};
|
|
1415
804
|
|
|
1416
805
|
// packages/core/halo/credentials/src/state-machine/space-state-machine.ts
|
|
1417
|
-
var
|
|
806
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/space-state-machine.ts";
|
|
807
|
+
var REVOCABLE_CREDENTIALS = [
|
|
808
|
+
"dxos.halo.credentials.SpaceMember"
|
|
809
|
+
];
|
|
1418
810
|
var SpaceStateMachine = class {
|
|
1419
811
|
constructor(_spaceKey) {
|
|
1420
812
|
this._spaceKey = _spaceKey;
|
|
@@ -1422,11 +814,11 @@ var SpaceStateMachine = class {
|
|
|
1422
814
|
this._feeds = new FeedStateMachine(this._spaceKey);
|
|
1423
815
|
this._invitations = new InvitationStateMachine();
|
|
1424
816
|
this._credentials = [];
|
|
1425
|
-
this._credentialsById = new
|
|
1426
|
-
this._processedCredentials = new
|
|
817
|
+
this._credentialsById = new ComplexMap4(PublicKey6.hash);
|
|
818
|
+
this._processedCredentials = new ComplexSet2(PublicKey6.hash);
|
|
1427
819
|
this._credentialProcessors = [];
|
|
1428
820
|
this.onCredentialProcessed = new Callback4();
|
|
1429
|
-
this.
|
|
821
|
+
this.onMemberAdmitted = this._members.onMemberAdmitted;
|
|
1430
822
|
this.onFeedAdmitted = this._feeds.onFeedAdmitted;
|
|
1431
823
|
this.onDelegatedInvitation = this._invitations.onDelegatedInvitation;
|
|
1432
824
|
this.onDelegatedInvitationRemoved = this._invitations.onDelegatedInvitationRemoved;
|
|
@@ -1437,9 +829,6 @@ var SpaceStateMachine = class {
|
|
|
1437
829
|
get members() {
|
|
1438
830
|
return this._members.members;
|
|
1439
831
|
}
|
|
1440
|
-
get membershipChainHeads() {
|
|
1441
|
-
return this._members.membershipChainHeads;
|
|
1442
|
-
}
|
|
1443
832
|
get feeds() {
|
|
1444
833
|
return this._feeds.feeds;
|
|
1445
834
|
}
|
|
@@ -1491,9 +880,9 @@ var SpaceStateMachine = class {
|
|
|
1491
880
|
if (!skipVerification) {
|
|
1492
881
|
const result = await verifyCredential(credential);
|
|
1493
882
|
if (result.kind !== "pass") {
|
|
1494
|
-
|
|
1495
|
-
F:
|
|
1496
|
-
L:
|
|
883
|
+
log2.warn(`Invalid credential: ${result.errors.join(", ")}`, void 0, {
|
|
884
|
+
F: __dxlog_file4,
|
|
885
|
+
L: 149,
|
|
1497
886
|
S: this,
|
|
1498
887
|
C: (f, a) => f(...a)
|
|
1499
888
|
});
|
|
@@ -1502,29 +891,61 @@ var SpaceStateMachine = class {
|
|
|
1502
891
|
}
|
|
1503
892
|
const assertion = getCredentialAssertion(credential);
|
|
1504
893
|
switch (assertion["@type"]) {
|
|
894
|
+
case "dxos.halo.credentials.Revocation": {
|
|
895
|
+
if (!this._canRevokeCredentials(credential.issuer)) {
|
|
896
|
+
log2.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
897
|
+
F: __dxlog_file4,
|
|
898
|
+
L: 158,
|
|
899
|
+
S: this,
|
|
900
|
+
C: (f, a) => f(...a)
|
|
901
|
+
});
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
const toBeRevoked = this._credentialsById.get(credential.subject.id);
|
|
905
|
+
if (!toBeRevoked) {
|
|
906
|
+
log2.warn(`Credential to revoke not found: ${credential.subject.id}`, void 0, {
|
|
907
|
+
F: __dxlog_file4,
|
|
908
|
+
L: 163,
|
|
909
|
+
S: this,
|
|
910
|
+
C: (f, a) => f(...a)
|
|
911
|
+
});
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
if (!this._credentialCanBeRevoked(toBeRevoked)) {
|
|
915
|
+
log2.warn(`Credential cannot be revoked: ${credential.subject.id}`, void 0, {
|
|
916
|
+
F: __dxlog_file4,
|
|
917
|
+
L: 167,
|
|
918
|
+
S: this,
|
|
919
|
+
C: (f, a) => f(...a)
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
toBeRevoked.revoked = true;
|
|
923
|
+
await this._members.onRevoked(toBeRevoked.credential, credential);
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
1505
926
|
case "dxos.halo.credentials.SpaceGenesis": {
|
|
1506
927
|
if (this._genesisCredential) {
|
|
1507
|
-
|
|
1508
|
-
F:
|
|
1509
|
-
L:
|
|
928
|
+
log2.warn("Space already has a genesis credential.", void 0, {
|
|
929
|
+
F: __dxlog_file4,
|
|
930
|
+
L: 176,
|
|
1510
931
|
S: this,
|
|
1511
932
|
C: (f, a) => f(...a)
|
|
1512
933
|
});
|
|
1513
934
|
return false;
|
|
1514
935
|
}
|
|
1515
936
|
if (!credential.issuer.equals(this._spaceKey)) {
|
|
1516
|
-
|
|
1517
|
-
F:
|
|
1518
|
-
L:
|
|
937
|
+
log2.warn("Space genesis credential must be issued by space.", void 0, {
|
|
938
|
+
F: __dxlog_file4,
|
|
939
|
+
L: 180,
|
|
1519
940
|
S: this,
|
|
1520
941
|
C: (f, a) => f(...a)
|
|
1521
942
|
});
|
|
1522
943
|
return false;
|
|
1523
944
|
}
|
|
1524
945
|
if (!credential.subject.id.equals(this._spaceKey)) {
|
|
1525
|
-
|
|
1526
|
-
F:
|
|
1527
|
-
L:
|
|
946
|
+
log2.warn("Space genesis credential must be issued to space.", void 0, {
|
|
947
|
+
F: __dxlog_file4,
|
|
948
|
+
L: 184,
|
|
1528
949
|
S: this,
|
|
1529
950
|
C: (f, a) => f(...a)
|
|
1530
951
|
});
|
|
@@ -1538,18 +959,18 @@ var SpaceStateMachine = class {
|
|
|
1538
959
|
break;
|
|
1539
960
|
}
|
|
1540
961
|
if (!this._genesisCredential) {
|
|
1541
|
-
|
|
1542
|
-
F:
|
|
1543
|
-
L:
|
|
962
|
+
log2.warn("Space must have a genesis credential before adding members.", void 0, {
|
|
963
|
+
F: __dxlog_file4,
|
|
964
|
+
L: 198,
|
|
1544
965
|
S: this,
|
|
1545
966
|
C: (f, a) => f(...a)
|
|
1546
967
|
});
|
|
1547
968
|
return false;
|
|
1548
969
|
}
|
|
1549
970
|
if (!this._canInviteNewMembers(credential.issuer)) {
|
|
1550
|
-
|
|
1551
|
-
F:
|
|
1552
|
-
L:
|
|
971
|
+
log2.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
972
|
+
F: __dxlog_file4,
|
|
973
|
+
L: 202,
|
|
1553
974
|
S: this,
|
|
1554
975
|
C: (f, a) => f(...a)
|
|
1555
976
|
});
|
|
@@ -1561,9 +982,9 @@ var SpaceStateMachine = class {
|
|
|
1561
982
|
}
|
|
1562
983
|
case "dxos.halo.credentials.MemberProfile": {
|
|
1563
984
|
if (!this._genesisCredential) {
|
|
1564
|
-
|
|
1565
|
-
F:
|
|
1566
|
-
L:
|
|
985
|
+
log2.warn("Space must have a genesis credential before adding members.", void 0, {
|
|
986
|
+
F: __dxlog_file4,
|
|
987
|
+
L: 213,
|
|
1567
988
|
S: this,
|
|
1568
989
|
C: (f, a) => f(...a)
|
|
1569
990
|
});
|
|
@@ -1574,18 +995,18 @@ var SpaceStateMachine = class {
|
|
|
1574
995
|
}
|
|
1575
996
|
case "dxos.halo.credentials.AdmittedFeed": {
|
|
1576
997
|
if (!this._genesisCredential) {
|
|
1577
|
-
|
|
1578
|
-
F:
|
|
1579
|
-
L:
|
|
998
|
+
log2.warn("Space must have a genesis credential before admitting feeds.", void 0, {
|
|
999
|
+
F: __dxlog_file4,
|
|
1000
|
+
L: 223,
|
|
1580
1001
|
S: this,
|
|
1581
1002
|
C: (f, a) => f(...a)
|
|
1582
1003
|
});
|
|
1583
1004
|
return false;
|
|
1584
1005
|
}
|
|
1585
1006
|
if (!this._canAdmitFeeds(credential.issuer)) {
|
|
1586
|
-
|
|
1587
|
-
F:
|
|
1588
|
-
L:
|
|
1007
|
+
log2.warn(`Space member is not authorized to admit feeds: ${credential.issuer}`, void 0, {
|
|
1008
|
+
F: __dxlog_file4,
|
|
1009
|
+
L: 227,
|
|
1589
1010
|
S: this,
|
|
1590
1011
|
C: (f, a) => f(...a)
|
|
1591
1012
|
});
|
|
@@ -1597,9 +1018,9 @@ var SpaceStateMachine = class {
|
|
|
1597
1018
|
case "dxos.halo.invitations.CancelDelegatedInvitation":
|
|
1598
1019
|
case "dxos.halo.invitations.DelegateSpaceInvitation": {
|
|
1599
1020
|
if (!this._canInviteNewMembers(credential.issuer)) {
|
|
1600
|
-
|
|
1601
|
-
F:
|
|
1602
|
-
L:
|
|
1021
|
+
log2.warn(`Invalid invitation, space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
1022
|
+
F: __dxlog_file4,
|
|
1023
|
+
L: 238,
|
|
1603
1024
|
S: this,
|
|
1604
1025
|
C: (f, a) => f(...a)
|
|
1605
1026
|
});
|
|
@@ -1627,11 +1048,17 @@ var SpaceStateMachine = class {
|
|
|
1627
1048
|
return true;
|
|
1628
1049
|
}
|
|
1629
1050
|
_canInviteNewMembers(key) {
|
|
1630
|
-
return key.equals(this._spaceKey) || this._members.getRole(key) ===
|
|
1051
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember2.Role.ADMIN;
|
|
1631
1052
|
}
|
|
1632
1053
|
_canAdmitFeeds(key) {
|
|
1633
1054
|
const role = this._members.getRole(key);
|
|
1634
|
-
return role ===
|
|
1055
|
+
return role === SpaceMember2.Role.MEMBER || role === SpaceMember2.Role.ADMIN;
|
|
1056
|
+
}
|
|
1057
|
+
_canRevokeCredentials(key) {
|
|
1058
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember2.Role.ADMIN;
|
|
1059
|
+
}
|
|
1060
|
+
_credentialCanBeRevoked(entry) {
|
|
1061
|
+
return REVOCABLE_CREDENTIALS.includes(entry.credential.subject.assertion["@type"]);
|
|
1635
1062
|
}
|
|
1636
1063
|
};
|
|
1637
1064
|
var CredentialConsumer = class {
|
|
@@ -1664,24 +1091,24 @@ var CredentialConsumer = class {
|
|
|
1664
1091
|
|
|
1665
1092
|
// packages/core/halo/credentials/src/processor/device-state-machine.ts
|
|
1666
1093
|
import { Trigger } from "@dxos/async";
|
|
1667
|
-
import { invariant as
|
|
1668
|
-
import { PublicKey as
|
|
1669
|
-
import { log as
|
|
1670
|
-
import { ComplexMap as
|
|
1671
|
-
var
|
|
1094
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
1095
|
+
import { PublicKey as PublicKey7 } from "@dxos/keys";
|
|
1096
|
+
import { log as log3 } from "@dxos/log";
|
|
1097
|
+
import { ComplexMap as ComplexMap5 } from "@dxos/util";
|
|
1098
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/device-state-machine.ts";
|
|
1672
1099
|
var DeviceStateMachine = class {
|
|
1673
1100
|
constructor(_params) {
|
|
1674
1101
|
this._params = _params;
|
|
1675
|
-
this.authorizedDeviceKeys = new
|
|
1102
|
+
this.authorizedDeviceKeys = new ComplexMap5(PublicKey7.hash);
|
|
1676
1103
|
this.deviceChainReady = new Trigger();
|
|
1677
1104
|
}
|
|
1678
1105
|
async processCredential(credential) {
|
|
1679
|
-
|
|
1106
|
+
log3("processing credential...", {
|
|
1680
1107
|
identityKey: this._params.identityKey,
|
|
1681
1108
|
deviceKey: this._params.deviceKey,
|
|
1682
1109
|
credential
|
|
1683
1110
|
}, {
|
|
1684
|
-
F:
|
|
1111
|
+
F: __dxlog_file5,
|
|
1685
1112
|
L: 35,
|
|
1686
1113
|
S: this,
|
|
1687
1114
|
C: (f, a) => f(...a)
|
|
@@ -1695,8 +1122,8 @@ var DeviceStateMachine = class {
|
|
|
1695
1122
|
const assertion = getCredentialAssertion(credential);
|
|
1696
1123
|
switch (assertion["@type"]) {
|
|
1697
1124
|
case "dxos.halo.credentials.AuthorizedDevice": {
|
|
1698
|
-
|
|
1699
|
-
F:
|
|
1125
|
+
invariant4(!this.authorizedDeviceKeys.has(assertion.deviceKey), "Device already added.", {
|
|
1126
|
+
F: __dxlog_file5,
|
|
1700
1127
|
L: 51,
|
|
1701
1128
|
S: this,
|
|
1702
1129
|
A: [
|
|
@@ -1705,12 +1132,12 @@ var DeviceStateMachine = class {
|
|
|
1705
1132
|
]
|
|
1706
1133
|
});
|
|
1707
1134
|
this.authorizedDeviceKeys.set(assertion.deviceKey, {});
|
|
1708
|
-
|
|
1135
|
+
log3("added device", {
|
|
1709
1136
|
localDeviceKey: this._params.deviceKey,
|
|
1710
1137
|
deviceKey: assertion.deviceKey,
|
|
1711
1138
|
size: this.authorizedDeviceKeys.size
|
|
1712
1139
|
}, {
|
|
1713
|
-
F:
|
|
1140
|
+
F: __dxlog_file5,
|
|
1714
1141
|
L: 54,
|
|
1715
1142
|
S: this,
|
|
1716
1143
|
C: (f, a) => f(...a)
|
|
@@ -1719,8 +1146,8 @@ var DeviceStateMachine = class {
|
|
|
1719
1146
|
break;
|
|
1720
1147
|
}
|
|
1721
1148
|
case "dxos.halo.credentials.DeviceProfile": {
|
|
1722
|
-
|
|
1723
|
-
F:
|
|
1149
|
+
invariant4(this.authorizedDeviceKeys.has(credential.subject.id), "Device not found.", {
|
|
1150
|
+
F: __dxlog_file5,
|
|
1724
1151
|
L: 64,
|
|
1725
1152
|
S: this,
|
|
1726
1153
|
A: [
|
|
@@ -1729,11 +1156,11 @@ var DeviceStateMachine = class {
|
|
|
1729
1156
|
]
|
|
1730
1157
|
});
|
|
1731
1158
|
if (assertion && credential.subject.id.equals(this._params.deviceKey)) {
|
|
1732
|
-
|
|
1159
|
+
log3.trace("dxos.halo.device", {
|
|
1733
1160
|
deviceKey: credential.subject.id,
|
|
1734
1161
|
profile: assertion.profile
|
|
1735
1162
|
}, {
|
|
1736
|
-
F:
|
|
1163
|
+
F: __dxlog_file5,
|
|
1737
1164
|
L: 67,
|
|
1738
1165
|
S: this,
|
|
1739
1166
|
C: (f, a) => f(...a)
|
|
@@ -1748,8 +1175,8 @@ var DeviceStateMachine = class {
|
|
|
1748
1175
|
};
|
|
1749
1176
|
|
|
1750
1177
|
// packages/core/halo/credentials/src/processor/profile-state-machine.ts
|
|
1751
|
-
import { log as
|
|
1752
|
-
var
|
|
1178
|
+
import { log as log4 } from "@dxos/log";
|
|
1179
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/profile-state-machine.ts";
|
|
1753
1180
|
var ProfileStateMachine = class {
|
|
1754
1181
|
constructor(_params) {
|
|
1755
1182
|
this._params = _params;
|
|
@@ -1759,11 +1186,11 @@ var ProfileStateMachine = class {
|
|
|
1759
1186
|
switch (assertion["@type"]) {
|
|
1760
1187
|
case "dxos.halo.credentials.IdentityProfile": {
|
|
1761
1188
|
if (!credential.issuer.equals(this._params.identityKey) || !credential.subject.id.equals(this._params.identityKey)) {
|
|
1762
|
-
|
|
1189
|
+
log4.warn("Invalid profile credential", {
|
|
1763
1190
|
expectedIdentity: this._params.identityKey,
|
|
1764
1191
|
credential
|
|
1765
1192
|
}, {
|
|
1766
|
-
F:
|
|
1193
|
+
F: __dxlog_file6,
|
|
1767
1194
|
L: 34,
|
|
1768
1195
|
S: this,
|
|
1769
1196
|
C: (f, a) => f(...a)
|
|
@@ -1771,11 +1198,11 @@ var ProfileStateMachine = class {
|
|
|
1771
1198
|
return;
|
|
1772
1199
|
}
|
|
1773
1200
|
this.profile = assertion.profile;
|
|
1774
|
-
|
|
1201
|
+
log4("updated profile", {
|
|
1775
1202
|
identityKey: this._params.identityKey,
|
|
1776
1203
|
profile: this.profile
|
|
1777
1204
|
}, {
|
|
1778
|
-
F:
|
|
1205
|
+
F: __dxlog_file6,
|
|
1779
1206
|
L: 40,
|
|
1780
1207
|
S: this,
|
|
1781
1208
|
C: (f, a) => f(...a)
|