cojson 0.9.19 → 0.10.0

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.
@@ -467,33 +467,6 @@ describe("writeOnly", () => {
467
467
  expect(mapOnNode3.get("test")).toEqual("Written from a writeOnly member");
468
468
  });
469
469
 
470
- test("inherited writer roles should work correctly", async () => {
471
- const { node1, node2 } = await createTwoConnectedNodes("server", "server");
472
-
473
- const group = node1.node.createGroup();
474
- group.addMember(
475
- await loadCoValueOrFail(node1.node, node2.accountID),
476
- "writer",
477
- );
478
-
479
- const childGroup = node1.node.createGroup();
480
- childGroup.extend(group);
481
- childGroup.addMember(
482
- await loadCoValueOrFail(node1.node, node2.accountID),
483
- "writeOnly",
484
- );
485
-
486
- const map = childGroup.createMap();
487
- map.set("test", "Written from the admin");
488
-
489
- await map.core.waitForSync();
490
-
491
- const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
492
-
493
- // The writer role should be able to see the edits from the admin
494
- expect(mapOnNode2.get("test")).toEqual("Written from the admin");
495
- });
496
-
497
470
  test("upgrade to writer roles should work correctly", async () => {
498
471
  const { node1, node2 } = await createTwoConnectedNodes("server", "server");
499
472
 
@@ -528,6 +501,35 @@ describe("writeOnly", () => {
528
501
  // The writer role should be able to see the edits from the admin
529
502
  expect(mapOnNode2.get("test")).toEqual("Written from the writeOnly member");
530
503
  });
504
+ });
505
+
506
+ describe("extend", () => {
507
+ test("inherited writer roles should work correctly", async () => {
508
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
509
+
510
+ const group = node1.node.createGroup();
511
+ group.addMember(
512
+ await loadCoValueOrFail(node1.node, node2.accountID),
513
+ "writer",
514
+ );
515
+
516
+ const childGroup = node1.node.createGroup();
517
+ childGroup.extend(group);
518
+ childGroup.addMember(
519
+ await loadCoValueOrFail(node1.node, node2.accountID),
520
+ "writeOnly",
521
+ );
522
+
523
+ const map = childGroup.createMap();
524
+ map.set("test", "Written from the admin");
525
+
526
+ await map.core.waitForSync();
527
+
528
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
529
+
530
+ // The writer role should be able to see the edits from the admin
531
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
532
+ });
531
533
 
532
534
  test("a user should be able to extend a group when his role on the parent group is writer", async () => {
533
535
  const { node1, node2 } = await createTwoConnectedNodes("server", "server");
@@ -638,4 +640,205 @@ describe("writeOnly", () => {
638
640
 
639
641
  expect(map.get("test")).toEqual("Hello!");
640
642
  });
643
+
644
+ test("a writerInvite role should not be inherited", async () => {
645
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
646
+
647
+ const group = node1.node.createGroup();
648
+ group.addMember(
649
+ await loadCoValueOrFail(node1.node, node2.accountID),
650
+ "writerInvite",
651
+ );
652
+
653
+ const childGroup = node1.node.createGroup();
654
+ childGroup.extend(group);
655
+
656
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
657
+ });
658
+ });
659
+
660
+ describe("extend with role mapping", () => {
661
+ test("mapping to writer should add the ability to write", async () => {
662
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
663
+
664
+ const group = node1.node.createGroup();
665
+ group.addMember(
666
+ await loadCoValueOrFail(node1.node, node2.accountID),
667
+ "reader",
668
+ );
669
+
670
+ const childGroup = node1.node.createGroup();
671
+ childGroup.extend(group, "writer");
672
+
673
+ expect(childGroup.roleOf(node2.accountID)).toEqual("writer");
674
+
675
+ const map = childGroup.createMap();
676
+ map.set("test", "Written from the admin");
677
+
678
+ await map.core.waitForSync();
679
+
680
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
681
+
682
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
683
+
684
+ mapOnNode2.set("test", "Written from the inherited role");
685
+ expect(mapOnNode2.get("test")).toEqual("Written from the inherited role");
686
+
687
+ await mapOnNode2.core.waitForSync();
688
+
689
+ expect(map.get("test")).toEqual("Written from the inherited role");
690
+ });
691
+
692
+ test("mapping to reader should remove the ability to write", async () => {
693
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
694
+
695
+ const group = node1.node.createGroup();
696
+ group.addMember(
697
+ await loadCoValueOrFail(node1.node, node2.accountID),
698
+ "writer",
699
+ );
700
+
701
+ const childGroup = node1.node.createGroup();
702
+ childGroup.extend(group, "reader");
703
+
704
+ expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
705
+
706
+ const map = childGroup.createMap();
707
+ map.set("test", "Written from the admin");
708
+
709
+ await map.core.waitForSync();
710
+
711
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
712
+
713
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
714
+
715
+ mapOnNode2.set("test", "Should not be visible");
716
+
717
+ await mapOnNode2.core.waitForSync();
718
+
719
+ expect(map.get("test")).toEqual("Written from the admin");
720
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
721
+ });
722
+
723
+ test("mapping to admin should add the ability to add members", async () => {
724
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
725
+ "server",
726
+ "server",
727
+ "server",
728
+ );
729
+
730
+ const group = node1.node.createGroup();
731
+ group.addMember(
732
+ await loadCoValueOrFail(node1.node, node2.accountID),
733
+ "reader",
734
+ );
735
+
736
+ const childGroup = node1.node.createGroup();
737
+ childGroup.extend(group, "admin");
738
+
739
+ expect(childGroup.roleOf(node2.accountID)).toEqual("admin");
740
+
741
+ await childGroup.core.waitForSync();
742
+
743
+ const childGroupOnNode2 = await loadCoValueOrFail(
744
+ node2.node,
745
+ childGroup.id,
746
+ );
747
+
748
+ childGroupOnNode2.addMember(
749
+ await loadCoValueOrFail(node2.node, node3.accountID),
750
+ "reader",
751
+ );
752
+
753
+ expect(childGroupOnNode2.roleOf(node3.accountID)).toEqual("reader");
754
+ });
755
+
756
+ test("mapping to reader should remove the ability to add members", async () => {
757
+ const { node1, node2, node3 } = await createThreeConnectedNodes(
758
+ "server",
759
+ "server",
760
+ "server",
761
+ );
762
+
763
+ const group = node1.node.createGroup();
764
+ group.addMember(
765
+ await loadCoValueOrFail(node1.node, node2.accountID),
766
+ "admin",
767
+ );
768
+
769
+ const childGroup = node1.node.createGroup();
770
+ childGroup.extend(group, "reader");
771
+
772
+ expect(childGroup.roleOf(node2.accountID)).toEqual("reader");
773
+
774
+ await childGroup.core.waitForSync();
775
+
776
+ const childGroupOnNode2 = await loadCoValueOrFail(
777
+ node2.node,
778
+ childGroup.id,
779
+ );
780
+
781
+ const accountToAdd = await loadCoValueOrFail(node2.node, node3.accountID);
782
+
783
+ expect(() => {
784
+ childGroupOnNode2.addMember(accountToAdd, "reader");
785
+ }).toThrow();
786
+
787
+ expect(childGroupOnNode2.roleOf(node3.accountID)).toEqual(undefined);
788
+ });
789
+
790
+ test("non-inheritable roles should not give access to the child group when role mapping is used", async () => {
791
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
792
+
793
+ const group = node1.node.createGroup();
794
+ group.addMember(
795
+ await loadCoValueOrFail(node1.node, node2.accountID),
796
+ "writeOnly",
797
+ );
798
+
799
+ const childGroup = node1.node.createGroup();
800
+ childGroup.extend(group, "reader");
801
+
802
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
803
+
804
+ const map = childGroup.createMap();
805
+ map.set("test", "Written from the admin");
806
+
807
+ await map.core.waitForSync();
808
+
809
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
810
+
811
+ expect(mapOnNode2.get("test")).toEqual(undefined);
812
+ });
813
+
814
+ test("invite roles should not give write access to the child group when role mapping is used", async () => {
815
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
816
+
817
+ const group = node1.node.createGroup();
818
+ group.addMember(
819
+ await loadCoValueOrFail(node1.node, node2.accountID),
820
+ "writerInvite",
821
+ );
822
+
823
+ const childGroup = node1.node.createGroup();
824
+ childGroup.extend(group, "writer");
825
+
826
+ expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
827
+
828
+ const map = childGroup.createMap();
829
+ map.set("test", "Written from the admin");
830
+
831
+ await map.core.waitForSync();
832
+
833
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
834
+
835
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin"); // The invite roles have access to the readKey hence can read the values on inherited groups
836
+
837
+ mapOnNode2.set("test", "Should not be visible");
838
+
839
+ await mapOnNode2.core.waitForSync();
840
+
841
+ expect(map.get("test")).toEqual("Written from the admin");
842
+ expect(mapOnNode2.get("test")).toEqual("Written from the admin");
843
+ });
641
844
  });
@@ -1,4 +1,4 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, vi } from "vitest";
2
2
  import { expectMap } from "../coValue.js";
3
3
  import { ControlledAgent } from "../coValues/account.js";
4
4
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
@@ -2908,3 +2908,61 @@ test("extend cycles should not break the keys rotation", () => {
2908
2908
 
2909
2909
  expect(map.get("test")).toEqual("Hello!");
2910
2910
  });
2911
+
2912
+ test("Admin can remove themselves from a group", async () => {
2913
+ const warnSpy = vi.spyOn(console, "warn");
2914
+ const { group, admin } = newGroupHighLevel();
2915
+
2916
+ // Admin removes themselves
2917
+ await group.removeMember(admin);
2918
+
2919
+ expect(group.myRole()).toBeUndefined();
2920
+ expect(warnSpy).not.toHaveBeenCalled();
2921
+ });
2922
+
2923
+ test("Can revoke read permission from 'everyone'", async () => {
2924
+ const { group } = newGroupHighLevel();
2925
+ const childObject = group.createMap();
2926
+
2927
+ // Give everyone read access
2928
+ group.addMember("everyone", "reader");
2929
+
2930
+ childObject.set("foo", "bar", "private");
2931
+ expect(childObject.get("foo")).toEqual("bar");
2932
+
2933
+ // Create a new account to verify access
2934
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
2935
+ const childContent = expectMap(
2936
+ childObject.core
2937
+ .testWithDifferentAccount(
2938
+ newAccount,
2939
+ Crypto.newRandomSessionID(newAccount.currentAgentID()._unsafeUnwrap()),
2940
+ )
2941
+ .getCurrentContent(),
2942
+ );
2943
+
2944
+ // Verify the new account can read
2945
+ expect(childContent.get("foo")).toEqual("bar");
2946
+
2947
+ // Revoke everyone's access
2948
+ await group.removeMember("everyone");
2949
+
2950
+ childObject.set("foo", "updated after revoke", "private");
2951
+
2952
+ // Create another new account to verify access is revoked
2953
+ const newAccount2 = new ControlledAgent(
2954
+ Crypto.newRandomAgentSecret(),
2955
+ Crypto,
2956
+ );
2957
+ const childContent2 = expectMap(
2958
+ childObject.core
2959
+ .testWithDifferentAccount(
2960
+ newAccount2,
2961
+ Crypto.newRandomSessionID(newAccount2.currentAgentID()._unsafeUnwrap()),
2962
+ )
2963
+ .getCurrentContent(),
2964
+ );
2965
+
2966
+ // Verify the new account cannot read after revocation
2967
+ expect(childContent2.get("foo")).toEqual("bar");
2968
+ });