cojson 0.14.0 → 0.14.16
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 +18 -0
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +14 -5
- package/dist/coValues/group.js.map +1 -1
- package/dist/exports.d.ts +2 -1
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +2 -1
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +1 -0
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +0 -7
- package/dist/permissions.js.map +1 -1
- package/dist/tests/group.inheritance.test.js +45 -0
- package/dist/tests/group.inheritance.test.js.map +1 -1
- package/dist/tests/group.invite.test.js +3 -0
- package/dist/tests/group.invite.test.js.map +1 -1
- package/dist/tests/permissions.test.js +0 -19
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/sync.invite.test.js +12 -4
- package/dist/tests/sync.invite.test.js.map +1 -1
- package/package.json +1 -1
- package/src/coValues/group.ts +22 -8
- package/src/exports.ts +2 -0
- package/src/localNode.ts +1 -0
- package/src/permissions.ts +0 -12
- package/src/tests/group.inheritance.test.ts +95 -0
- package/src/tests/group.invite.test.ts +4 -0
- package/src/tests/permissions.test.ts +0 -34
- package/src/tests/sync.invite.test.ts +12 -4
package/src/coValues/group.ts
CHANGED
|
@@ -451,7 +451,9 @@ export class RawGroup<
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
getCurrentReadKeyId() {
|
|
454
|
-
|
|
454
|
+
const myRole = this.myRole();
|
|
455
|
+
|
|
456
|
+
if (myRole === "writeOnly") {
|
|
455
457
|
const accountId = this.core.node.getCurrentAgent().id;
|
|
456
458
|
|
|
457
459
|
const key = this.get(`writeKeyFor_${accountId}`) as KeyID;
|
|
@@ -469,6 +471,16 @@ export class RawGroup<
|
|
|
469
471
|
return key;
|
|
470
472
|
}
|
|
471
473
|
|
|
474
|
+
if (!myRole) {
|
|
475
|
+
const accountId = this.core.node.getCurrentAgent().id;
|
|
476
|
+
|
|
477
|
+
const key = this.get(`writeKeyFor_${accountId}`) as KeyID;
|
|
478
|
+
|
|
479
|
+
if (key) {
|
|
480
|
+
return key;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
472
484
|
return this.get("readKey");
|
|
473
485
|
}
|
|
474
486
|
|
|
@@ -670,22 +682,24 @@ export class RawGroup<
|
|
|
670
682
|
);
|
|
671
683
|
}
|
|
672
684
|
|
|
685
|
+
const value = role === "inherit" ? "extend" : role;
|
|
686
|
+
|
|
687
|
+
this.set(`parent_${parent.id}`, value, "trusting");
|
|
688
|
+
parent.set(`child_${this.id}`, "extend", "trusting");
|
|
689
|
+
|
|
673
690
|
if (
|
|
674
691
|
parent.myRole() !== "admin" &&
|
|
675
692
|
parent.myRole() !== "writer" &&
|
|
676
693
|
parent.myRole() !== "reader" &&
|
|
677
694
|
parent.myRole() !== "writeOnly"
|
|
678
695
|
) {
|
|
679
|
-
|
|
680
|
-
|
|
696
|
+
// Create a writeOnly key in the parent group to be able to reveal the current child key to the parent group
|
|
697
|
+
parent.internalCreateWriteOnlyKeyForMember(
|
|
698
|
+
this.core.node.getCurrentAgent().id,
|
|
699
|
+
this.core.node.getCurrentAgent().currentAgentID(),
|
|
681
700
|
);
|
|
682
701
|
}
|
|
683
702
|
|
|
684
|
-
const value = role === "inherit" ? "extend" : role;
|
|
685
|
-
|
|
686
|
-
this.set(`parent_${parent.id}`, value, "trusting");
|
|
687
|
-
parent.set(`child_${this.id}`, "extend", "trusting");
|
|
688
|
-
|
|
689
703
|
const { id: parentReadKeyID, secret: parentReadKeySecret } =
|
|
690
704
|
parent.core.getCurrentReadKey();
|
|
691
705
|
if (!parentReadKeySecret) {
|
package/src/exports.ts
CHANGED
|
@@ -72,6 +72,7 @@ import type {
|
|
|
72
72
|
import {
|
|
73
73
|
DisconnectedError,
|
|
74
74
|
PingTimeoutError,
|
|
75
|
+
SyncManager,
|
|
75
76
|
emptyKnownState,
|
|
76
77
|
} from "./sync.js";
|
|
77
78
|
|
|
@@ -102,6 +103,7 @@ export const cojsonInternals = {
|
|
|
102
103
|
getGroupDependentKeyList,
|
|
103
104
|
getGroupDependentKey,
|
|
104
105
|
disablePermissionErrors,
|
|
106
|
+
SyncManager,
|
|
105
107
|
CO_VALUE_LOADING_CONFIG,
|
|
106
108
|
setCoValueLoadingRetryDelay(delay: number) {
|
|
107
109
|
CO_VALUE_LOADING_CONFIG.RETRY_DELAY = delay;
|
package/src/localNode.ts
CHANGED
package/src/permissions.ts
CHANGED
|
@@ -345,18 +345,6 @@ function determineValidTransactionsForGroup(
|
|
|
345
345
|
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
|
346
346
|
continue;
|
|
347
347
|
} else if (isChildExtension(change.key)) {
|
|
348
|
-
if (
|
|
349
|
-
memberState[transactor] !== "admin" &&
|
|
350
|
-
memberState[transactor] !== "writer" &&
|
|
351
|
-
memberState[transactor] !== "reader" &&
|
|
352
|
-
memberState[transactor] !== "writeOnly"
|
|
353
|
-
) {
|
|
354
|
-
logPermissionError(
|
|
355
|
-
"Only admins, writers, readers and writeOnly can set child extensions",
|
|
356
|
-
);
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
348
|
validTransactions.push({ txID: { sessionID, txIndex }, tx });
|
|
361
349
|
continue;
|
|
362
350
|
} else if (isWriteKeyForMember(change.key)) {
|
|
@@ -157,6 +157,29 @@ describe("extend", () => {
|
|
|
157
157
|
|
|
158
158
|
expect(childGroup.roleOf(node2.accountID)).toEqual(undefined);
|
|
159
159
|
});
|
|
160
|
+
|
|
161
|
+
test("should be possible to extend a group without having membership in the parent group", async () => {
|
|
162
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
163
|
+
"server",
|
|
164
|
+
"server",
|
|
165
|
+
"server",
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const parentGroup = node1.node.createGroup();
|
|
169
|
+
const childGroup = node2.node.createGroup();
|
|
170
|
+
|
|
171
|
+
const alice = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
172
|
+
parentGroup.addMember(alice, "writer");
|
|
173
|
+
|
|
174
|
+
const parentGroupOnNode2 = await loadCoValueOrFail(
|
|
175
|
+
node2.node,
|
|
176
|
+
parentGroup.id,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
childGroup.extend(parentGroupOnNode2);
|
|
180
|
+
|
|
181
|
+
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
|
182
|
+
});
|
|
160
183
|
});
|
|
161
184
|
|
|
162
185
|
describe("unextend", () => {
|
|
@@ -191,6 +214,78 @@ describe("unextend", () => {
|
|
|
191
214
|
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
192
215
|
});
|
|
193
216
|
|
|
217
|
+
test("should work when the account has no access to the parent group but owns the writeKey", async () => {
|
|
218
|
+
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
219
|
+
"server",
|
|
220
|
+
"server",
|
|
221
|
+
"server",
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const parentGroup = node1.node.createGroup();
|
|
225
|
+
const childGroup = node2.node.createGroup();
|
|
226
|
+
|
|
227
|
+
const alice = await loadCoValueOrFail(node1.node, node3.accountID);
|
|
228
|
+
parentGroup.addMember(alice, "writer");
|
|
229
|
+
|
|
230
|
+
const parentGroupOnNode2 = await loadCoValueOrFail(
|
|
231
|
+
node2.node,
|
|
232
|
+
parentGroup.id,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
childGroup.extend(parentGroupOnNode2);
|
|
236
|
+
|
|
237
|
+
expect(childGroup.roleOf(alice.id)).toBe("writer");
|
|
238
|
+
|
|
239
|
+
// `childGroup` no longer has `parentGroup`'s members
|
|
240
|
+
await childGroup.revokeExtend(parentGroup);
|
|
241
|
+
expect(childGroup.roleOf(alice.id)).toBe(undefined);
|
|
242
|
+
|
|
243
|
+
const map = childGroup.createMap();
|
|
244
|
+
map.set("test", "Hello!");
|
|
245
|
+
|
|
246
|
+
const mapOnAlice = await loadCoValueOrFail(node3.node, map.id);
|
|
247
|
+
|
|
248
|
+
expect(mapOnAlice.get("test")).toEqual(undefined);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("should work when the account has no access to the parent group and not owns the writeKey", async () => {
|
|
252
|
+
const {
|
|
253
|
+
node1: bobNode,
|
|
254
|
+
node2: johnNode,
|
|
255
|
+
node3: aliceNode,
|
|
256
|
+
} = await createThreeConnectedNodes("server", "server", "server");
|
|
257
|
+
|
|
258
|
+
const parentGroup = bobNode.node.createGroup();
|
|
259
|
+
const childGroup = johnNode.node.createGroup();
|
|
260
|
+
|
|
261
|
+
const parentGroupOnJohn = await loadCoValueOrFail(
|
|
262
|
+
johnNode.node,
|
|
263
|
+
parentGroup.id,
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
childGroup.extend(parentGroupOnJohn);
|
|
267
|
+
|
|
268
|
+
const bob = await loadCoValueOrFail(johnNode.node, bobNode.accountID);
|
|
269
|
+
const alice = await loadCoValueOrFail(johnNode.node, aliceNode.accountID);
|
|
270
|
+
childGroup.addMember(alice, "admin");
|
|
271
|
+
|
|
272
|
+
const childGroupOnAlice = await loadCoValueOrFail(
|
|
273
|
+
aliceNode.node,
|
|
274
|
+
childGroup.id,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// `childGroup` no longer has `parentGroup`'s members
|
|
278
|
+
await childGroupOnAlice.revokeExtend(parentGroup);
|
|
279
|
+
expect(childGroupOnAlice.roleOf(bob.id)).toBe(undefined);
|
|
280
|
+
|
|
281
|
+
const map = childGroupOnAlice.createMap();
|
|
282
|
+
map.set("test", "Hello!");
|
|
283
|
+
|
|
284
|
+
const mapOnBob = await loadCoValueOrFail(bobNode.node, map.id);
|
|
285
|
+
|
|
286
|
+
expect(mapOnBob.get("test")).toEqual(undefined);
|
|
287
|
+
});
|
|
288
|
+
|
|
194
289
|
test("should do nothing if applied to a group that is not extended", async () => {
|
|
195
290
|
const { node1, node2, node3 } = await createThreeConnectedNodes(
|
|
196
291
|
"server",
|
|
@@ -323,6 +323,10 @@ describe("Group invites", () => {
|
|
|
323
323
|
|
|
324
324
|
expect(groupOnMemberNode.roleOf(member.accountID)).toEqual("reader");
|
|
325
325
|
|
|
326
|
+
await waitFor(() => {
|
|
327
|
+
expect(group.roleOf(member.accountID)).toEqual("reader");
|
|
328
|
+
});
|
|
329
|
+
|
|
326
330
|
// Verify read access is restored
|
|
327
331
|
const personOnMemberNode = await loadCoValueOrFail(member.node, person.id);
|
|
328
332
|
expect(personOnMemberNode.get("name")).toEqual("John Doe");
|
|
@@ -1982,40 +1982,6 @@ test("Writers, readers and writeOnly can set child extensions", () => {
|
|
|
1982
1982
|
expect(groupAsReader.get(`child_${childGroup.id}`)).toEqual("extend");
|
|
1983
1983
|
});
|
|
1984
1984
|
|
|
1985
|
-
test("Invitees can not set child extensions", () => {
|
|
1986
|
-
const { group, node } = newGroupHighLevel();
|
|
1987
|
-
const childGroup = node.createGroup();
|
|
1988
|
-
|
|
1989
|
-
const adminInvite = createAccountInNode(node);
|
|
1990
|
-
const writerInvite = createAccountInNode(node);
|
|
1991
|
-
const readerInvite = createAccountInNode(node);
|
|
1992
|
-
|
|
1993
|
-
group.addMember(adminInvite, "adminInvite");
|
|
1994
|
-
group.addMember(writerInvite, "writerInvite");
|
|
1995
|
-
group.addMember(readerInvite, "readerInvite");
|
|
1996
|
-
|
|
1997
|
-
const groupAsAdminInvite = expectGroup(
|
|
1998
|
-
group.core.contentInClonedNodeWithDifferentAccount(adminInvite),
|
|
1999
|
-
);
|
|
2000
|
-
|
|
2001
|
-
groupAsAdminInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
2002
|
-
expect(groupAsAdminInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
2003
|
-
|
|
2004
|
-
const groupAsWriterInvite = expectGroup(
|
|
2005
|
-
group.core.contentInClonedNodeWithDifferentAccount(writerInvite),
|
|
2006
|
-
);
|
|
2007
|
-
|
|
2008
|
-
groupAsWriterInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
2009
|
-
expect(groupAsWriterInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
2010
|
-
|
|
2011
|
-
const groupAsReaderInvite = expectGroup(
|
|
2012
|
-
group.core.contentInClonedNodeWithDifferentAccount(readerInvite),
|
|
2013
|
-
);
|
|
2014
|
-
|
|
2015
|
-
groupAsReaderInvite.set(`child_${childGroup.id}`, "extend", "trusting");
|
|
2016
|
-
expect(groupAsReaderInvite.get(`child_${childGroup.id}`)).toBeUndefined();
|
|
2017
|
-
});
|
|
2018
|
-
|
|
2019
1985
|
test("Member roles are inherited by child groups (except invites)", () => {
|
|
2020
1986
|
const { group, node, admin } = newGroupHighLevel();
|
|
2021
1987
|
const parentGroup = node.createGroup();
|
|
@@ -56,6 +56,10 @@ describe("invitations sync", () => {
|
|
|
56
56
|
"invite-consumer -> server | KNOWN Group sessions: header/5",
|
|
57
57
|
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
|
58
58
|
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
|
59
|
+
"invite-consumer -> server | CONTENT Group header: false new: After: 0 New: 2",
|
|
60
|
+
"server -> invite-consumer | KNOWN Group sessions: header/7",
|
|
61
|
+
"server -> invite-provider | CONTENT Group header: false new: After: 0 New: 2",
|
|
62
|
+
"invite-provider -> server | KNOWN Group sessions: header/7",
|
|
59
63
|
]
|
|
60
64
|
`);
|
|
61
65
|
});
|
|
@@ -104,6 +108,10 @@ describe("invitations sync", () => {
|
|
|
104
108
|
"invite-consumer -> server | KNOWN Group sessions: header/7",
|
|
105
109
|
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
|
106
110
|
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
|
111
|
+
"invite-consumer -> server | CONTENT Group header: false new: After: 0 New: 2",
|
|
112
|
+
"server -> invite-consumer | KNOWN Group sessions: header/9",
|
|
113
|
+
"server -> invite-provider | CONTENT Group header: false new: After: 0 New: 2",
|
|
114
|
+
"invite-provider -> server | KNOWN Group sessions: header/9",
|
|
107
115
|
]
|
|
108
116
|
`);
|
|
109
117
|
});
|
|
@@ -148,15 +156,15 @@ describe("invitations sync", () => {
|
|
|
148
156
|
"invite-consumer -> server | LOAD ParentGroup sessions: empty",
|
|
149
157
|
"server -> invite-consumer | CONTENT ParentGroup header: true new: After: 0 New: 6",
|
|
150
158
|
"invite-consumer -> server | KNOWN ParentGroup sessions: header/6",
|
|
159
|
+
"invite-consumer -> server | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
|
160
|
+
"server -> invite-consumer | KNOWN ParentGroup sessions: header/8",
|
|
161
|
+
"server -> invite-provider | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
|
151
162
|
"invite-consumer -> server | LOAD Map sessions: empty",
|
|
163
|
+
"invite-provider -> server | KNOWN ParentGroup sessions: header/8",
|
|
152
164
|
"server -> invite-consumer | CONTENT Group header: true new: After: 0 New: 5",
|
|
153
165
|
"invite-consumer -> server | KNOWN Group sessions: header/5",
|
|
154
166
|
"server -> invite-consumer | CONTENT Map header: true new: After: 0 New: 1",
|
|
155
|
-
"invite-consumer -> server | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
|
156
|
-
"server -> invite-consumer | KNOWN ParentGroup sessions: header/8",
|
|
157
|
-
"server -> invite-provider | CONTENT ParentGroup header: false new: After: 0 New: 2",
|
|
158
167
|
"invite-consumer -> server | KNOWN Map sessions: header/1",
|
|
159
|
-
"invite-provider -> server | KNOWN ParentGroup sessions: header/8",
|
|
160
168
|
]
|
|
161
169
|
`);
|
|
162
170
|
});
|