@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
package/dist/lib/node/index.cjs
CHANGED
|
@@ -88,18 +88,13 @@ var import_util4 = require("@dxos/util");
|
|
|
88
88
|
var import_invariant3 = require("@dxos/invariant");
|
|
89
89
|
var import_keys6 = require("@dxos/keys");
|
|
90
90
|
var import_log2 = require("@dxos/log");
|
|
91
|
-
var import_credentials3 = require("@dxos/protocols/proto/dxos/halo/credentials");
|
|
92
91
|
var import_util5 = require("@dxos/util");
|
|
92
|
+
var import_async2 = require("@dxos/async");
|
|
93
93
|
var import_invariant4 = require("@dxos/invariant");
|
|
94
94
|
var import_keys7 = require("@dxos/keys");
|
|
95
95
|
var import_log3 = require("@dxos/log");
|
|
96
96
|
var import_util6 = require("@dxos/util");
|
|
97
|
-
var import_async2 = require("@dxos/async");
|
|
98
|
-
var import_invariant5 = require("@dxos/invariant");
|
|
99
|
-
var import_keys8 = require("@dxos/keys");
|
|
100
97
|
var import_log4 = require("@dxos/log");
|
|
101
|
-
var import_util7 = require("@dxos/util");
|
|
102
|
-
var import_log5 = require("@dxos/log");
|
|
103
98
|
var defaultSecretProvider = async () => Buffer.from("0000");
|
|
104
99
|
var defaultSecretValidator = async (invitation, secret) => true;
|
|
105
100
|
var generatePasscode = (length = 4) => {
|
|
@@ -184,9 +179,6 @@ var checkCredentialType = (credential, type) => credential.subject.assertion["@t
|
|
|
184
179
|
var credentialTypeFilter = (type) => (credential) => checkCredentialType(credential, type);
|
|
185
180
|
var SIGNATURE_TYPE_ED25519 = "ED25519Signature";
|
|
186
181
|
var verifyCredential = async (credential) => {
|
|
187
|
-
if (credential.parentCredentialIds?.length === 0) {
|
|
188
|
-
delete credential.parentCredentialIds;
|
|
189
|
-
}
|
|
190
182
|
if (!credential.issuer.equals(credential.proof.signer)) {
|
|
191
183
|
if (!credential.proof.chain) {
|
|
192
184
|
return {
|
|
@@ -249,10 +241,10 @@ var verifyChain = async (chain, authority, subject) => {
|
|
|
249
241
|
};
|
|
250
242
|
};
|
|
251
243
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/credentials/credential-factory.ts";
|
|
252
|
-
var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce
|
|
244
|
+
var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce }) => {
|
|
253
245
|
(0, import_invariant.invariant)(assertion["@type"], "Invalid assertion.", {
|
|
254
246
|
F: __dxlog_file,
|
|
255
|
-
L:
|
|
247
|
+
L: 45,
|
|
256
248
|
S: void 0,
|
|
257
249
|
A: [
|
|
258
250
|
"assertion['@type']",
|
|
@@ -261,7 +253,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
261
253
|
});
|
|
262
254
|
(0, import_invariant.invariant)(!!signingKey === !!chain, "Chain must be provided if and only if the signing key differs from the issuer.", {
|
|
263
255
|
F: __dxlog_file,
|
|
264
|
-
L:
|
|
256
|
+
L: 46,
|
|
265
257
|
S: void 0,
|
|
266
258
|
A: [
|
|
267
259
|
"!!signingKey === !!chain",
|
|
@@ -272,7 +264,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
272
264
|
const result = await verifyChain(chain, issuer, signingKey);
|
|
273
265
|
(0, import_invariant.invariant)(result.kind === "pass", "Invalid chain.", {
|
|
274
266
|
F: __dxlog_file,
|
|
275
|
-
L:
|
|
267
|
+
L: 49,
|
|
276
268
|
S: void 0,
|
|
277
269
|
A: [
|
|
278
270
|
"result.kind === 'pass'",
|
|
@@ -295,9 +287,6 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
|
|
|
295
287
|
nonce
|
|
296
288
|
}
|
|
297
289
|
};
|
|
298
|
-
if ((parentCredentialIds?.length ?? 0) > 0) {
|
|
299
|
-
credential.parentCredentialIds = parentCredentialIds;
|
|
300
|
-
}
|
|
301
290
|
const signedPayload = getCredentialProofPayload(credential);
|
|
302
291
|
credential.proof.value = await signer.sign(signingKey ?? issuer, signedPayload);
|
|
303
292
|
if (chain) {
|
|
@@ -314,13 +303,12 @@ var createCredentialMessage = (credential) => {
|
|
|
314
303
|
};
|
|
315
304
|
var createCredentialSignerWithKey = (signer, issuer) => ({
|
|
316
305
|
getIssuer: () => issuer,
|
|
317
|
-
createCredential: ({ subject, assertion, nonce
|
|
306
|
+
createCredential: ({ subject, assertion, nonce }) => createCredential({
|
|
318
307
|
signer,
|
|
319
308
|
issuer,
|
|
320
309
|
subject,
|
|
321
310
|
assertion,
|
|
322
|
-
nonce
|
|
323
|
-
parentCredentialIds
|
|
311
|
+
nonce
|
|
324
312
|
})
|
|
325
313
|
});
|
|
326
314
|
var createCredentialSignerWithChain = (signer, chain, signingKey) => ({
|
|
@@ -384,7 +372,7 @@ var CredentialGenerator = class {
|
|
|
384
372
|
assertion: {
|
|
385
373
|
"@type": "dxos.halo.credentials.SpaceMember",
|
|
386
374
|
spaceKey,
|
|
387
|
-
role: import_credentials.SpaceMember.Role.
|
|
375
|
+
role: import_credentials.SpaceMember.Role.MEMBER,
|
|
388
376
|
genesisFeedKey
|
|
389
377
|
}
|
|
390
378
|
}),
|
|
@@ -479,15 +467,14 @@ var createDeviceAuthorization = async (signer, identityKey, deviceKey) => {
|
|
|
479
467
|
credential
|
|
480
468
|
}));
|
|
481
469
|
};
|
|
482
|
-
var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey,
|
|
470
|
+
var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey, profile, invitationCredentialId) => {
|
|
483
471
|
const credentials = await Promise.all([
|
|
484
472
|
await signer.createCredential({
|
|
485
473
|
subject: identityKey,
|
|
486
|
-
parentCredentialIds: membershipChainHeads,
|
|
487
474
|
assertion: {
|
|
488
475
|
"@type": "dxos.halo.credentials.SpaceMember",
|
|
489
476
|
spaceKey,
|
|
490
|
-
role,
|
|
477
|
+
role: import_credentials.SpaceMember.Role.ADMIN,
|
|
491
478
|
profile,
|
|
492
479
|
genesisFeedKey,
|
|
493
480
|
invitationCredentialId
|
|
@@ -748,574 +735,21 @@ var InvitationStateMachine = class {
|
|
|
748
735
|
}
|
|
749
736
|
}
|
|
750
737
|
};
|
|
751
|
-
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/
|
|
752
|
-
var CredentialGraph = class {
|
|
753
|
-
constructor(_stateHandler) {
|
|
754
|
-
this._stateHandler = _stateHandler;
|
|
755
|
-
this._vertexIdGenerator = 1;
|
|
756
|
-
this._root = {
|
|
757
|
-
id: -1,
|
|
758
|
-
parents: [],
|
|
759
|
-
children: []
|
|
760
|
-
};
|
|
761
|
-
this._sentinel = {
|
|
762
|
-
id: -2,
|
|
763
|
-
parents: [],
|
|
764
|
-
children: []
|
|
765
|
-
};
|
|
766
|
-
this._vertexByCredentialId = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
767
|
-
this._subjectToVertex = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
768
|
-
this._subjectToState = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
769
|
-
this.onSubjectStateChanged = new import_util6.Callback();
|
|
770
|
-
}
|
|
771
|
-
getSubjectState(subjectId) {
|
|
772
|
-
return this._subjectToState.get(subjectId);
|
|
773
|
-
}
|
|
774
|
-
getState() {
|
|
775
|
-
return this._subjectToState;
|
|
776
|
-
}
|
|
777
|
-
getLeafIds() {
|
|
778
|
-
return this._sentinel.parents.map((v) => v.credential.id);
|
|
779
|
-
}
|
|
780
|
-
getGlobalStateScope() {
|
|
781
|
-
return {
|
|
782
|
-
state: this._subjectToVertex
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
addVertex(credential, assertion) {
|
|
786
|
-
const newVertex = {
|
|
787
|
-
id: this._vertexIdGenerator++,
|
|
788
|
-
credential,
|
|
789
|
-
assertion,
|
|
790
|
-
parents: [],
|
|
791
|
-
children: []
|
|
792
|
-
};
|
|
793
|
-
this._vertexByCredentialId.set(credential.id, newVertex);
|
|
794
|
-
const parentIds = credential.parentCredentialIds ?? [];
|
|
795
|
-
if (parentIds.length === 0) {
|
|
796
|
-
this._root.children.push(newVertex);
|
|
797
|
-
newVertex.parents.push(this._root);
|
|
798
|
-
} else {
|
|
799
|
-
for (const parentId of parentIds) {
|
|
800
|
-
const parentVertex = this._vertexByCredentialId.get(parentId);
|
|
801
|
-
if (parentVertex == null) {
|
|
802
|
-
import_log3.log.error("credential skipped because of the unknown parent", {
|
|
803
|
-
credential,
|
|
804
|
-
parentId
|
|
805
|
-
}, {
|
|
806
|
-
F: __dxlog_file3,
|
|
807
|
-
L: 72,
|
|
808
|
-
S: this,
|
|
809
|
-
C: (f, a) => f(...a)
|
|
810
|
-
});
|
|
811
|
-
continue;
|
|
812
|
-
}
|
|
813
|
-
parentVertex.children.push(newVertex);
|
|
814
|
-
newVertex.parents.push(parentVertex);
|
|
815
|
-
this._removeSentinelConnection(parentVertex);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
newVertex.children.push(this._sentinel);
|
|
819
|
-
this._sentinel.parents.push(newVertex);
|
|
820
|
-
return this._onVertexInserted(newVertex);
|
|
821
|
-
}
|
|
822
|
-
_removeSentinelConnection(vertex) {
|
|
823
|
-
const sentinelIdx = vertex.children.indexOf(this._sentinel);
|
|
824
|
-
if (sentinelIdx >= 0) {
|
|
825
|
-
vertex.children.splice(sentinelIdx, 1);
|
|
826
|
-
const vertexInSentinelIdx = this._sentinel.parents.indexOf(vertex);
|
|
827
|
-
(0, import_invariant4.invariant)(vertexInSentinelIdx >= 0, void 0, {
|
|
828
|
-
F: __dxlog_file3,
|
|
829
|
-
L: 90,
|
|
830
|
-
S: this,
|
|
831
|
-
A: [
|
|
832
|
-
"vertexInSentinelIdx >= 0",
|
|
833
|
-
""
|
|
834
|
-
]
|
|
835
|
-
});
|
|
836
|
-
this._sentinel.parents.splice(vertexInSentinelIdx, 1);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
async _onVertexInserted(newVertex) {
|
|
840
|
-
const { credential, assertion } = newVertex;
|
|
841
|
-
(0, import_invariant4.invariant)(credential, void 0, {
|
|
842
|
-
F: __dxlog_file3,
|
|
843
|
-
L: 97,
|
|
844
|
-
S: this,
|
|
845
|
-
A: [
|
|
846
|
-
"credential",
|
|
847
|
-
""
|
|
848
|
-
]
|
|
849
|
-
});
|
|
850
|
-
let changedSubjects = [];
|
|
851
|
-
const isUpdateAppliedOnTopOfThePreviousState = this._sentinel.parents.length === 1;
|
|
852
|
-
if (isUpdateAppliedOnTopOfThePreviousState) {
|
|
853
|
-
const subjectId = credential.subject.id;
|
|
854
|
-
if (this._stateHandler.isUpdateAllowed(this.getGlobalStateScope(), credential, assertion)) {
|
|
855
|
-
const newSubjectState = this._stateHandler.createState(credential, newVertex.assertion);
|
|
856
|
-
const prevSubjectState = this._subjectToState.get(subjectId);
|
|
857
|
-
this._subjectToState.set(subjectId, newSubjectState);
|
|
858
|
-
this._subjectToVertex.set(subjectId, newVertex);
|
|
859
|
-
if (this._stateHandler.hasStateChanged(newSubjectState, prevSubjectState)) {
|
|
860
|
-
changedSubjects.push(newSubjectState);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
} else {
|
|
864
|
-
changedSubjects = this._recomputeState();
|
|
865
|
-
}
|
|
866
|
-
if (changedSubjects.length > 0) {
|
|
867
|
-
await this.onSubjectStateChanged.callIfSet(changedSubjects);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* DFS the graph from root to sentinel pausing on merge points (nodes with multiple parents).
|
|
872
|
-
* Continue after all paths leading to a merge point converge by merging their states.
|
|
873
|
-
* In case of a concurrent update paths are replayed taking into account the state set
|
|
874
|
-
* by the winning branch.
|
|
875
|
-
*/
|
|
876
|
-
_recomputeState() {
|
|
877
|
-
const pendingPaths = /* @__PURE__ */ new Map();
|
|
878
|
-
const paths = [
|
|
879
|
-
this._createRootPath()
|
|
880
|
-
];
|
|
881
|
-
let lastPath = null;
|
|
882
|
-
while (lastPath == null) {
|
|
883
|
-
const path = paths.pop();
|
|
884
|
-
(0, import_log3.log)("visit vertex", {
|
|
885
|
-
id: path.head.id
|
|
886
|
-
}, {
|
|
887
|
-
F: __dxlog_file3,
|
|
888
|
-
L: 132,
|
|
889
|
-
S: this,
|
|
890
|
-
C: (f, a) => f(...a)
|
|
891
|
-
});
|
|
892
|
-
this._updatePathState(path);
|
|
893
|
-
const convergedPaths = this._handleMergePoint(paths, pendingPaths, path);
|
|
894
|
-
if (convergedPaths == null) {
|
|
895
|
-
(0, import_log3.log)("waiting for other paths", void 0, {
|
|
896
|
-
F: __dxlog_file3,
|
|
897
|
-
L: 136,
|
|
898
|
-
S: this,
|
|
899
|
-
C: (f, a) => f(...a)
|
|
900
|
-
});
|
|
901
|
-
continue;
|
|
902
|
-
}
|
|
903
|
-
const mergeResult = this._mergePaths(convergedPaths);
|
|
904
|
-
if (mergeResult.type === "replay_required") {
|
|
905
|
-
this._replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths);
|
|
906
|
-
continue;
|
|
907
|
-
}
|
|
908
|
-
const merged = mergeResult.path;
|
|
909
|
-
if (merged.head.children.length === 0) {
|
|
910
|
-
lastPath = merged;
|
|
911
|
-
} else if (merged.head.children.length === 1) {
|
|
912
|
-
merged.head = merged.head.children[0];
|
|
913
|
-
paths.push(merged);
|
|
914
|
-
} else {
|
|
915
|
-
this._forkTraversal(paths, merged);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
if (paths.length > 0) {
|
|
919
|
-
import_log3.log.error("traversal finished while there were active paths", {
|
|
920
|
-
paths: paths.map((p) => ({
|
|
921
|
-
path: toChosenPath(p),
|
|
922
|
-
head: p.head.id
|
|
923
|
-
}))
|
|
924
|
-
}, {
|
|
925
|
-
F: __dxlog_file3,
|
|
926
|
-
L: 155,
|
|
927
|
-
S: this,
|
|
928
|
-
C: (f, a) => f(...a)
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
return this._setCurrentState(lastPath);
|
|
932
|
-
}
|
|
933
|
-
_replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths) {
|
|
934
|
-
paths.push(...mergeResult.replay.map((path) => {
|
|
935
|
-
const stateOverrides = path.stateOverrides ?? new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
936
|
-
mergeResult.stateOverrides.forEach((value, key) => stateOverrides.set(key, value));
|
|
937
|
-
return {
|
|
938
|
-
...mergeResult.from,
|
|
939
|
-
chosenPath: path.chosenPath,
|
|
940
|
-
stateOverrides
|
|
941
|
-
};
|
|
942
|
-
}));
|
|
943
|
-
(0, import_log3.log)("replay paths", () => ({
|
|
944
|
-
count: paths.length,
|
|
945
|
-
paths: paths.map((path) => ({
|
|
946
|
-
from: mergeResult.from.head.id,
|
|
947
|
-
path: toChosenPath(path),
|
|
948
|
-
overrides: path?.stateOverrides?.mapValues((v) => this._stateHandler.toLogString(v.assertion))
|
|
949
|
-
}))
|
|
950
|
-
}), {
|
|
951
|
-
F: __dxlog_file3,
|
|
952
|
-
L: 175,
|
|
953
|
-
S: this,
|
|
954
|
-
C: (f, a) => f(...a)
|
|
955
|
-
});
|
|
956
|
-
const clearedPending = convergedPaths.filter((l) => !mergeResult.replay.includes(l));
|
|
957
|
-
pendingPaths.set(convergedPaths[0].head.id, clearedPending);
|
|
958
|
-
}
|
|
959
|
-
_handleMergePoint(paths, pendingPaths, path) {
|
|
960
|
-
const pendingList = pendingPaths.get(path.head.id) ?? [];
|
|
961
|
-
pendingPaths.set(path.head.id, pendingList);
|
|
962
|
-
pendingList.push(path);
|
|
963
|
-
if (pendingList.length < path.head.parents.length) {
|
|
964
|
-
return null;
|
|
965
|
-
}
|
|
966
|
-
if (path.head.id === this._sentinel.id && paths.length > 0) {
|
|
967
|
-
(0, import_log3.log)("waiting for all the active paths to converge on the sentinel", void 0, {
|
|
968
|
-
F: __dxlog_file3,
|
|
969
|
-
L: 199,
|
|
970
|
-
S: this,
|
|
971
|
-
C: (f, a) => f(...a)
|
|
972
|
-
});
|
|
973
|
-
return null;
|
|
974
|
-
}
|
|
975
|
-
pendingPaths.delete(path.head.id);
|
|
976
|
-
return pendingList;
|
|
977
|
-
}
|
|
978
|
-
_updatePathState(path) {
|
|
979
|
-
const headCredential = path.head.credential;
|
|
980
|
-
if (headCredential == null) {
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
const updatedSubject = headCredential.subject.id;
|
|
984
|
-
path.credentials.add(headCredential.id);
|
|
985
|
-
let isUpdateAllowed = this._stateHandler.isUpdateAllowed(path, headCredential, path.head.assertion);
|
|
986
|
-
if (!isUpdateAllowed && path.head.parents[0]?.id === this._root.id) {
|
|
987
|
-
const globalState = this.getGlobalStateScope();
|
|
988
|
-
isUpdateAllowed = this._stateHandler.isUpdateAllowed(globalState, headCredential, path.head.assertion);
|
|
989
|
-
}
|
|
990
|
-
if (isUpdateAllowed) {
|
|
991
|
-
path.forkChangedSubjects.add(updatedSubject);
|
|
992
|
-
path.forkIssuers.add(headCredential.issuer);
|
|
993
|
-
path.state.set(updatedSubject, path.head);
|
|
994
|
-
(0, import_log3.log)("path state updated", () => ({
|
|
995
|
-
subject: updatedSubject,
|
|
996
|
-
newState: this._stateHandler.toLogString(path.head.assertion)
|
|
997
|
-
}), {
|
|
998
|
-
F: __dxlog_file3,
|
|
999
|
-
L: 223,
|
|
1000
|
-
S: this,
|
|
1001
|
-
C: (f, a) => f(...a)
|
|
1002
|
-
});
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
_forkTraversal(paths, path) {
|
|
1006
|
-
const replayChoice = path.chosenPath?.[path.head.id];
|
|
1007
|
-
const choices = replayChoice ?? path.head.children;
|
|
1008
|
-
for (const choice of choices) {
|
|
1009
|
-
(0, import_log3.log)("edge traversal", {
|
|
1010
|
-
from: path.head.id,
|
|
1011
|
-
to: choice.id
|
|
1012
|
-
}, {
|
|
1013
|
-
F: __dxlog_file3,
|
|
1014
|
-
L: 234,
|
|
1015
|
-
S: this,
|
|
1016
|
-
C: (f, a) => f(...a)
|
|
1017
|
-
});
|
|
1018
|
-
const fork = {
|
|
1019
|
-
forkPoint: path,
|
|
1020
|
-
chosenPath: {
|
|
1021
|
-
...path.chosenPath,
|
|
1022
|
-
[path.head.id]: [
|
|
1023
|
-
choice
|
|
1024
|
-
]
|
|
1025
|
-
},
|
|
1026
|
-
head: choice,
|
|
1027
|
-
credentials: new import_util6.ComplexSet(import_keys7.PublicKey.hash, path.credentials),
|
|
1028
|
-
state: new import_util6.ComplexMap(import_keys7.PublicKey.hash, [
|
|
1029
|
-
...path.state.entries()
|
|
1030
|
-
]),
|
|
1031
|
-
forkIssuers: new import_util6.ComplexSet(import_keys7.PublicKey.hash),
|
|
1032
|
-
forkChangedSubjects: new import_util6.ComplexSet(import_keys7.PublicKey.hash),
|
|
1033
|
-
stateOverrides: path.stateOverrides
|
|
1034
|
-
};
|
|
1035
|
-
paths.push(fork);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Updates the current graph state.
|
|
1040
|
-
* @returns changed states.
|
|
1041
|
-
*/
|
|
1042
|
-
_setCurrentState(path) {
|
|
1043
|
-
const changedSubjects = [];
|
|
1044
|
-
const newStateMap = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
1045
|
-
const newVertexMap = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
1046
|
-
for (const [subjectKey, subjectVertex] of path.state.entries()) {
|
|
1047
|
-
const newState = this._stateHandler.createState(subjectVertex.credential, subjectVertex.assertion);
|
|
1048
|
-
const prevState = this._subjectToState.get(subjectKey);
|
|
1049
|
-
newStateMap.set(subjectKey, newState);
|
|
1050
|
-
newVertexMap.set(subjectKey, subjectVertex);
|
|
1051
|
-
if (this._stateHandler.hasStateChanged(newState, prevState)) {
|
|
1052
|
-
changedSubjects.push(newState);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
this._subjectToState = newStateMap;
|
|
1056
|
-
this._subjectToVertex = newVertexMap;
|
|
1057
|
-
return changedSubjects;
|
|
1058
|
-
}
|
|
1059
|
-
/*
|
|
1060
|
-
* Walk up all the fork points and return the first one present in all the paths.
|
|
1061
|
-
* We use local id to determine vertex position in the graph, because nodes can't
|
|
1062
|
-
* be inserted in the middle (between a parent and a child) and ids are monotonically increasing.
|
|
1063
|
-
*/
|
|
1064
|
-
_leastCommonAncestor(paths) {
|
|
1065
|
-
const uniqueForkPoints = paths.reduce((acc, path) => {
|
|
1066
|
-
let it = path.forkPoint;
|
|
1067
|
-
while (it) {
|
|
1068
|
-
acc.set(it.head.id, it);
|
|
1069
|
-
it = it.forkPoint;
|
|
1070
|
-
}
|
|
1071
|
-
return acc;
|
|
1072
|
-
}, /* @__PURE__ */ new Map());
|
|
1073
|
-
let maxId = this._root.id;
|
|
1074
|
-
let maxState = null;
|
|
1075
|
-
for (const [id, state] of uniqueForkPoints.entries()) {
|
|
1076
|
-
const headCredential = state.head.credential;
|
|
1077
|
-
if (headCredential != null) {
|
|
1078
|
-
const isPointInEveryPath = paths.every((p) => p.credentials.has(headCredential.id));
|
|
1079
|
-
if (isPointInEveryPath && id > maxId) {
|
|
1080
|
-
maxId = id;
|
|
1081
|
-
maxState = state;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
return maxState ?? this._createRootPath();
|
|
1086
|
-
}
|
|
1087
|
-
/**
|
|
1088
|
-
* We might be merging paths where some of them had fork points after the initial forking.
|
|
1089
|
-
* We need all the paths to point to the least common fork point and contain all the changes
|
|
1090
|
-
* that happened after it.
|
|
1091
|
-
*/
|
|
1092
|
-
_moveUpToForkPoint(forkPoint, path) {
|
|
1093
|
-
const isForkPointInPath = path.chosenPath[forkPoint.head.id] == null || path.forkPoint == null;
|
|
1094
|
-
if (isForkPointInPath) {
|
|
1095
|
-
return path;
|
|
1096
|
-
}
|
|
1097
|
-
if (forkPoint.head.id === path.forkPoint?.head.id) {
|
|
1098
|
-
return path;
|
|
1099
|
-
}
|
|
1100
|
-
let it = path.forkPoint;
|
|
1101
|
-
while (it.head.id !== forkPoint.head.id) {
|
|
1102
|
-
it.forkIssuers.forEach((iss) => path.forkIssuers.add(iss));
|
|
1103
|
-
it.forkChangedSubjects.forEach((m) => path.forkChangedSubjects.add(m));
|
|
1104
|
-
it = it.forkPoint;
|
|
1105
|
-
path.forkPoint = it;
|
|
1106
|
-
}
|
|
1107
|
-
return path;
|
|
1108
|
-
}
|
|
1109
|
-
_mergePaths(convergedPaths) {
|
|
1110
|
-
(0, import_invariant4.invariant)(convergedPaths.length >= 1, void 0, {
|
|
1111
|
-
F: __dxlog_file3,
|
|
1112
|
-
L: 324,
|
|
1113
|
-
S: this,
|
|
1114
|
-
A: [
|
|
1115
|
-
"convergedPaths.length >= 1",
|
|
1116
|
-
""
|
|
1117
|
-
]
|
|
1118
|
-
});
|
|
1119
|
-
if (convergedPaths.length === 1) {
|
|
1120
|
-
return {
|
|
1121
|
-
type: "merged",
|
|
1122
|
-
path: convergedPaths[0]
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
const forkPoint = this._leastCommonAncestor(convergedPaths);
|
|
1126
|
-
(0, import_log3.log)("merging paths", () => ({
|
|
1127
|
-
forkPointId: forkPoint.head.id,
|
|
1128
|
-
pathCount: convergedPaths.length,
|
|
1129
|
-
forkPoints: convergedPaths.map((fp) => fp.forkPoint?.head.id)
|
|
1130
|
-
}), {
|
|
1131
|
-
F: __dxlog_file3,
|
|
1132
|
-
L: 329,
|
|
1133
|
-
S: this,
|
|
1134
|
-
C: (f, a) => f(...a)
|
|
1135
|
-
});
|
|
1136
|
-
const paths = convergedPaths.map((p) => this._moveUpToForkPoint(forkPoint, p));
|
|
1137
|
-
(0, import_invariant4.invariant)(forkPoint, void 0, {
|
|
1138
|
-
F: __dxlog_file3,
|
|
1139
|
-
L: 335,
|
|
1140
|
-
S: this,
|
|
1141
|
-
A: [
|
|
1142
|
-
"forkPoint",
|
|
1143
|
-
""
|
|
1144
|
-
]
|
|
1145
|
-
});
|
|
1146
|
-
const result = {
|
|
1147
|
-
forkPoint: forkPoint.forkPoint,
|
|
1148
|
-
chosenPath: {
|
|
1149
|
-
...forkPoint.chosenPath,
|
|
1150
|
-
[forkPoint.head.id]: []
|
|
1151
|
-
},
|
|
1152
|
-
stateOverrides: forkPoint.stateOverrides,
|
|
1153
|
-
credentials: new import_util6.ComplexSet(import_keys7.PublicKey.hash, forkPoint.credentials),
|
|
1154
|
-
forkIssuers: new import_util6.ComplexSet(import_keys7.PublicKey.hash, forkPoint.forkIssuers),
|
|
1155
|
-
forkChangedSubjects: new import_util6.ComplexSet(import_keys7.PublicKey.hash, forkPoint.forkChangedSubjects),
|
|
1156
|
-
state: forkPoint.state.mapValues((v) => v),
|
|
1157
|
-
head: paths[0].head
|
|
1158
|
-
};
|
|
1159
|
-
const subjectToBranch = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
1160
|
-
for (const path of paths) {
|
|
1161
|
-
(0, import_log3.log)("processing a path", () => ({
|
|
1162
|
-
choices: toChosenPath(path),
|
|
1163
|
-
modified: path.forkChangedSubjects,
|
|
1164
|
-
forkIssuers: path.forkIssuers,
|
|
1165
|
-
state: path.state.mapValues((v) => this._stateHandler.toLogString(v.assertion))
|
|
1166
|
-
}), {
|
|
1167
|
-
F: __dxlog_file3,
|
|
1168
|
-
L: 348,
|
|
1169
|
-
S: this,
|
|
1170
|
-
C: (f, a) => f(...a)
|
|
1171
|
-
});
|
|
1172
|
-
path.forkIssuers.forEach((iss) => result.forkIssuers.add(iss));
|
|
1173
|
-
path.credentials.forEach((cred) => result.credentials.add(cred));
|
|
1174
|
-
result.chosenPath[forkPoint.head.id].push(...path.chosenPath[forkPoint.head.id] ?? []);
|
|
1175
|
-
for (const modifiedSubject of path.forkChangedSubjects) {
|
|
1176
|
-
const existingBranch = subjectToBranch.get(modifiedSubject);
|
|
1177
|
-
if (existingBranch == null || this._shouldOverrideCredential(existingBranch, path, modifiedSubject)) {
|
|
1178
|
-
subjectToBranch.set(modifiedSubject, path);
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
const replayPaths = /* @__PURE__ */ new Set();
|
|
1183
|
-
const addReplayPath = replayPaths.add.bind(replayPaths);
|
|
1184
|
-
for (const [subject, branch] of subjectToBranch.entries()) {
|
|
1185
|
-
result.forkChangedSubjects.add(subject);
|
|
1186
|
-
const vertex = branch.state.get(subject);
|
|
1187
|
-
result.state.set(subject, vertex);
|
|
1188
|
-
(0, import_log3.log)("set subject state", () => ({
|
|
1189
|
-
subject,
|
|
1190
|
-
state: this._stateHandler.toLogString(vertex.assertion)
|
|
1191
|
-
}), {
|
|
1192
|
-
F: __dxlog_file3,
|
|
1193
|
-
L: 370,
|
|
1194
|
-
S: this,
|
|
1195
|
-
C: (f, a) => f(...a)
|
|
1196
|
-
});
|
|
1197
|
-
const otherPaths = paths.filter((p) => p !== branch);
|
|
1198
|
-
this._stateHandler.getConflictingPaths(otherPaths, vertex).forEach(addReplayPath);
|
|
1199
|
-
}
|
|
1200
|
-
if (replayPaths.size > 0) {
|
|
1201
|
-
return {
|
|
1202
|
-
type: "replay_required",
|
|
1203
|
-
replay: [
|
|
1204
|
-
...replayPaths.values()
|
|
1205
|
-
],
|
|
1206
|
-
from: forkPoint,
|
|
1207
|
-
stateOverrides: subjectToBranch.mapValues((v, key) => v.state.get(key))
|
|
1208
|
-
};
|
|
1209
|
-
}
|
|
1210
|
-
return {
|
|
1211
|
-
type: "merged",
|
|
1212
|
-
path: result
|
|
1213
|
-
};
|
|
1214
|
-
}
|
|
1215
|
-
/**
|
|
1216
|
-
* A candidate credential is preferred over the existing credential if:
|
|
1217
|
-
* 1. It is the merge-point, because it's the last credential that was issued in awareness of all
|
|
1218
|
-
* the previously existing ones.
|
|
1219
|
-
* 2. A path where candidate was set contains existing credential in it, which means that the candidate
|
|
1220
|
-
* was issued after the existing credential by a legitimate issuer.
|
|
1221
|
-
* 3. A state-specific logic (_stateHandler) is able to justify using the candidate credential.
|
|
1222
|
-
* 4. The path where candidate was set has more issuers than the existing path (longer branch).
|
|
1223
|
-
* 5. The issuance time of the candidate is after the issuance time of the existing credential (LWW).
|
|
1224
|
-
*/
|
|
1225
|
-
_shouldOverrideCredential(existing, candidate, modifiedSubject) {
|
|
1226
|
-
const candidateVertex = candidate.state.get(modifiedSubject);
|
|
1227
|
-
const currentVertex = existing.state.get(modifiedSubject);
|
|
1228
|
-
if (candidateVertex.id === currentVertex.id) {
|
|
1229
|
-
return false;
|
|
1230
|
-
}
|
|
1231
|
-
const mergePointId = existing.head.id;
|
|
1232
|
-
if (candidateVertex.id === mergePointId || currentVertex.id === mergePointId) {
|
|
1233
|
-
(0, import_log3.log)("merge point chosen to break the tie", {
|
|
1234
|
-
mergePointId: existing.head.id
|
|
1235
|
-
}, {
|
|
1236
|
-
F: __dxlog_file3,
|
|
1237
|
-
L: 408,
|
|
1238
|
-
S: this,
|
|
1239
|
-
C: (f, a) => f(...a)
|
|
1240
|
-
});
|
|
1241
|
-
return mergePointId === candidateVertex.id;
|
|
1242
|
-
}
|
|
1243
|
-
const candidateCredential = candidateVertex.credential;
|
|
1244
|
-
const currentCredential = currentVertex.credential;
|
|
1245
|
-
if (existing.credentials.has(candidateCredential.id) !== candidate.credentials.has(currentCredential.id)) {
|
|
1246
|
-
(0, import_log3.log)("one of the credentials was overridden in another branch", {
|
|
1247
|
-
current: currentVertex.id,
|
|
1248
|
-
candidate: candidateVertex.id
|
|
1249
|
-
}, {
|
|
1250
|
-
F: __dxlog_file3,
|
|
1251
|
-
L: 415,
|
|
1252
|
-
S: this,
|
|
1253
|
-
C: (f, a) => f(...a)
|
|
1254
|
-
});
|
|
1255
|
-
return candidate.credentials.has(currentCredential.id);
|
|
1256
|
-
}
|
|
1257
|
-
const winningCredential = this._stateHandler.tryPickWinningUpdate(existing, currentCredential, candidate, candidateCredential);
|
|
1258
|
-
if (winningCredential != null) {
|
|
1259
|
-
return winningCredential === candidateCredential;
|
|
1260
|
-
}
|
|
1261
|
-
if (candidate.forkIssuers.size !== existing.forkIssuers.size) {
|
|
1262
|
-
(0, import_log3.log)("longer issuers branch used to break the tie", {
|
|
1263
|
-
issuerCount: [
|
|
1264
|
-
existing.forkIssuers.size,
|
|
1265
|
-
candidate.forkIssuers.size
|
|
1266
|
-
]
|
|
1267
|
-
}, {
|
|
1268
|
-
F: __dxlog_file3,
|
|
1269
|
-
L: 432,
|
|
1270
|
-
S: this,
|
|
1271
|
-
C: (f, a) => f(...a)
|
|
1272
|
-
});
|
|
1273
|
-
return candidate.forkIssuers.size > existing.forkIssuers.size;
|
|
1274
|
-
}
|
|
1275
|
-
(0, import_log3.log)("issuance date used to break the tie", void 0, {
|
|
1276
|
-
F: __dxlog_file3,
|
|
1277
|
-
L: 437,
|
|
1278
|
-
S: this,
|
|
1279
|
-
C: (f, a) => f(...a)
|
|
1280
|
-
});
|
|
1281
|
-
return candidateCredential.issuanceDate.getTime() > currentCredential.issuanceDate.getTime();
|
|
1282
|
-
}
|
|
1283
|
-
_createRootPath() {
|
|
1284
|
-
return {
|
|
1285
|
-
head: this._root,
|
|
1286
|
-
chosenPath: {},
|
|
1287
|
-
forkIssuers: new import_util6.ComplexSet(import_keys7.PublicKey.hash),
|
|
1288
|
-
forkChangedSubjects: new import_util6.ComplexSet(import_keys7.PublicKey.hash),
|
|
1289
|
-
state: new import_util6.ComplexMap(import_keys7.PublicKey.hash),
|
|
1290
|
-
credentials: new import_util6.ComplexSet(import_keys7.PublicKey.hash)
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
1294
|
-
var toChosenPath = (path) => {
|
|
1295
|
-
return Object.fromEntries(Object.entries(path.chosenPath).map(([k, vs]) => [
|
|
1296
|
-
k,
|
|
1297
|
-
vs.map((v) => v.id)
|
|
1298
|
-
]));
|
|
1299
|
-
};
|
|
1300
|
-
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
|
|
738
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
|
|
1301
739
|
var MemberStateMachine = class {
|
|
1302
740
|
constructor(_spaceKey) {
|
|
1303
741
|
this._spaceKey = _spaceKey;
|
|
1304
|
-
this.
|
|
1305
|
-
this.
|
|
1306
|
-
this.onMemberRoleChanged = this._hashgraph.onSubjectStateChanged;
|
|
742
|
+
this._members = new import_util5.ComplexMap(import_keys6.PublicKey.hash);
|
|
743
|
+
this.onMemberAdmitted = new import_util5.Callback();
|
|
1307
744
|
}
|
|
1308
745
|
get creator() {
|
|
1309
|
-
return this.
|
|
746
|
+
return this._creator;
|
|
1310
747
|
}
|
|
1311
748
|
get members() {
|
|
1312
|
-
return this.
|
|
1313
|
-
}
|
|
1314
|
-
get membershipChainHeads() {
|
|
1315
|
-
return this._hashgraph.getLeafIds();
|
|
749
|
+
return this._members;
|
|
1316
750
|
}
|
|
1317
751
|
getRole(member) {
|
|
1318
|
-
return this.
|
|
752
|
+
return this._members.get(member)?.assertion.role;
|
|
1319
753
|
}
|
|
1320
754
|
/**
|
|
1321
755
|
* Processes the SpaceMember credential.
|
|
@@ -1326,122 +760,84 @@ var MemberStateMachine = class {
|
|
|
1326
760
|
switch (assertion["@type"]) {
|
|
1327
761
|
case "dxos.halo.credentials.SpaceMember": {
|
|
1328
762
|
(0, import_invariant3.invariant)(assertion.spaceKey.equals(this._spaceKey), void 0, {
|
|
1329
|
-
F:
|
|
1330
|
-
L:
|
|
763
|
+
F: __dxlog_file3,
|
|
764
|
+
L: 58,
|
|
1331
765
|
S: this,
|
|
1332
766
|
A: [
|
|
1333
767
|
"assertion.spaceKey.equals(this._spaceKey)",
|
|
1334
768
|
""
|
|
1335
769
|
]
|
|
1336
770
|
});
|
|
1337
|
-
if (this.
|
|
1338
|
-
|
|
771
|
+
if (this._members.has(credential.subject.id)) {
|
|
772
|
+
return;
|
|
1339
773
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
774
|
+
const info = {
|
|
775
|
+
key: credential.subject.id,
|
|
776
|
+
credential,
|
|
777
|
+
assertion,
|
|
778
|
+
removed: false,
|
|
779
|
+
profile: assertion.profile
|
|
780
|
+
};
|
|
781
|
+
if (!this._creator) {
|
|
782
|
+
this._creator = info;
|
|
1342
783
|
}
|
|
1343
|
-
|
|
784
|
+
this._members.set(credential.subject.id, info);
|
|
785
|
+
(0, import_log2.log)("member added", {
|
|
786
|
+
member: credential.subject.id,
|
|
787
|
+
space: this._spaceKey,
|
|
788
|
+
role: assertion.role,
|
|
789
|
+
profile: assertion.profile
|
|
790
|
+
}, {
|
|
791
|
+
F: __dxlog_file3,
|
|
792
|
+
L: 79,
|
|
793
|
+
S: this,
|
|
794
|
+
C: (f, a) => f(...a)
|
|
795
|
+
});
|
|
796
|
+
await this.onMemberAdmitted.callIfSet(info);
|
|
1344
797
|
break;
|
|
1345
798
|
}
|
|
1346
799
|
case "dxos.halo.credentials.MemberProfile": {
|
|
1347
|
-
const member = this.
|
|
1348
|
-
if (member) {
|
|
1349
|
-
member.profile = assertion.profile;
|
|
1350
|
-
} else {
|
|
800
|
+
const member = this._members.get(credential.subject.id);
|
|
801
|
+
if (!member) {
|
|
1351
802
|
import_log2.log.warn("Member not found", {
|
|
1352
803
|
id: credential.subject.id
|
|
1353
804
|
}, {
|
|
1354
|
-
F:
|
|
1355
|
-
L:
|
|
805
|
+
F: __dxlog_file3,
|
|
806
|
+
L: 93,
|
|
1356
807
|
S: this,
|
|
1357
808
|
C: (f, a) => f(...a)
|
|
1358
809
|
});
|
|
810
|
+
} else {
|
|
811
|
+
member.profile = assertion.profile;
|
|
1359
812
|
}
|
|
1360
|
-
this._memberProfiles.set(credential.subject.id, assertion.profile);
|
|
1361
813
|
break;
|
|
1362
814
|
}
|
|
1363
815
|
default:
|
|
1364
816
|
throw new Error("Invalid assertion type");
|
|
1365
817
|
}
|
|
1366
818
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
const issuer = credential.issuer;
|
|
1382
|
-
const isChangingOwnRole = issuer.equals(credential.subject.id);
|
|
1383
|
-
if (isChangingOwnRole) {
|
|
1384
|
-
return false;
|
|
1385
|
-
}
|
|
1386
|
-
if (issuer.equals(assertion.spaceKey)) {
|
|
1387
|
-
return true;
|
|
1388
|
-
}
|
|
1389
|
-
const issuerRole = this._getRole(scope, issuer);
|
|
1390
|
-
return issuerRole === import_credentials3.SpaceMember.Role.ADMIN || issuerRole === import_credentials3.SpaceMember.Role.OWNER;
|
|
1391
|
-
}
|
|
1392
|
-
getConflictingPaths(paths, update) {
|
|
1393
|
-
if (update.assertion.role !== import_credentials3.SpaceMember.Role.REMOVED && update.assertion.role !== import_credentials3.SpaceMember.Role.EDITOR) {
|
|
1394
|
-
return [];
|
|
1395
|
-
}
|
|
1396
|
-
const memberId = update.credential.subject.id;
|
|
1397
|
-
return paths.filter((p) => p.forkIssuers.has(memberId));
|
|
1398
|
-
}
|
|
1399
|
-
tryPickWinningUpdate(scope1, update1, scope2, update2) {
|
|
1400
|
-
const path1IssuerRole = this._getRole(scope1, update1.issuer);
|
|
1401
|
-
const path2IssuerRole = this._getRole(scope2, update2.issuer);
|
|
1402
|
-
if (path2IssuerRole === import_credentials3.SpaceMember.Role.OWNER !== (path1IssuerRole === import_credentials3.SpaceMember.Role.OWNER)) {
|
|
1403
|
-
(0, import_log2.log)("owner decision used to break the tie", void 0, {
|
|
1404
|
-
F: __dxlog_file4,
|
|
1405
|
-
L: 139,
|
|
1406
|
-
S: this,
|
|
1407
|
-
C: (f, a) => f(...a)
|
|
1408
|
-
});
|
|
1409
|
-
return path1IssuerRole === import_credentials3.SpaceMember.Role.OWNER ? update1 : update2;
|
|
1410
|
-
}
|
|
1411
|
-
return null;
|
|
1412
|
-
}
|
|
1413
|
-
toLogString(assertion) {
|
|
1414
|
-
const role = assertion?.role ?? import_credentials3.SpaceMember.Role.REMOVED;
|
|
1415
|
-
return Object.entries(import_credentials3.SpaceMember.Role).find(([_, value]) => value === role)[0];
|
|
1416
|
-
}
|
|
1417
|
-
hasStateChanged(s1, s2) {
|
|
1418
|
-
return s1?.role !== s2?.role;
|
|
1419
|
-
}
|
|
1420
|
-
_getRole(scope, memberId) {
|
|
1421
|
-
if (this._ownerKey?.equals(memberId)) {
|
|
1422
|
-
return import_credentials3.SpaceMember.Role.OWNER;
|
|
1423
|
-
}
|
|
1424
|
-
const realRole = scope.state.get(memberId)?.assertion?.role ?? import_credentials3.SpaceMember.Role.REMOVED;
|
|
1425
|
-
if (scope.stateOverrides != null) {
|
|
1426
|
-
const override = scope.stateOverrides.get(memberId);
|
|
1427
|
-
if (override != null) {
|
|
1428
|
-
(0, import_log2.log)("member role overridden in path", () => ({
|
|
1429
|
-
headId: scope.head?.id,
|
|
1430
|
-
roleOverride: this.toLogString(override.assertion),
|
|
1431
|
-
realRole: this.toLogString(scope.state.get(memberId)?.assertion)
|
|
1432
|
-
}), {
|
|
1433
|
-
F: __dxlog_file4,
|
|
1434
|
-
L: 162,
|
|
1435
|
-
S: this,
|
|
1436
|
-
C: (f, a) => f(...a)
|
|
1437
|
-
});
|
|
1438
|
-
return override.assertion.role;
|
|
819
|
+
async onRevoked(revoked, revocation) {
|
|
820
|
+
(0, import_invariant3.invariant)(revoked.id, void 0, {
|
|
821
|
+
F: __dxlog_file3,
|
|
822
|
+
L: 106,
|
|
823
|
+
S: this,
|
|
824
|
+
A: [
|
|
825
|
+
"revoked.id",
|
|
826
|
+
""
|
|
827
|
+
]
|
|
828
|
+
});
|
|
829
|
+
for (const member of this._members.values()) {
|
|
830
|
+
if (member.credential.id?.equals(revoked.id)) {
|
|
831
|
+
member.removed = true;
|
|
832
|
+
break;
|
|
1439
833
|
}
|
|
1440
834
|
}
|
|
1441
|
-
return realRole;
|
|
1442
835
|
}
|
|
1443
836
|
};
|
|
1444
|
-
var
|
|
837
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/space-state-machine.ts";
|
|
838
|
+
var REVOCABLE_CREDENTIALS = [
|
|
839
|
+
"dxos.halo.credentials.SpaceMember"
|
|
840
|
+
];
|
|
1445
841
|
var SpaceStateMachine = class {
|
|
1446
842
|
constructor(_spaceKey) {
|
|
1447
843
|
this._spaceKey = _spaceKey;
|
|
@@ -1453,7 +849,7 @@ var SpaceStateMachine = class {
|
|
|
1453
849
|
this._processedCredentials = new import_util2.ComplexSet(import_keys3.PublicKey.hash);
|
|
1454
850
|
this._credentialProcessors = [];
|
|
1455
851
|
this.onCredentialProcessed = new import_util2.Callback();
|
|
1456
|
-
this.
|
|
852
|
+
this.onMemberAdmitted = this._members.onMemberAdmitted;
|
|
1457
853
|
this.onFeedAdmitted = this._feeds.onFeedAdmitted;
|
|
1458
854
|
this.onDelegatedInvitation = this._invitations.onDelegatedInvitation;
|
|
1459
855
|
this.onDelegatedInvitationRemoved = this._invitations.onDelegatedInvitationRemoved;
|
|
@@ -1464,9 +860,6 @@ var SpaceStateMachine = class {
|
|
|
1464
860
|
get members() {
|
|
1465
861
|
return this._members.members;
|
|
1466
862
|
}
|
|
1467
|
-
get membershipChainHeads() {
|
|
1468
|
-
return this._members.membershipChainHeads;
|
|
1469
|
-
}
|
|
1470
863
|
get feeds() {
|
|
1471
864
|
return this._feeds.feeds;
|
|
1472
865
|
}
|
|
@@ -1519,8 +912,8 @@ var SpaceStateMachine = class {
|
|
|
1519
912
|
const result = await verifyCredential(credential);
|
|
1520
913
|
if (result.kind !== "pass") {
|
|
1521
914
|
import_log.log.warn(`Invalid credential: ${result.errors.join(", ")}`, void 0, {
|
|
1522
|
-
F:
|
|
1523
|
-
L:
|
|
915
|
+
F: __dxlog_file4,
|
|
916
|
+
L: 149,
|
|
1524
917
|
S: this,
|
|
1525
918
|
C: (f, a) => f(...a)
|
|
1526
919
|
});
|
|
@@ -1529,11 +922,43 @@ var SpaceStateMachine = class {
|
|
|
1529
922
|
}
|
|
1530
923
|
const assertion = getCredentialAssertion(credential);
|
|
1531
924
|
switch (assertion["@type"]) {
|
|
925
|
+
case "dxos.halo.credentials.Revocation": {
|
|
926
|
+
if (!this._canRevokeCredentials(credential.issuer)) {
|
|
927
|
+
import_log.log.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
928
|
+
F: __dxlog_file4,
|
|
929
|
+
L: 158,
|
|
930
|
+
S: this,
|
|
931
|
+
C: (f, a) => f(...a)
|
|
932
|
+
});
|
|
933
|
+
return false;
|
|
934
|
+
}
|
|
935
|
+
const toBeRevoked = this._credentialsById.get(credential.subject.id);
|
|
936
|
+
if (!toBeRevoked) {
|
|
937
|
+
import_log.log.warn(`Credential to revoke not found: ${credential.subject.id}`, void 0, {
|
|
938
|
+
F: __dxlog_file4,
|
|
939
|
+
L: 163,
|
|
940
|
+
S: this,
|
|
941
|
+
C: (f, a) => f(...a)
|
|
942
|
+
});
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
if (!this._credentialCanBeRevoked(toBeRevoked)) {
|
|
946
|
+
import_log.log.warn(`Credential cannot be revoked: ${credential.subject.id}`, void 0, {
|
|
947
|
+
F: __dxlog_file4,
|
|
948
|
+
L: 167,
|
|
949
|
+
S: this,
|
|
950
|
+
C: (f, a) => f(...a)
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
toBeRevoked.revoked = true;
|
|
954
|
+
await this._members.onRevoked(toBeRevoked.credential, credential);
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
1532
957
|
case "dxos.halo.credentials.SpaceGenesis": {
|
|
1533
958
|
if (this._genesisCredential) {
|
|
1534
959
|
import_log.log.warn("Space already has a genesis credential.", void 0, {
|
|
1535
|
-
F:
|
|
1536
|
-
L:
|
|
960
|
+
F: __dxlog_file4,
|
|
961
|
+
L: 176,
|
|
1537
962
|
S: this,
|
|
1538
963
|
C: (f, a) => f(...a)
|
|
1539
964
|
});
|
|
@@ -1541,8 +966,8 @@ var SpaceStateMachine = class {
|
|
|
1541
966
|
}
|
|
1542
967
|
if (!credential.issuer.equals(this._spaceKey)) {
|
|
1543
968
|
import_log.log.warn("Space genesis credential must be issued by space.", void 0, {
|
|
1544
|
-
F:
|
|
1545
|
-
L:
|
|
969
|
+
F: __dxlog_file4,
|
|
970
|
+
L: 180,
|
|
1546
971
|
S: this,
|
|
1547
972
|
C: (f, a) => f(...a)
|
|
1548
973
|
});
|
|
@@ -1550,8 +975,8 @@ var SpaceStateMachine = class {
|
|
|
1550
975
|
}
|
|
1551
976
|
if (!credential.subject.id.equals(this._spaceKey)) {
|
|
1552
977
|
import_log.log.warn("Space genesis credential must be issued to space.", void 0, {
|
|
1553
|
-
F:
|
|
1554
|
-
L:
|
|
978
|
+
F: __dxlog_file4,
|
|
979
|
+
L: 184,
|
|
1555
980
|
S: this,
|
|
1556
981
|
C: (f, a) => f(...a)
|
|
1557
982
|
});
|
|
@@ -1566,8 +991,8 @@ var SpaceStateMachine = class {
|
|
|
1566
991
|
}
|
|
1567
992
|
if (!this._genesisCredential) {
|
|
1568
993
|
import_log.log.warn("Space must have a genesis credential before adding members.", void 0, {
|
|
1569
|
-
F:
|
|
1570
|
-
L:
|
|
994
|
+
F: __dxlog_file4,
|
|
995
|
+
L: 198,
|
|
1571
996
|
S: this,
|
|
1572
997
|
C: (f, a) => f(...a)
|
|
1573
998
|
});
|
|
@@ -1575,8 +1000,8 @@ var SpaceStateMachine = class {
|
|
|
1575
1000
|
}
|
|
1576
1001
|
if (!this._canInviteNewMembers(credential.issuer)) {
|
|
1577
1002
|
import_log.log.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
1578
|
-
F:
|
|
1579
|
-
L:
|
|
1003
|
+
F: __dxlog_file4,
|
|
1004
|
+
L: 202,
|
|
1580
1005
|
S: this,
|
|
1581
1006
|
C: (f, a) => f(...a)
|
|
1582
1007
|
});
|
|
@@ -1589,8 +1014,8 @@ var SpaceStateMachine = class {
|
|
|
1589
1014
|
case "dxos.halo.credentials.MemberProfile": {
|
|
1590
1015
|
if (!this._genesisCredential) {
|
|
1591
1016
|
import_log.log.warn("Space must have a genesis credential before adding members.", void 0, {
|
|
1592
|
-
F:
|
|
1593
|
-
L:
|
|
1017
|
+
F: __dxlog_file4,
|
|
1018
|
+
L: 213,
|
|
1594
1019
|
S: this,
|
|
1595
1020
|
C: (f, a) => f(...a)
|
|
1596
1021
|
});
|
|
@@ -1602,8 +1027,8 @@ var SpaceStateMachine = class {
|
|
|
1602
1027
|
case "dxos.halo.credentials.AdmittedFeed": {
|
|
1603
1028
|
if (!this._genesisCredential) {
|
|
1604
1029
|
import_log.log.warn("Space must have a genesis credential before admitting feeds.", void 0, {
|
|
1605
|
-
F:
|
|
1606
|
-
L:
|
|
1030
|
+
F: __dxlog_file4,
|
|
1031
|
+
L: 223,
|
|
1607
1032
|
S: this,
|
|
1608
1033
|
C: (f, a) => f(...a)
|
|
1609
1034
|
});
|
|
@@ -1611,8 +1036,8 @@ var SpaceStateMachine = class {
|
|
|
1611
1036
|
}
|
|
1612
1037
|
if (!this._canAdmitFeeds(credential.issuer)) {
|
|
1613
1038
|
import_log.log.warn(`Space member is not authorized to admit feeds: ${credential.issuer}`, void 0, {
|
|
1614
|
-
F:
|
|
1615
|
-
L:
|
|
1039
|
+
F: __dxlog_file4,
|
|
1040
|
+
L: 227,
|
|
1616
1041
|
S: this,
|
|
1617
1042
|
C: (f, a) => f(...a)
|
|
1618
1043
|
});
|
|
@@ -1625,8 +1050,8 @@ var SpaceStateMachine = class {
|
|
|
1625
1050
|
case "dxos.halo.invitations.DelegateSpaceInvitation": {
|
|
1626
1051
|
if (!this._canInviteNewMembers(credential.issuer)) {
|
|
1627
1052
|
import_log.log.warn(`Invalid invitation, space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
|
|
1628
|
-
F:
|
|
1629
|
-
L:
|
|
1053
|
+
F: __dxlog_file4,
|
|
1054
|
+
L: 238,
|
|
1630
1055
|
S: this,
|
|
1631
1056
|
C: (f, a) => f(...a)
|
|
1632
1057
|
});
|
|
@@ -1654,11 +1079,17 @@ var SpaceStateMachine = class {
|
|
|
1654
1079
|
return true;
|
|
1655
1080
|
}
|
|
1656
1081
|
_canInviteNewMembers(key) {
|
|
1657
|
-
return key.equals(this._spaceKey) || this._members.getRole(key) === import_credentials2.SpaceMember.Role.ADMIN
|
|
1082
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === import_credentials2.SpaceMember.Role.ADMIN;
|
|
1658
1083
|
}
|
|
1659
1084
|
_canAdmitFeeds(key) {
|
|
1660
1085
|
const role = this._members.getRole(key);
|
|
1661
|
-
return role === import_credentials2.SpaceMember.Role.
|
|
1086
|
+
return role === import_credentials2.SpaceMember.Role.MEMBER || role === import_credentials2.SpaceMember.Role.ADMIN;
|
|
1087
|
+
}
|
|
1088
|
+
_canRevokeCredentials(key) {
|
|
1089
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === import_credentials2.SpaceMember.Role.ADMIN;
|
|
1090
|
+
}
|
|
1091
|
+
_credentialCanBeRevoked(entry) {
|
|
1092
|
+
return REVOCABLE_CREDENTIALS.includes(entry.credential.subject.assertion["@type"]);
|
|
1662
1093
|
}
|
|
1663
1094
|
};
|
|
1664
1095
|
var CredentialConsumer = class {
|
|
@@ -1688,20 +1119,20 @@ var CredentialConsumer = class {
|
|
|
1688
1119
|
await this._onClose();
|
|
1689
1120
|
}
|
|
1690
1121
|
};
|
|
1691
|
-
var
|
|
1122
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/device-state-machine.ts";
|
|
1692
1123
|
var DeviceStateMachine = class {
|
|
1693
1124
|
constructor(_params) {
|
|
1694
1125
|
this._params = _params;
|
|
1695
|
-
this.authorizedDeviceKeys = new
|
|
1126
|
+
this.authorizedDeviceKeys = new import_util6.ComplexMap(import_keys7.PublicKey.hash);
|
|
1696
1127
|
this.deviceChainReady = new import_async2.Trigger();
|
|
1697
1128
|
}
|
|
1698
1129
|
async processCredential(credential) {
|
|
1699
|
-
(0,
|
|
1130
|
+
(0, import_log3.log)("processing credential...", {
|
|
1700
1131
|
identityKey: this._params.identityKey,
|
|
1701
1132
|
deviceKey: this._params.deviceKey,
|
|
1702
1133
|
credential
|
|
1703
1134
|
}, {
|
|
1704
|
-
F:
|
|
1135
|
+
F: __dxlog_file5,
|
|
1705
1136
|
L: 35,
|
|
1706
1137
|
S: this,
|
|
1707
1138
|
C: (f, a) => f(...a)
|
|
@@ -1715,8 +1146,8 @@ var DeviceStateMachine = class {
|
|
|
1715
1146
|
const assertion = getCredentialAssertion(credential);
|
|
1716
1147
|
switch (assertion["@type"]) {
|
|
1717
1148
|
case "dxos.halo.credentials.AuthorizedDevice": {
|
|
1718
|
-
(0,
|
|
1719
|
-
F:
|
|
1149
|
+
(0, import_invariant4.invariant)(!this.authorizedDeviceKeys.has(assertion.deviceKey), "Device already added.", {
|
|
1150
|
+
F: __dxlog_file5,
|
|
1720
1151
|
L: 51,
|
|
1721
1152
|
S: this,
|
|
1722
1153
|
A: [
|
|
@@ -1725,12 +1156,12 @@ var DeviceStateMachine = class {
|
|
|
1725
1156
|
]
|
|
1726
1157
|
});
|
|
1727
1158
|
this.authorizedDeviceKeys.set(assertion.deviceKey, {});
|
|
1728
|
-
(0,
|
|
1159
|
+
(0, import_log3.log)("added device", {
|
|
1729
1160
|
localDeviceKey: this._params.deviceKey,
|
|
1730
1161
|
deviceKey: assertion.deviceKey,
|
|
1731
1162
|
size: this.authorizedDeviceKeys.size
|
|
1732
1163
|
}, {
|
|
1733
|
-
F:
|
|
1164
|
+
F: __dxlog_file5,
|
|
1734
1165
|
L: 54,
|
|
1735
1166
|
S: this,
|
|
1736
1167
|
C: (f, a) => f(...a)
|
|
@@ -1739,8 +1170,8 @@ var DeviceStateMachine = class {
|
|
|
1739
1170
|
break;
|
|
1740
1171
|
}
|
|
1741
1172
|
case "dxos.halo.credentials.DeviceProfile": {
|
|
1742
|
-
(0,
|
|
1743
|
-
F:
|
|
1173
|
+
(0, import_invariant4.invariant)(this.authorizedDeviceKeys.has(credential.subject.id), "Device not found.", {
|
|
1174
|
+
F: __dxlog_file5,
|
|
1744
1175
|
L: 64,
|
|
1745
1176
|
S: this,
|
|
1746
1177
|
A: [
|
|
@@ -1749,11 +1180,11 @@ var DeviceStateMachine = class {
|
|
|
1749
1180
|
]
|
|
1750
1181
|
});
|
|
1751
1182
|
if (assertion && credential.subject.id.equals(this._params.deviceKey)) {
|
|
1752
|
-
|
|
1183
|
+
import_log3.log.trace("dxos.halo.device", {
|
|
1753
1184
|
deviceKey: credential.subject.id,
|
|
1754
1185
|
profile: assertion.profile
|
|
1755
1186
|
}, {
|
|
1756
|
-
F:
|
|
1187
|
+
F: __dxlog_file5,
|
|
1757
1188
|
L: 67,
|
|
1758
1189
|
S: this,
|
|
1759
1190
|
C: (f, a) => f(...a)
|
|
@@ -1766,7 +1197,7 @@ var DeviceStateMachine = class {
|
|
|
1766
1197
|
}
|
|
1767
1198
|
}
|
|
1768
1199
|
};
|
|
1769
|
-
var
|
|
1200
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/profile-state-machine.ts";
|
|
1770
1201
|
var ProfileStateMachine = class {
|
|
1771
1202
|
constructor(_params) {
|
|
1772
1203
|
this._params = _params;
|
|
@@ -1776,11 +1207,11 @@ var ProfileStateMachine = class {
|
|
|
1776
1207
|
switch (assertion["@type"]) {
|
|
1777
1208
|
case "dxos.halo.credentials.IdentityProfile": {
|
|
1778
1209
|
if (!credential.issuer.equals(this._params.identityKey) || !credential.subject.id.equals(this._params.identityKey)) {
|
|
1779
|
-
|
|
1210
|
+
import_log4.log.warn("Invalid profile credential", {
|
|
1780
1211
|
expectedIdentity: this._params.identityKey,
|
|
1781
1212
|
credential
|
|
1782
1213
|
}, {
|
|
1783
|
-
F:
|
|
1214
|
+
F: __dxlog_file6,
|
|
1784
1215
|
L: 34,
|
|
1785
1216
|
S: this,
|
|
1786
1217
|
C: (f, a) => f(...a)
|
|
@@ -1788,11 +1219,11 @@ var ProfileStateMachine = class {
|
|
|
1788
1219
|
return;
|
|
1789
1220
|
}
|
|
1790
1221
|
this.profile = assertion.profile;
|
|
1791
|
-
(0,
|
|
1222
|
+
(0, import_log4.log)("updated profile", {
|
|
1792
1223
|
identityKey: this._params.identityKey,
|
|
1793
1224
|
profile: this.profile
|
|
1794
1225
|
}, {
|
|
1795
|
-
F:
|
|
1226
|
+
F: __dxlog_file6,
|
|
1796
1227
|
L: 40,
|
|
1797
1228
|
S: this,
|
|
1798
1229
|
C: (f, a) => f(...a)
|