cojson 0.10.15 → 0.11.3
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +20 -0
- package/dist/CoValuesStore.d.ts +12 -0
- package/dist/CoValuesStore.d.ts.map +1 -0
- package/dist/PeerKnownStates.d.ts +38 -0
- package/dist/PeerKnownStates.d.ts.map +1 -0
- package/dist/PeerState.d.ts +46 -0
- package/dist/PeerState.d.ts.map +1 -0
- package/dist/PriorityBasedMessageQueue.d.ts +18 -0
- package/dist/PriorityBasedMessageQueue.d.ts.map +1 -0
- package/dist/SyncStateManager.d.ts +20 -0
- package/dist/SyncStateManager.d.ts.map +1 -0
- package/dist/base64url.d.ts +3 -0
- package/dist/base64url.d.ts.map +1 -0
- package/dist/base64url.test.d.ts +2 -0
- package/dist/base64url.test.d.ts.map +1 -0
- package/dist/coValue.d.ts +52 -0
- package/dist/coValue.d.ts.map +1 -0
- package/dist/coValueCore.d.ts +143 -0
- package/dist/coValueCore.d.ts.map +1 -0
- package/dist/coValueCore.js +7 -4
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts +58 -0
- package/dist/coValueState.d.ts.map +1 -0
- package/dist/coValueState.js +6 -2
- package/dist/coValueState.js.map +1 -1
- package/dist/coValues/account.d.ts +69 -0
- package/dist/coValues/account.d.ts.map +1 -0
- package/dist/coValues/coList.d.ts +163 -0
- package/dist/coValues/coList.d.ts.map +1 -0
- package/dist/coValues/coMap.d.ts +142 -0
- package/dist/coValues/coMap.d.ts.map +1 -0
- package/dist/coValues/coPlainText.d.ts +33 -0
- package/dist/coValues/coPlainText.d.ts.map +1 -0
- package/dist/coValues/coStream.d.ts +109 -0
- package/dist/coValues/coStream.d.ts.map +1 -0
- package/dist/coValues/group.d.ts +143 -0
- package/dist/coValues/group.d.ts.map +1 -0
- package/dist/coValues/group.js +51 -3
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +15 -0
- package/dist/coreToCoValue.d.ts.map +1 -0
- package/dist/crypto/PureJSCrypto.d.ts +50 -0
- package/dist/crypto/PureJSCrypto.d.ts.map +1 -0
- package/dist/crypto/PureJSCrypto.js +1 -1
- package/dist/crypto/PureJSCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +49 -0
- package/dist/crypto/WasmCrypto.d.ts.map +1 -0
- package/dist/crypto/WasmCrypto.js +1 -1
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/crypto.d.ts +142 -0
- package/dist/crypto/crypto.d.ts.map +1 -0
- package/dist/crypto/crypto.js +1 -1
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +84 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/ids.d.ts +23 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/jsonStringify.d.ts +7 -0
- package/dist/jsonStringify.d.ts.map +1 -0
- package/dist/jsonValue.d.ts +45 -0
- package/dist/jsonValue.d.ts.map +1 -0
- package/dist/localNode.d.ts +111 -0
- package/dist/localNode.d.ts.map +1 -0
- package/dist/localNode.js +7 -3
- package/dist/localNode.js.map +1 -1
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js.map +1 -1
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/permissions.d.ts +24 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +3 -4
- package/dist/permissions.js.map +1 -1
- package/dist/priority.d.ts +19 -0
- package/dist/priority.d.ts.map +1 -0
- package/dist/storage/FileSystem.d.ts +37 -0
- package/dist/storage/FileSystem.d.ts.map +1 -0
- package/dist/storage/chunksAndKnownStates.d.ts +7 -0
- package/dist/storage/chunksAndKnownStates.d.ts.map +1 -0
- package/dist/storage/index.d.ts +52 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +10 -11
- package/dist/storage/index.js.map +1 -1
- package/dist/streamUtils.d.ts +13 -0
- package/dist/streamUtils.d.ts.map +1 -0
- package/dist/sync.d.ts +97 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +20 -14
- package/dist/sync.js.map +1 -1
- package/dist/tests/PeerKnownStates.test.d.ts +2 -0
- package/dist/tests/PeerKnownStates.test.d.ts.map +1 -0
- package/dist/tests/PeerState.test.d.ts +2 -0
- package/dist/tests/PeerState.test.d.ts.map +1 -0
- package/dist/tests/PriorityBasedMessageQueue.test.d.ts +2 -0
- package/dist/tests/PriorityBasedMessageQueue.test.d.ts.map +1 -0
- package/dist/tests/SyncStateManager.test.d.ts +2 -0
- package/dist/tests/SyncStateManager.test.d.ts.map +1 -0
- package/dist/tests/account.test.d.ts +2 -0
- package/dist/tests/account.test.d.ts.map +1 -0
- package/dist/tests/coList.test.d.ts +2 -0
- package/dist/tests/coList.test.d.ts.map +1 -0
- package/dist/tests/coMap.test.d.ts +2 -0
- package/dist/tests/coMap.test.d.ts.map +1 -0
- package/dist/tests/coPlainText.test.d.ts +2 -0
- package/dist/tests/coPlainText.test.d.ts.map +1 -0
- package/dist/tests/coStream.test.d.ts +2 -0
- package/dist/tests/coStream.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.test.d.ts +2 -0
- package/dist/tests/coValueCore.test.d.ts.map +1 -0
- package/dist/tests/coValueState.test.d.ts +2 -0
- package/dist/tests/coValueState.test.d.ts.map +1 -0
- package/dist/tests/crypto.test.d.ts +2 -0
- package/dist/tests/crypto.test.d.ts.map +1 -0
- package/dist/tests/cryptoImpl.test.d.ts +2 -0
- package/dist/tests/cryptoImpl.test.d.ts.map +1 -0
- package/dist/tests/group.test.d.ts +2 -0
- package/dist/tests/group.test.d.ts.map +1 -0
- package/dist/tests/group.test.js +130 -0
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/logger.test.d.ts +2 -0
- package/dist/tests/logger.test.d.ts.map +1 -0
- package/dist/tests/permissions.test.d.ts +2 -0
- package/dist/tests/permissions.test.d.ts.map +1 -0
- package/dist/tests/permissions.test.js +194 -1
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/priority.test.d.ts +2 -0
- package/dist/tests/priority.test.d.ts.map +1 -0
- package/dist/tests/sync.test.d.ts +2 -0
- package/dist/tests/sync.test.d.ts.map +1 -0
- package/dist/tests/testUtils.d.ts +142 -0
- package/dist/tests/testUtils.d.ts.map +1 -0
- package/dist/tests/testUtils.js +4 -1
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts +4 -0
- package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts.map +1 -0
- package/dist/typeUtils/expectGroup.d.ts +4 -0
- package/dist/typeUtils/expectGroup.d.ts.map +1 -0
- package/dist/typeUtils/isAccountID.d.ts +4 -0
- package/dist/typeUtils/isAccountID.d.ts.map +1 -0
- package/dist/typeUtils/isCoValue.d.ts +4 -0
- package/dist/typeUtils/isCoValue.d.ts.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +6 -6
- package/src/coValueCore.ts +7 -9
- package/src/coValueState.ts +6 -2
- package/src/coValues/group.ts +84 -9
- package/src/crypto/PureJSCrypto.ts +1 -3
- package/src/crypto/WasmCrypto.ts +1 -3
- package/src/crypto/crypto.ts +1 -1
- package/src/jsonValue.ts +9 -5
- package/src/localNode.ts +7 -5
- package/src/logger.ts +26 -6
- package/src/permissions.ts +3 -4
- package/src/storage/index.ts +12 -17
- package/src/sync.ts +26 -26
- package/src/tests/group.test.ts +187 -0
- package/src/tests/permissions.test.ts +285 -0
- package/src/tests/testUtils.ts +4 -1
- package/tsconfig.json +3 -1
package/src/sync.ts
CHANGED
|
@@ -187,7 +187,7 @@ export class SyncManager {
|
|
|
187
187
|
|
|
188
188
|
if (entry.state.type !== "available") {
|
|
189
189
|
entry.loadFromPeers([peer]).catch((e: unknown) => {
|
|
190
|
-
logger.error("Error sending load
|
|
190
|
+
logger.error("Error sending load", { err: e });
|
|
191
191
|
});
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
@@ -204,7 +204,7 @@ export class SyncManager {
|
|
|
204
204
|
action: "load",
|
|
205
205
|
...coValue.knownState(),
|
|
206
206
|
}).catch((e: unknown) => {
|
|
207
|
-
logger.error("Error sending load
|
|
207
|
+
logger.error("Error sending load", { err: e });
|
|
208
208
|
});
|
|
209
209
|
}
|
|
210
210
|
}
|
|
@@ -234,7 +234,7 @@ export class SyncManager {
|
|
|
234
234
|
asDependencyOf,
|
|
235
235
|
...coValue.knownState(),
|
|
236
236
|
}).catch((e: unknown) => {
|
|
237
|
-
logger.error("Error sending known state
|
|
237
|
+
logger.error("Error sending known state", { err: e });
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
peer.toldKnownState.add(id);
|
|
@@ -262,7 +262,7 @@ export class SyncManager {
|
|
|
262
262
|
let lastYield = performance.now();
|
|
263
263
|
for (const [_i, piece] of newContentPieces.entries()) {
|
|
264
264
|
this.trySendToPeer(peer, piece).catch((e: unknown) => {
|
|
265
|
-
logger.error("Error sending content piece
|
|
265
|
+
logger.error("Error sending content piece", { err: e });
|
|
266
266
|
});
|
|
267
267
|
|
|
268
268
|
if (performance.now() - lastYield > 10) {
|
|
@@ -275,7 +275,7 @@ export class SyncManager {
|
|
|
275
275
|
};
|
|
276
276
|
|
|
277
277
|
sendPieces().catch((e) => {
|
|
278
|
-
logger.error("Error sending new content piece, retrying", e);
|
|
278
|
+
logger.error("Error sending new content piece, retrying", { err: e });
|
|
279
279
|
peer.optimisticKnownStates.dispatch({
|
|
280
280
|
type: "SET",
|
|
281
281
|
id,
|
|
@@ -358,13 +358,11 @@ export class SyncManager {
|
|
|
358
358
|
}
|
|
359
359
|
})
|
|
360
360
|
.catch((e) => {
|
|
361
|
-
logger.error(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
},
|
|
367
|
-
);
|
|
361
|
+
logger.error("Error processing messages from peer", {
|
|
362
|
+
err: e,
|
|
363
|
+
peerId: peer.id,
|
|
364
|
+
peerRole: peer.role,
|
|
365
|
+
});
|
|
368
366
|
if (peer.crashOnClose) {
|
|
369
367
|
this.local.crashed = e;
|
|
370
368
|
throw new Error(e);
|
|
@@ -404,13 +402,13 @@ export class SyncManager {
|
|
|
404
402
|
// where we can get informations about the coValue
|
|
405
403
|
if (msg.header || Object.keys(msg.sessions).length > 0) {
|
|
406
404
|
entry.loadFromPeers([peer]).catch((e) => {
|
|
407
|
-
logger.error("Error loading coValue in handleLoad", e);
|
|
405
|
+
logger.error("Error loading coValue in handleLoad", { err: e });
|
|
408
406
|
});
|
|
409
407
|
}
|
|
410
408
|
return;
|
|
411
409
|
} else {
|
|
412
410
|
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
|
|
413
|
-
logger.error("Error loading coValue in handleLoad", e);
|
|
411
|
+
logger.error("Error loading coValue in handleLoad", { err: e });
|
|
414
412
|
});
|
|
415
413
|
}
|
|
416
414
|
}
|
|
@@ -437,7 +435,7 @@ export class SyncManager {
|
|
|
437
435
|
header: false,
|
|
438
436
|
sessions: {},
|
|
439
437
|
}).catch((e) => {
|
|
440
|
-
logger.error("Error sending known state back", e);
|
|
438
|
+
logger.error("Error sending known state back", { err: e });
|
|
441
439
|
});
|
|
442
440
|
|
|
443
441
|
return;
|
|
@@ -447,7 +445,9 @@ export class SyncManager {
|
|
|
447
445
|
await this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
448
446
|
})
|
|
449
447
|
.catch((e) => {
|
|
450
|
-
logger.error("Error loading coValue in handleLoad loading state",
|
|
448
|
+
logger.error("Error loading coValue in handleLoad loading state", {
|
|
449
|
+
err: e,
|
|
450
|
+
});
|
|
451
451
|
});
|
|
452
452
|
}
|
|
453
453
|
|
|
@@ -484,7 +484,7 @@ export class SyncManager {
|
|
|
484
484
|
.catch((e) => {
|
|
485
485
|
logger.error(
|
|
486
486
|
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
|
|
487
|
-
e,
|
|
487
|
+
{ err: e },
|
|
488
488
|
);
|
|
489
489
|
});
|
|
490
490
|
}
|
|
@@ -604,10 +604,11 @@ export class SyncManager {
|
|
|
604
604
|
// );
|
|
605
605
|
|
|
606
606
|
if (result.isErr()) {
|
|
607
|
-
logger.error("Failed to add transactions
|
|
607
|
+
logger.error("Failed to add transactions", {
|
|
608
608
|
peerId: peer.id,
|
|
609
609
|
peerRole: peer.role,
|
|
610
610
|
id: msg.id,
|
|
611
|
+
err: result.error,
|
|
611
612
|
});
|
|
612
613
|
peer.erroredCoValues.set(msg.id, result.error);
|
|
613
614
|
continue;
|
|
@@ -629,13 +630,11 @@ export class SyncManager {
|
|
|
629
630
|
isCorrection: true,
|
|
630
631
|
...coValue.knownState(),
|
|
631
632
|
}).catch((e) => {
|
|
632
|
-
logger.error(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
},
|
|
638
|
-
);
|
|
633
|
+
logger.error("Error sending known state correction", {
|
|
634
|
+
peerId: peer.id,
|
|
635
|
+
peerRole: peer.role,
|
|
636
|
+
err: e,
|
|
637
|
+
});
|
|
639
638
|
});
|
|
640
639
|
} else {
|
|
641
640
|
/**
|
|
@@ -649,9 +648,10 @@ export class SyncManager {
|
|
|
649
648
|
action: "known",
|
|
650
649
|
...coValue.knownState(),
|
|
651
650
|
}).catch((e: unknown) => {
|
|
652
|
-
logger.error("Error sending known state
|
|
651
|
+
logger.error("Error sending known state", {
|
|
653
652
|
peerId: peer.id,
|
|
654
653
|
peerRole: peer.role,
|
|
654
|
+
err: e,
|
|
655
655
|
});
|
|
656
656
|
});
|
|
657
657
|
}
|
package/src/tests/group.test.ts
CHANGED
|
@@ -657,6 +657,79 @@ describe("extend", () => {
|
|
|
657
657
|
});
|
|
658
658
|
});
|
|
659
659
|
|
|
660
|
+
describe("unextend", () => {
|
|
661
|
+
test("should revoke roles", async () => {
|
|
662
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
663
|
+
"server",
|
|
664
|
+
"server",
|
|
665
|
+
"server",
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
// `parentGroup` has `alice` as a writer
|
|
669
|
+
const parentGroup = node1.node.createGroup();
|
|
670
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
671
|
+
parentGroup.addMember(alice, "writer");
|
|
672
|
+
// `alice`'s role in `parentGroup` is `"writer"`
|
|
673
|
+
expect(parentGroup.roleOf(alice.id)).toBe("writer");
|
|
674
|
+
|
|
675
|
+
// `childGroup` has `bob` as a reader
|
|
676
|
+
const childGroup = node1.node.createGroup();
|
|
677
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
678
|
+
childGroup.addMember(bob, "reader");
|
|
679
|
+
// `bob`'s role in `childGroup` is `"reader"`
|
|
680
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
681
|
+
|
|
682
|
+
// `childGroup` has `parentGroup`'s members (in this case, `alice` as a writer)
|
|
683
|
+
childGroup.extend(parentGroup);
|
|
684
|
+
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
|
685
|
+
|
|
686
|
+
// `childGroup` no longer has `parentGroup`'s members
|
|
687
|
+
await childGroup.revokeExtend(parentGroup);
|
|
688
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
689
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
test("should do nothing if applied to a group that is not extended", async () => {
|
|
693
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
694
|
+
"server",
|
|
695
|
+
"server",
|
|
696
|
+
"server",
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
const parentGroup = node1.node.createGroup();
|
|
700
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
701
|
+
parentGroup.addMember(alice, "writer");
|
|
702
|
+
const childGroup = node1.node.createGroup();
|
|
703
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
704
|
+
childGroup.addMember(bob, "reader");
|
|
705
|
+
await childGroup.revokeExtend(parentGroup);
|
|
706
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
707
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
test("should not throw if the revokeExtend is called twice", async () => {
|
|
711
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
712
|
+
"server",
|
|
713
|
+
"server",
|
|
714
|
+
"server",
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const parentGroup = node1.node.createGroup();
|
|
718
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
719
|
+
parentGroup.addMember(alice, "writer");
|
|
720
|
+
const childGroup = node1.node.createGroup();
|
|
721
|
+
const bob = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
722
|
+
childGroup.addMember(bob, "reader");
|
|
723
|
+
|
|
724
|
+
childGroup.extend(parentGroup);
|
|
725
|
+
|
|
726
|
+
await childGroup.revokeExtend(parentGroup);
|
|
727
|
+
await childGroup.revokeExtend(parentGroup);
|
|
728
|
+
expect(childGroup.roleOf(bob.id)).toBe("reader");
|
|
729
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
660
733
|
describe("extend with role mapping", () => {
|
|
661
734
|
test("mapping to writer should add the ability to write", async () => {
|
|
662
735
|
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
@@ -842,3 +915,117 @@ describe("extend with role mapping", () => {
|
|
|
842
915
|
expect(mapOnNode2.get("test")).toEqual("Written from the admin");
|
|
843
916
|
});
|
|
844
917
|
});
|
|
918
|
+
test("roleOf should prioritize explicit account role over everyone role in same group", async () => {
|
|
919
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
920
|
+
|
|
921
|
+
const group = node1.node.createGroup();
|
|
922
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
923
|
+
|
|
924
|
+
// Add both everyone and specific account
|
|
925
|
+
group.addMember("everyone", "reader");
|
|
926
|
+
group.addMember(account2, "writer");
|
|
927
|
+
|
|
928
|
+
// Should return the explicit role, not everyone's role
|
|
929
|
+
expect(group.roleOf(node2.accountID)).toEqual("writer");
|
|
930
|
+
|
|
931
|
+
// Change everyone's role
|
|
932
|
+
group.addMember("everyone", "writer");
|
|
933
|
+
|
|
934
|
+
// Should still return the explicit role
|
|
935
|
+
expect(group.roleOf(node2.accountID)).toEqual("writer");
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
test("roleOf should prioritize inherited everyone role over explicit account role", async () => {
|
|
939
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
940
|
+
|
|
941
|
+
const parentGroup = node1.node.createGroup();
|
|
942
|
+
const childGroup = node1.node.createGroup();
|
|
943
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
944
|
+
|
|
945
|
+
// Set up inheritance
|
|
946
|
+
childGroup.extend(parentGroup);
|
|
947
|
+
|
|
948
|
+
// Add everyone to parent and account to child
|
|
949
|
+
parentGroup.addMember("everyone", "writer");
|
|
950
|
+
childGroup.addMember(account2, "reader");
|
|
951
|
+
|
|
952
|
+
// Should return the explicit role from child, not inherited everyone role
|
|
953
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
test("roleOf should use everyone role when no explicit role exists", async () => {
|
|
957
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
958
|
+
|
|
959
|
+
const group = node1.node.createGroup();
|
|
960
|
+
|
|
961
|
+
// Add only everyone role
|
|
962
|
+
group.addMember("everyone", "reader");
|
|
963
|
+
|
|
964
|
+
// Should return everyone's role when no explicit role exists
|
|
965
|
+
expect(group.roleOf(node2.accountID)).toEqual("reader");
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
test("roleOf should inherit everyone role from parent when no explicit roles exist", async () => {
|
|
969
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
970
|
+
|
|
971
|
+
const parentGroup = node1.node.createGroup();
|
|
972
|
+
const childGroup = node1.node.createGroup();
|
|
973
|
+
|
|
974
|
+
// Set up inheritance
|
|
975
|
+
childGroup.extend(parentGroup);
|
|
976
|
+
|
|
977
|
+
// Add everyone to parent only
|
|
978
|
+
parentGroup.addMember("everyone", "reader");
|
|
979
|
+
|
|
980
|
+
// Should inherit everyone's role from parent
|
|
981
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
test("roleOf should handle everyone role inheritance through multiple levels", async () => {
|
|
985
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
986
|
+
|
|
987
|
+
const grandParentGroup = node1.node.createGroup();
|
|
988
|
+
const parentGroup = node1.node.createGroup();
|
|
989
|
+
const childGroup = node1.node.createGroup();
|
|
990
|
+
|
|
991
|
+
const childGroupOnNode2 = await loadCoValueOrFail(node2.node, childGroup.id);
|
|
992
|
+
|
|
993
|
+
const account2 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
994
|
+
|
|
995
|
+
// Set up inheritance chain
|
|
996
|
+
parentGroup.extend(grandParentGroup);
|
|
997
|
+
childGroup.extend(parentGroup);
|
|
998
|
+
|
|
999
|
+
// Add everyone to grandparent
|
|
1000
|
+
grandParentGroup.addMember("everyone", "writer");
|
|
1001
|
+
|
|
1002
|
+
// Should inherit everyone's role from grandparent
|
|
1003
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1004
|
+
|
|
1005
|
+
// Add explicit role in parent
|
|
1006
|
+
parentGroup.addMember(account2, "reader");
|
|
1007
|
+
|
|
1008
|
+
// Should use parent's explicit role instead of grandparent's everyone role
|
|
1009
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1010
|
+
|
|
1011
|
+
// Add explicit role in child
|
|
1012
|
+
childGroup.addMember(account2, "admin");
|
|
1013
|
+
await childGroup.core.waitForSync();
|
|
1014
|
+
|
|
1015
|
+
// Should use child's explicit role
|
|
1016
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("admin");
|
|
1017
|
+
|
|
1018
|
+
// Remove child's explicit role
|
|
1019
|
+
await childGroupOnNode2.removeMember(account2);
|
|
1020
|
+
await childGroupOnNode2.core.waitForSync();
|
|
1021
|
+
|
|
1022
|
+
// Should fall back to parent's explicit role
|
|
1023
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1024
|
+
|
|
1025
|
+
// Remove parent's explicit role
|
|
1026
|
+
await parentGroup.removeMember(account2);
|
|
1027
|
+
await childGroup.core.waitForSync();
|
|
1028
|
+
|
|
1029
|
+
// Should fall back to grandparent's everyone role
|
|
1030
|
+
expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
|
|
1031
|
+
});
|
|
@@ -5,6 +5,7 @@ import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
|
5
5
|
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
6
6
|
import {
|
|
7
7
|
connectTwoPeers,
|
|
8
|
+
createThreeConnectedNodes,
|
|
8
9
|
createTwoConnectedNodes,
|
|
9
10
|
groupWithTwoAdmins,
|
|
10
11
|
groupWithTwoAdminsHighLevel,
|
|
@@ -893,6 +894,26 @@ test("Admins can set group read key, make a private transaction in an owned obje
|
|
|
893
894
|
expect(childContentAsReader.get("foo2")).toEqual("bar2");
|
|
894
895
|
});
|
|
895
896
|
|
|
897
|
+
test("only admins can add agent ids", () => {
|
|
898
|
+
const { groupCore } = newGroup();
|
|
899
|
+
|
|
900
|
+
const inviteSecret = Crypto.newRandomAgentSecret();
|
|
901
|
+
const inviteID = Crypto.getAgentID(inviteSecret);
|
|
902
|
+
|
|
903
|
+
const groupAsInvite = expectGroup(
|
|
904
|
+
groupCore
|
|
905
|
+
.testWithDifferentAccount(
|
|
906
|
+
new ControlledAgent(inviteSecret, Crypto),
|
|
907
|
+
Crypto.newRandomSessionID(inviteID),
|
|
908
|
+
)
|
|
909
|
+
.getCurrentContent(),
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
groupAsInvite.set(inviteID, "adminInvite", "trusting");
|
|
913
|
+
|
|
914
|
+
expect(groupAsInvite.get(inviteID)).toEqual(undefined);
|
|
915
|
+
});
|
|
916
|
+
|
|
896
917
|
test("Admins can set group read rey, make a private transaction in an owned object, rotate the read key, add two readers, rotate the read key again to kick out one reader, make another private transaction in the owned object, and only the remaining reader can read both transactions", () => {
|
|
897
918
|
const { node, groupCore, admin } = newGroup();
|
|
898
919
|
|
|
@@ -2802,6 +2823,270 @@ test("Calling extend to create grand-child groups parent and child references an
|
|
|
2802
2823
|
expect(childContentAsReader.get("foo")).toEqual("bar");
|
|
2803
2824
|
});
|
|
2804
2825
|
|
|
2826
|
+
test("revoking access on a child group doesn't block access to that group if a more permissive role is inheritable", async () => {
|
|
2827
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
2828
|
+
|
|
2829
|
+
const group = node1.node.createGroup();
|
|
2830
|
+
const parentGroup = node1.node.createGroup();
|
|
2831
|
+
|
|
2832
|
+
group.extend(parentGroup);
|
|
2833
|
+
|
|
2834
|
+
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
2835
|
+
|
|
2836
|
+
parentGroup.addMember(randomUser, "writer");
|
|
2837
|
+
group.addMember(randomUser, "writer");
|
|
2838
|
+
await group.removeMember(randomUser);
|
|
2839
|
+
|
|
2840
|
+
const childMap = group.createMap();
|
|
2841
|
+
|
|
2842
|
+
childMap.set("foo", "bar", "private");
|
|
2843
|
+
|
|
2844
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
|
|
2845
|
+
|
|
2846
|
+
mapOnNode2.set("foo", "baz", "private");
|
|
2847
|
+
|
|
2848
|
+
expect(mapOnNode2.get("foo")).toEqual("baz");
|
|
2849
|
+
});
|
|
2850
|
+
|
|
2851
|
+
test("revoking access on a parent group doesn't block access to the child group if the same role is inheritable from a grand-parent group", async () => {
|
|
2852
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
2853
|
+
|
|
2854
|
+
const group = node1.node.createGroup();
|
|
2855
|
+
const parentGroup = node1.node.createGroup();
|
|
2856
|
+
const grandParentGroup = node1.node.createGroup();
|
|
2857
|
+
|
|
2858
|
+
group.extend(parentGroup);
|
|
2859
|
+
parentGroup.extend(grandParentGroup);
|
|
2860
|
+
|
|
2861
|
+
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
2862
|
+
|
|
2863
|
+
grandParentGroup.addMember(randomUser, "writer");
|
|
2864
|
+
parentGroup.addMember(randomUser, "writer");
|
|
2865
|
+
await parentGroup.removeMember(randomUser);
|
|
2866
|
+
|
|
2867
|
+
const childMap = group.createMap();
|
|
2868
|
+
|
|
2869
|
+
childMap.set("foo", "bar", "private");
|
|
2870
|
+
|
|
2871
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
|
|
2872
|
+
|
|
2873
|
+
mapOnNode2.set("foo", "baz", "private");
|
|
2874
|
+
|
|
2875
|
+
expect(mapOnNode2.get("foo")).toEqual("baz");
|
|
2876
|
+
});
|
|
2877
|
+
|
|
2878
|
+
test("revoking access on a parent group doesn't block access to the child group if the same role is inheritable from another parent group", async () => {
|
|
2879
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
2880
|
+
|
|
2881
|
+
const group = node1.node.createGroup();
|
|
2882
|
+
const parentGroup1 = node1.node.createGroup();
|
|
2883
|
+
const parentGroup2 = node1.node.createGroup();
|
|
2884
|
+
|
|
2885
|
+
group.extend(parentGroup1);
|
|
2886
|
+
group.extend(parentGroup2);
|
|
2887
|
+
|
|
2888
|
+
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
2889
|
+
|
|
2890
|
+
parentGroup1.addMember(randomUser, "writer");
|
|
2891
|
+
parentGroup2.addMember(randomUser, "writer");
|
|
2892
|
+
await parentGroup1.removeMember(randomUser);
|
|
2893
|
+
|
|
2894
|
+
const childMap = group.createMap();
|
|
2895
|
+
|
|
2896
|
+
childMap.set("foo", "bar", "private");
|
|
2897
|
+
|
|
2898
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
|
|
2899
|
+
|
|
2900
|
+
mapOnNode2.set("foo", "baz", "private");
|
|
2901
|
+
|
|
2902
|
+
expect(mapOnNode2.get("foo")).toEqual("baz");
|
|
2903
|
+
});
|
|
2904
|
+
|
|
2905
|
+
test("revoking write access to parent group", async () => {
|
|
2906
|
+
// Start with a node and a group
|
|
2907
|
+
const { group, node } = newGroupHighLevel();
|
|
2908
|
+
|
|
2909
|
+
// Create a parent group and relate it to the existing group
|
|
2910
|
+
const parentGroup = node.createGroup();
|
|
2911
|
+
group.extend(parentGroup);
|
|
2912
|
+
|
|
2913
|
+
// Create an account (`alice`) that can write to the parent group
|
|
2914
|
+
// Create an account (`bob`) that can write to the child group
|
|
2915
|
+
const alice = node.createAccount();
|
|
2916
|
+
const bob = node.createAccount();
|
|
2917
|
+
parentGroup.addMember(alice, "writer");
|
|
2918
|
+
group.addMember(bob, "writer");
|
|
2919
|
+
|
|
2920
|
+
// The child group has a map that can be written to by `bob`
|
|
2921
|
+
const mapCore = node.createCoValue({
|
|
2922
|
+
type: "comap",
|
|
2923
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
2924
|
+
meta: null,
|
|
2925
|
+
...Crypto.createdNowUnique(),
|
|
2926
|
+
});
|
|
2927
|
+
const bobMap = expectMap(
|
|
2928
|
+
mapCore
|
|
2929
|
+
.testWithDifferentAccount(bob, Crypto.newRandomSessionID(bob.id))
|
|
2930
|
+
.getCurrentContent(),
|
|
2931
|
+
);
|
|
2932
|
+
|
|
2933
|
+
// `bob` sets `foo` to `bar`
|
|
2934
|
+
bobMap.set("foo", "bar", "private");
|
|
2935
|
+
// `bob`'s change is made successfully
|
|
2936
|
+
expect(bobMap.get("foo")).toEqual("bar");
|
|
2937
|
+
|
|
2938
|
+
const aliceMap = expectMap(
|
|
2939
|
+
mapCore
|
|
2940
|
+
.testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
|
|
2941
|
+
.getCurrentContent(),
|
|
2942
|
+
);
|
|
2943
|
+
// `alice` sets `foo` to `baz`
|
|
2944
|
+
aliceMap.set("foo", "baz", "private");
|
|
2945
|
+
// `alice`'s change is made successfully
|
|
2946
|
+
expect(aliceMap.get("foo")).toEqual("baz");
|
|
2947
|
+
|
|
2948
|
+
// The two groups are no longer related
|
|
2949
|
+
await group.revokeExtend(parentGroup);
|
|
2950
|
+
|
|
2951
|
+
// `bob` sets `foo` to `abc`
|
|
2952
|
+
bobMap.set("foo", "abc", "private");
|
|
2953
|
+
// `bob`'s change is made successfully
|
|
2954
|
+
expect(bobMap.get("foo")).toEqual("abc");
|
|
2955
|
+
|
|
2956
|
+
const aliceMapAfterUnextend = expectMap(
|
|
2957
|
+
mapCore
|
|
2958
|
+
.testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
|
|
2959
|
+
.getCurrentContent(),
|
|
2960
|
+
);
|
|
2961
|
+
// `alice` attempts to set `foo` to `def`, but fails
|
|
2962
|
+
expect(() => aliceMapAfterUnextend.set("foo", "def", "private")).toThrow(
|
|
2963
|
+
"Can't make transaction without read key secret",
|
|
2964
|
+
);
|
|
2965
|
+
// `alice`'s change is not made successfully
|
|
2966
|
+
expect(aliceMapAfterUnextend.get("foo")).not.toEqual("def");
|
|
2967
|
+
});
|
|
2968
|
+
|
|
2969
|
+
test("revoking read access to parent group", async () => {
|
|
2970
|
+
// Start with two nodes
|
|
2971
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
2972
|
+
const group = node1.node.createGroup();
|
|
2973
|
+
|
|
2974
|
+
// Create a parent group and relate it to the existing group
|
|
2975
|
+
const parentGroup = node1.node.createGroup();
|
|
2976
|
+
group.extend(parentGroup);
|
|
2977
|
+
|
|
2978
|
+
// Create an account (`alice`) that can read from the parent group
|
|
2979
|
+
// Create an account (`bob`) that can write to the child group
|
|
2980
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
2981
|
+
const bob = await loadCoValueOrFail(node1.node, node1.accountID);
|
|
2982
|
+
parentGroup.addMember(alice, "reader");
|
|
2983
|
+
group.addMember(bob, "writer");
|
|
2984
|
+
|
|
2985
|
+
// The child group has a map that can be written to by `bob`
|
|
2986
|
+
const bobMap = group.createMap();
|
|
2987
|
+
|
|
2988
|
+
// `bob` sets `foo` to `bar`
|
|
2989
|
+
bobMap.set("foo", "bar", "private");
|
|
2990
|
+
// `bob`'s change is made successfully
|
|
2991
|
+
expect(bobMap.get("foo")).toEqual("bar");
|
|
2992
|
+
|
|
2993
|
+
const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
|
|
2994
|
+
// `alice` reads `foo` as `bar`
|
|
2995
|
+
expect(aliceMap.get("foo")).toEqual("bar");
|
|
2996
|
+
|
|
2997
|
+
// The two groups are no longer related
|
|
2998
|
+
await group.revokeExtend(parentGroup);
|
|
2999
|
+
|
|
3000
|
+
// `bob` sets `foo` to `abc`
|
|
3001
|
+
bobMap.set("foo", "abc", "private");
|
|
3002
|
+
// `bob`'s change is made successfully
|
|
3003
|
+
expect(bobMap.get("foo")).toEqual("abc");
|
|
3004
|
+
|
|
3005
|
+
// `alice` reads `foo` as `bar`
|
|
3006
|
+
expect(aliceMap.get("foo")).toEqual("bar");
|
|
3007
|
+
});
|
|
3008
|
+
|
|
3009
|
+
test("revoking read access to grandparent group", async () => {
|
|
3010
|
+
// Start with two nodes
|
|
3011
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
3012
|
+
"server",
|
|
3013
|
+
"server",
|
|
3014
|
+
"server",
|
|
3015
|
+
);
|
|
3016
|
+
const group = node1.node.createGroup();
|
|
3017
|
+
|
|
3018
|
+
// Create group hierarchy
|
|
3019
|
+
const parentGroup = node1.node.createGroup();
|
|
3020
|
+
const grandParentGroup = node1.node.createGroup();
|
|
3021
|
+
group.extend(parentGroup);
|
|
3022
|
+
parentGroup.extend(grandParentGroup);
|
|
3023
|
+
|
|
3024
|
+
// Create an account (`alice`) that can read from the parent group
|
|
3025
|
+
// Create an account (`bob`) that can write to the child group
|
|
3026
|
+
// Create an account (`charlie`) that can read from the grandparent group
|
|
3027
|
+
const alice = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
3028
|
+
const bob = await loadCoValueOrFail(node1.node, node1.accountID);
|
|
3029
|
+
const charlie = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
3030
|
+
parentGroup.addMember(alice, "reader");
|
|
3031
|
+
group.addMember(bob, "writer");
|
|
3032
|
+
grandParentGroup.addMember(charlie, "reader");
|
|
3033
|
+
|
|
3034
|
+
// The child group has a map that can be written to by `bob`
|
|
3035
|
+
const bobMap = group.createMap();
|
|
3036
|
+
|
|
3037
|
+
// `bob` sets `foo` to `bar`
|
|
3038
|
+
bobMap.set("foo", "bar", "private");
|
|
3039
|
+
// `bob`'s change is made successfully
|
|
3040
|
+
expect(bobMap.get("foo")).toEqual("bar");
|
|
3041
|
+
|
|
3042
|
+
const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
|
|
3043
|
+
// `alice` reads `foo` as `bar`
|
|
3044
|
+
expect(aliceMap.get("foo")).toEqual("bar");
|
|
3045
|
+
|
|
3046
|
+
const charlieMap = await loadCoValueOrFail(node3.node, bobMap.id);
|
|
3047
|
+
// `charlie` reads `foo` as `bar`
|
|
3048
|
+
expect(charlieMap.get("foo")).toEqual("bar");
|
|
3049
|
+
|
|
3050
|
+
// The groups are no longer related
|
|
3051
|
+
await parentGroup.revokeExtend(grandParentGroup);
|
|
3052
|
+
await group.revokeExtend(parentGroup);
|
|
3053
|
+
|
|
3054
|
+
// `bob` sets `foo` to `abc`
|
|
3055
|
+
bobMap.set("foo", "abc", "private");
|
|
3056
|
+
// `bob`'s change is made successfully
|
|
3057
|
+
expect(bobMap.get("foo")).toEqual("abc");
|
|
3058
|
+
|
|
3059
|
+
// `alice` reads `foo` as `bar`
|
|
3060
|
+
expect(aliceMap.get("foo")).toEqual("bar");
|
|
3061
|
+
// `charlie` reads `foo` as `bar`
|
|
3062
|
+
expect(charlieMap.get("foo")).toEqual("bar");
|
|
3063
|
+
});
|
|
3064
|
+
|
|
3065
|
+
test("a user should have write access if the parent group has everyone as a writer", async () => {
|
|
3066
|
+
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
3067
|
+
|
|
3068
|
+
const group = node1.node.createGroup();
|
|
3069
|
+
const parentGroup = node1.node.createGroup();
|
|
3070
|
+
|
|
3071
|
+
group.extend(parentGroup);
|
|
3072
|
+
|
|
3073
|
+
parentGroup.addMember("everyone", "writer");
|
|
3074
|
+
|
|
3075
|
+
const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
3076
|
+
|
|
3077
|
+
group.addMember(randomUser, "reader");
|
|
3078
|
+
|
|
3079
|
+
const childMap = group.createMap();
|
|
3080
|
+
|
|
3081
|
+
childMap.set("foo", "bar", "private");
|
|
3082
|
+
|
|
3083
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
|
|
3084
|
+
|
|
3085
|
+
mapOnNode2.set("foo", "baz", "private");
|
|
3086
|
+
|
|
3087
|
+
expect(mapOnNode2.get("foo")).toEqual("baz");
|
|
3088
|
+
});
|
|
3089
|
+
|
|
2805
3090
|
test("High-level permissions work correctly when a group is extended", async () => {
|
|
2806
3091
|
const { group, node } = newGroupHighLevel();
|
|
2807
3092
|
const parentGroup = node.createGroup();
|
package/src/tests/testUtils.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
MeterProvider,
|
|
6
6
|
MetricReader,
|
|
7
7
|
} from "@opentelemetry/sdk-metrics";
|
|
8
|
-
import { expect, vi } from "vitest";
|
|
8
|
+
import { expect, onTestFinished, vi } from "vitest";
|
|
9
9
|
import { ControlledAgent } from "../coValues/account.js";
|
|
10
10
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
11
11
|
import type { CoID, RawCoValue } from "../exports.js";
|
|
@@ -200,6 +200,9 @@ export function newGroupHighLevel() {
|
|
|
200
200
|
|
|
201
201
|
const group = node.createGroup();
|
|
202
202
|
|
|
203
|
+
onTestFinished(() => {
|
|
204
|
+
node.gracefulShutdown();
|
|
205
|
+
});
|
|
203
206
|
return { admin, node, group };
|
|
204
207
|
}
|
|
205
208
|
|
package/tsconfig.json
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"skipLibCheck": true,
|
|
10
10
|
"forceConsistentCasingInFileNames": true,
|
|
11
11
|
"noUncheckedIndexedAccess": true,
|
|
12
|
-
"esModuleInterop": true
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true
|
|
13
15
|
},
|
|
14
16
|
"include": ["./src/**/*.ts"],
|
|
15
17
|
"exclude": ["./node_modules"]
|