cojson 0.18.22 → 0.18.24
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 +15 -0
- package/dist/coValues/group.d.ts +6 -7
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +51 -32
- package/dist/coValues/group.js.map +1 -1
- package/dist/knownState.d.ts +1 -1
- package/dist/knownState.d.ts.map +1 -1
- package/dist/permissions.d.ts +1 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/tests/group.inheritance.test.js +111 -3
- package/dist/tests/group.inheritance.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +6 -8
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +22 -55
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +3 -3
- package/src/coValues/group.ts +115 -49
- package/src/knownState.ts +2 -2
- package/src/permissions.ts +9 -0
- package/src/tests/group.inheritance.test.ts +212 -8
- package/src/tests/testUtils.ts +33 -77
package/src/permissions.ts
CHANGED
|
@@ -57,6 +57,15 @@ export type Role =
|
|
|
57
57
|
| "readerInvite"
|
|
58
58
|
| "writeOnlyInvite";
|
|
59
59
|
|
|
60
|
+
export function isAccountRole(role?: Role): role is AccountRole {
|
|
61
|
+
return (
|
|
62
|
+
role === "admin" ||
|
|
63
|
+
role === "writer" ||
|
|
64
|
+
role === "reader" ||
|
|
65
|
+
role === "writeOnly"
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
type ValidTransactionsResult = { txID: TransactionID; tx: Transaction };
|
|
61
70
|
type MemberState = { [agent: RawAccountID | AgentID]: Role; [EVERYONE]?: Role };
|
|
62
71
|
|
|
@@ -3,11 +3,13 @@ import type { CoID, RawGroup } from "../exports";
|
|
|
3
3
|
import { NewContentMessage } from "../sync";
|
|
4
4
|
import {
|
|
5
5
|
SyncMessagesLog,
|
|
6
|
+
createNConnectedNodes,
|
|
6
7
|
createThreeConnectedNodes,
|
|
7
8
|
createTwoConnectedNodes,
|
|
8
9
|
loadCoValueOrFail,
|
|
9
10
|
setupTestNode,
|
|
10
11
|
} from "./testUtils";
|
|
12
|
+
import { expectMap } from "../coValue.js";
|
|
11
13
|
|
|
12
14
|
let jazzCloud: ReturnType<typeof setupTestNode>;
|
|
13
15
|
|
|
@@ -21,17 +23,12 @@ describe("extend", () => {
|
|
|
21
23
|
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
22
24
|
|
|
23
25
|
const group = node1.node.createGroup();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"writer",
|
|
27
|
-
);
|
|
26
|
+
const node2OnNode1 = await loadCoValueOrFail(node1.node, node2.accountID);
|
|
27
|
+
group.addMember(node2OnNode1, "writer");
|
|
28
28
|
|
|
29
29
|
const childGroup = node1.node.createGroup();
|
|
30
30
|
childGroup.extend(group);
|
|
31
|
-
childGroup.addMember(
|
|
32
|
-
await loadCoValueOrFail(node1.node, node2.accountID),
|
|
33
|
-
"writeOnly",
|
|
34
|
-
);
|
|
31
|
+
childGroup.addMember(node2OnNode1, "writeOnly");
|
|
35
32
|
|
|
36
33
|
const map = childGroup.createMap();
|
|
37
34
|
map.set("test", "Written from the admin");
|
|
@@ -124,6 +121,115 @@ describe("extend", () => {
|
|
|
124
121
|
expect(mapOnNode2.get("hello")).toEqual("from node 2");
|
|
125
122
|
});
|
|
126
123
|
|
|
124
|
+
test("existing parent groups have access to new writeOnly keys in the child group", async () => {
|
|
125
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
126
|
+
"server",
|
|
127
|
+
"server",
|
|
128
|
+
"server",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const parentGroup = node1.node.createGroup();
|
|
132
|
+
const account2OnNode1 = await loadCoValueOrFail(
|
|
133
|
+
node1.node,
|
|
134
|
+
node2.accountID,
|
|
135
|
+
);
|
|
136
|
+
parentGroup.addMember(account2OnNode1, "admin");
|
|
137
|
+
|
|
138
|
+
const childGroup = node1.node.createGroup();
|
|
139
|
+
const account3OnNode1 = await loadCoValueOrFail(
|
|
140
|
+
node1.node,
|
|
141
|
+
node3.accountID,
|
|
142
|
+
);
|
|
143
|
+
childGroup.extend(parentGroup);
|
|
144
|
+
// The existing parent group should have access to content written by
|
|
145
|
+
// an account with writeOnly permission
|
|
146
|
+
childGroup.addMember(account3OnNode1, "writeOnly");
|
|
147
|
+
|
|
148
|
+
const map = childGroup.createMap();
|
|
149
|
+
|
|
150
|
+
const mapOnNode3 = await loadCoValueOrFail(node3.node, map.id);
|
|
151
|
+
mapOnNode3.set("test", "Written by writeOnly member");
|
|
152
|
+
|
|
153
|
+
expect(mapOnNode3.get("test")).toEqual("Written by writeOnly member");
|
|
154
|
+
await mapOnNode3.core.waitForSync();
|
|
155
|
+
|
|
156
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
|
|
157
|
+
expect(mapOnNode2.get("test")).toEqual("Written by writeOnly member");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("new parent groups have access to existing writeOnly keys in the child group", async () => {
|
|
161
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
162
|
+
"server",
|
|
163
|
+
"server",
|
|
164
|
+
"server",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const parentGroup = node1.node.createGroup();
|
|
168
|
+
const account2OnNode1 = await loadCoValueOrFail(
|
|
169
|
+
node1.node,
|
|
170
|
+
node2.accountID,
|
|
171
|
+
);
|
|
172
|
+
parentGroup.addMember(account2OnNode1, "admin");
|
|
173
|
+
|
|
174
|
+
const childGroup = node1.node.createGroup();
|
|
175
|
+
const account3OnNode1 = await loadCoValueOrFail(
|
|
176
|
+
node1.node,
|
|
177
|
+
node3.accountID,
|
|
178
|
+
);
|
|
179
|
+
childGroup.addMember(account3OnNode1, "writeOnly");
|
|
180
|
+
// The new parent group should have access to content written by
|
|
181
|
+
// an account with writeOnly permission
|
|
182
|
+
childGroup.extend(parentGroup);
|
|
183
|
+
|
|
184
|
+
const map = childGroup.createMap();
|
|
185
|
+
|
|
186
|
+
const mapOnNode3 = await loadCoValueOrFail(node3.node, map.id);
|
|
187
|
+
mapOnNode3.set("test", "Written by writeOnly member");
|
|
188
|
+
|
|
189
|
+
expect(mapOnNode3.get("test")).toEqual("Written by writeOnly member");
|
|
190
|
+
await mapOnNode3.core.waitForSync();
|
|
191
|
+
|
|
192
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
|
|
193
|
+
expect(mapOnNode2.get("test")).toEqual("Written by writeOnly member");
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("writeOnly keys are rotated for parent groups", async () => {
|
|
197
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
198
|
+
"server",
|
|
199
|
+
"server",
|
|
200
|
+
"server",
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const parentGroup = node1.node.createGroup();
|
|
204
|
+
const account2OnNode1 = await loadCoValueOrFail(
|
|
205
|
+
node1.node,
|
|
206
|
+
node2.accountID,
|
|
207
|
+
);
|
|
208
|
+
parentGroup.addMember(account2OnNode1, "admin");
|
|
209
|
+
|
|
210
|
+
const childGroup = node1.node.createGroup();
|
|
211
|
+
const account3OnNode1 = await loadCoValueOrFail(
|
|
212
|
+
node1.node,
|
|
213
|
+
node3.accountID,
|
|
214
|
+
);
|
|
215
|
+
childGroup.addMember(account3OnNode1, "writeOnly");
|
|
216
|
+
childGroup.extend(parentGroup);
|
|
217
|
+
|
|
218
|
+
childGroup.rotateReadKey();
|
|
219
|
+
|
|
220
|
+
const childGroupOnNode3 = await loadCoValueOrFail(
|
|
221
|
+
node3.node,
|
|
222
|
+
childGroup.id,
|
|
223
|
+
);
|
|
224
|
+
const map = childGroupOnNode3.createMap();
|
|
225
|
+
map.set("test", "Written by writeOnly member");
|
|
226
|
+
|
|
227
|
+
await map.core.waitForSync();
|
|
228
|
+
|
|
229
|
+
const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
|
|
230
|
+
expect(mapOnNode2.get("test")).toEqual("Written by writeOnly member");
|
|
231
|
+
});
|
|
232
|
+
|
|
127
233
|
test("a user should be able to extend a group when his role on the parent group is writeOnly", async () => {
|
|
128
234
|
const { node1, node2 } = await createTwoConnectedNodes("server", "server");
|
|
129
235
|
|
|
@@ -344,6 +450,104 @@ describe("extend", () => {
|
|
|
344
450
|
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
|
345
451
|
});
|
|
346
452
|
|
|
453
|
+
test("group inheritance should work for groups extended without having membership in the parent group", async () => {
|
|
454
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
455
|
+
"server",
|
|
456
|
+
"server",
|
|
457
|
+
"server",
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
const parentGroup = node1.node.createGroup();
|
|
461
|
+
const account2OnNode1 = await loadCoValueOrFail(
|
|
462
|
+
node1.node,
|
|
463
|
+
node2.accountID,
|
|
464
|
+
);
|
|
465
|
+
parentGroup.addMember(account2OnNode1, "admin");
|
|
466
|
+
|
|
467
|
+
const childGroup = node1.node.createGroup();
|
|
468
|
+
childGroup.extend(parentGroup, "admin");
|
|
469
|
+
|
|
470
|
+
const sharedGroup = node3.node.createGroup();
|
|
471
|
+
|
|
472
|
+
// Account3 does not have permissions over the childGroup being extended
|
|
473
|
+
const childGroupOnNode3 = await loadCoValueOrFail(
|
|
474
|
+
node3.node,
|
|
475
|
+
childGroup.id,
|
|
476
|
+
);
|
|
477
|
+
sharedGroup.extend(childGroupOnNode3, "admin");
|
|
478
|
+
|
|
479
|
+
expect(sharedGroup.roleOf(node1.accountID)).toEqual("admin");
|
|
480
|
+
expect(sharedGroup.roleOf(node2.accountID)).toEqual("admin");
|
|
481
|
+
expect(sharedGroup.roleOf(node3.accountID)).toEqual("admin");
|
|
482
|
+
|
|
483
|
+
// Create a map owned by sharedGroup
|
|
484
|
+
const testMap = sharedGroup.createMap();
|
|
485
|
+
testMap.set("name", "Test");
|
|
486
|
+
|
|
487
|
+
// node1 should be able to access the map because it is admin of the childGroup
|
|
488
|
+
const testMapOnNode1 = expectMap(
|
|
489
|
+
await loadCoValueOrFail(node1.node, testMap.id),
|
|
490
|
+
);
|
|
491
|
+
expect(testMapOnNode1.get("name")).toEqual("Test");
|
|
492
|
+
|
|
493
|
+
// node2 should also be able to access the map because it is admin of parentGroup
|
|
494
|
+
const testMapOnNode2 = expectMap(
|
|
495
|
+
await loadCoValueOrFail(node2.node, testMap.id),
|
|
496
|
+
);
|
|
497
|
+
expect(testMapOnNode2.get("name")).toEqual("Test");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("adding new group members should work for groups extended without having membership in the parent group", async () => {
|
|
501
|
+
const nodes = await createNConnectedNodes(
|
|
502
|
+
"server",
|
|
503
|
+
"server",
|
|
504
|
+
"server",
|
|
505
|
+
"server",
|
|
506
|
+
);
|
|
507
|
+
const node1 = nodes[0]!;
|
|
508
|
+
const node2 = nodes[1]!;
|
|
509
|
+
const node3 = nodes[2]!;
|
|
510
|
+
const node4 = nodes[3]!;
|
|
511
|
+
|
|
512
|
+
const parentGroup = node1.node.createGroup();
|
|
513
|
+
const account2OnNode1 = await loadCoValueOrFail(
|
|
514
|
+
node1.node,
|
|
515
|
+
node2.accountID,
|
|
516
|
+
);
|
|
517
|
+
parentGroup.addMember(account2OnNode1, "admin");
|
|
518
|
+
|
|
519
|
+
const childGroup = node1.node.createGroup();
|
|
520
|
+
childGroup.extend(parentGroup, "admin");
|
|
521
|
+
|
|
522
|
+
const sharedGroup = node3.node.createGroup();
|
|
523
|
+
|
|
524
|
+
// Account3 does not have permissions over the childGroup being extended
|
|
525
|
+
const childGroupOnNode3 = await loadCoValueOrFail(
|
|
526
|
+
node3.node,
|
|
527
|
+
childGroup.id,
|
|
528
|
+
);
|
|
529
|
+
sharedGroup.extend(childGroupOnNode3, "admin");
|
|
530
|
+
|
|
531
|
+
// Add a new parent group to the previously extended child group
|
|
532
|
+
const newParentGroup = node1.node.createGroup();
|
|
533
|
+
const account4OnNode1 = await loadCoValueOrFail(
|
|
534
|
+
node1.node,
|
|
535
|
+
node4.accountID,
|
|
536
|
+
);
|
|
537
|
+
newParentGroup.addMember(account4OnNode1, "admin");
|
|
538
|
+
childGroup.extend(newParentGroup);
|
|
539
|
+
|
|
540
|
+
// Create a map owned by sharedGroup
|
|
541
|
+
const testMap = sharedGroup.createMap();
|
|
542
|
+
testMap.set("name", "Test");
|
|
543
|
+
|
|
544
|
+
// Account4 should be able to access the map because it is admin of newParentGroup
|
|
545
|
+
const testMapOnNode4 = expectMap(
|
|
546
|
+
await loadCoValueOrFail(node4.node, testMap.id),
|
|
547
|
+
);
|
|
548
|
+
expect(testMapOnNode4.get("name")).toEqual("Test");
|
|
549
|
+
});
|
|
550
|
+
|
|
347
551
|
test("should be possible to extend a group after getting revoked from the parent group", async () => {
|
|
348
552
|
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
349
553
|
"server",
|
package/src/tests/testUtils.ts
CHANGED
|
@@ -70,33 +70,11 @@ export async function createTwoConnectedNodes(
|
|
|
70
70
|
node1Role: Peer["role"],
|
|
71
71
|
node2Role: Peer["role"],
|
|
72
72
|
) {
|
|
73
|
-
|
|
74
|
-
const [node1ToNode2Peer, node2ToNode1Peer] = connectedPeers(
|
|
75
|
-
"node1ToNode2",
|
|
76
|
-
"node2ToNode1",
|
|
77
|
-
{
|
|
78
|
-
peer1role: node2Role,
|
|
79
|
-
peer2role: node1Role,
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const node1 = await LocalNode.withNewlyCreatedAccount({
|
|
84
|
-
peersToLoadFrom: [node1ToNode2Peer],
|
|
85
|
-
crypto: Crypto,
|
|
86
|
-
creationProps: { name: "Client" },
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const node2 = await LocalNode.withNewlyCreatedAccount({
|
|
90
|
-
peersToLoadFrom: [node2ToNode1Peer],
|
|
91
|
-
crypto: Crypto,
|
|
92
|
-
creationProps: { name: "Server" },
|
|
93
|
-
});
|
|
73
|
+
const [node1, node2] = await createNConnectedNodes(node1Role, node2Role);
|
|
94
74
|
|
|
95
75
|
return {
|
|
96
|
-
node1
|
|
97
|
-
node2
|
|
98
|
-
node1ToNode2Peer,
|
|
99
|
-
node2ToNode1Peer,
|
|
76
|
+
node1: node1!,
|
|
77
|
+
node2: node2!,
|
|
100
78
|
};
|
|
101
79
|
}
|
|
102
80
|
|
|
@@ -105,64 +83,42 @@ export async function createThreeConnectedNodes(
|
|
|
105
83
|
node2Role: Peer["role"],
|
|
106
84
|
node3Role: Peer["role"],
|
|
107
85
|
) {
|
|
108
|
-
const [
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
peer1role: node2Role,
|
|
113
|
-
peer2role: node1Role,
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
const [node1ToNode3Peer, node3ToNode1Peer] = connectedPeers(
|
|
118
|
-
"node1ToNode3",
|
|
119
|
-
"node3ToNode1",
|
|
120
|
-
{
|
|
121
|
-
peer1role: node3Role,
|
|
122
|
-
peer2role: node1Role,
|
|
123
|
-
},
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
const [node2ToNode3Peer, node3ToNode2Peer] = connectedPeers(
|
|
127
|
-
"node2ToNode3",
|
|
128
|
-
"node3ToNode2",
|
|
129
|
-
{
|
|
130
|
-
peer1role: node3Role,
|
|
131
|
-
peer2role: node2Role,
|
|
132
|
-
},
|
|
86
|
+
const [node1, node2, node3] = await createNConnectedNodes(
|
|
87
|
+
node1Role,
|
|
88
|
+
node2Role,
|
|
89
|
+
node3Role,
|
|
133
90
|
);
|
|
134
91
|
|
|
135
|
-
const node1 = await LocalNode.withNewlyCreatedAccount({
|
|
136
|
-
peersToLoadFrom: [node1ToNode2Peer, node1ToNode3Peer],
|
|
137
|
-
crypto: Crypto,
|
|
138
|
-
creationProps: { name: "Node 1" },
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const node2 = await LocalNode.withNewlyCreatedAccount({
|
|
142
|
-
peersToLoadFrom: [node2ToNode1Peer, node2ToNode3Peer],
|
|
143
|
-
crypto: Crypto,
|
|
144
|
-
creationProps: { name: "Node 2" },
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const node3 = await LocalNode.withNewlyCreatedAccount({
|
|
148
|
-
peersToLoadFrom: [node3ToNode1Peer, node3ToNode2Peer],
|
|
149
|
-
crypto: Crypto,
|
|
150
|
-
creationProps: { name: "Node 3" },
|
|
151
|
-
});
|
|
152
|
-
|
|
153
92
|
return {
|
|
154
|
-
node1
|
|
155
|
-
node2
|
|
156
|
-
node3
|
|
157
|
-
node1ToNode2Peer,
|
|
158
|
-
node2ToNode1Peer,
|
|
159
|
-
node1ToNode3Peer,
|
|
160
|
-
node3ToNode1Peer,
|
|
161
|
-
node2ToNode3Peer,
|
|
162
|
-
node3ToNode2Peer,
|
|
93
|
+
node1: node1!,
|
|
94
|
+
node2: node2!,
|
|
95
|
+
node3: node3!,
|
|
163
96
|
};
|
|
164
97
|
}
|
|
165
98
|
|
|
99
|
+
export async function createNConnectedNodes(...nodeRoles: Peer["role"][]) {
|
|
100
|
+
const nodes = await Promise.all(
|
|
101
|
+
Array.from({ length: nodeRoles.length }, async (_, i) => {
|
|
102
|
+
return LocalNode.withNewlyCreatedAccount({
|
|
103
|
+
peersToLoadFrom: [],
|
|
104
|
+
crypto: Crypto,
|
|
105
|
+
creationProps: { name: `Node ${i + 1}` },
|
|
106
|
+
});
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
110
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
111
|
+
connectTwoPeers(
|
|
112
|
+
nodes[i]!.node,
|
|
113
|
+
nodes[j]!.node,
|
|
114
|
+
nodeRoles[i]!,
|
|
115
|
+
nodeRoles[j]!,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return nodes;
|
|
120
|
+
}
|
|
121
|
+
|
|
166
122
|
export function connectTwoPeers(
|
|
167
123
|
a: LocalNode,
|
|
168
124
|
b: LocalNode,
|