cojson 0.10.8 → 0.11.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.
Files changed (180) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +20 -0
  3. package/dist/CoValuesStore.d.ts +12 -0
  4. package/dist/CoValuesStore.d.ts.map +1 -0
  5. package/dist/PeerKnownStates.d.ts +38 -0
  6. package/dist/PeerKnownStates.d.ts.map +1 -0
  7. package/dist/PeerState.d.ts +46 -0
  8. package/dist/PeerState.d.ts.map +1 -0
  9. package/dist/PriorityBasedMessageQueue.d.ts +18 -0
  10. package/dist/PriorityBasedMessageQueue.d.ts.map +1 -0
  11. package/dist/SyncStateManager.d.ts +20 -0
  12. package/dist/SyncStateManager.d.ts.map +1 -0
  13. package/dist/base64url.d.ts +3 -0
  14. package/dist/base64url.d.ts.map +1 -0
  15. package/dist/base64url.test.d.ts +2 -0
  16. package/dist/base64url.test.d.ts.map +1 -0
  17. package/dist/coValue.d.ts +52 -0
  18. package/dist/coValue.d.ts.map +1 -0
  19. package/dist/coValueCore.d.ts +143 -0
  20. package/dist/coValueCore.d.ts.map +1 -0
  21. package/dist/coValueCore.js +3 -9
  22. package/dist/coValueCore.js.map +1 -1
  23. package/dist/coValueState.d.ts +58 -0
  24. package/dist/coValueState.d.ts.map +1 -0
  25. package/dist/coValues/account.d.ts +69 -0
  26. package/dist/coValues/account.d.ts.map +1 -0
  27. package/dist/coValues/account.js +9 -10
  28. package/dist/coValues/account.js.map +1 -1
  29. package/dist/coValues/coList.d.ts +163 -0
  30. package/dist/coValues/coList.d.ts.map +1 -0
  31. package/dist/coValues/coMap.d.ts +142 -0
  32. package/dist/coValues/coMap.d.ts.map +1 -0
  33. package/dist/coValues/coPlainText.d.ts +33 -0
  34. package/dist/coValues/coPlainText.d.ts.map +1 -0
  35. package/dist/coValues/coStream.d.ts +109 -0
  36. package/dist/coValues/coStream.d.ts.map +1 -0
  37. package/dist/coValues/group.d.ts +143 -0
  38. package/dist/coValues/group.d.ts.map +1 -0
  39. package/dist/coValues/group.js +52 -6
  40. package/dist/coValues/group.js.map +1 -1
  41. package/dist/coreToCoValue.d.ts +15 -0
  42. package/dist/coreToCoValue.d.ts.map +1 -0
  43. package/dist/crypto/PureJSCrypto.d.ts +50 -0
  44. package/dist/crypto/PureJSCrypto.d.ts.map +1 -0
  45. package/dist/crypto/WasmCrypto.d.ts +49 -0
  46. package/dist/crypto/WasmCrypto.d.ts.map +1 -0
  47. package/dist/crypto/crypto.d.ts +142 -0
  48. package/dist/crypto/crypto.d.ts.map +1 -0
  49. package/dist/exports.d.ts +84 -0
  50. package/dist/exports.d.ts.map +1 -0
  51. package/dist/ids.d.ts +23 -0
  52. package/dist/ids.d.ts.map +1 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/jsonStringify.d.ts +7 -0
  56. package/dist/jsonStringify.d.ts.map +1 -0
  57. package/dist/jsonValue.d.ts +45 -0
  58. package/dist/jsonValue.d.ts.map +1 -0
  59. package/dist/localNode.d.ts +111 -0
  60. package/dist/localNode.d.ts.map +1 -0
  61. package/dist/localNode.js +3 -5
  62. package/dist/localNode.js.map +1 -1
  63. package/dist/logger.d.ts +33 -0
  64. package/dist/logger.d.ts.map +1 -0
  65. package/dist/media.d.ts +8 -0
  66. package/dist/media.d.ts.map +1 -0
  67. package/dist/permissions.d.ts +24 -0
  68. package/dist/permissions.d.ts.map +1 -0
  69. package/dist/permissions.js +5 -9
  70. package/dist/permissions.js.map +1 -1
  71. package/dist/priority.d.ts +19 -0
  72. package/dist/priority.d.ts.map +1 -0
  73. package/dist/storage/FileSystem.d.ts +37 -0
  74. package/dist/storage/FileSystem.d.ts.map +1 -0
  75. package/dist/storage/chunksAndKnownStates.d.ts +7 -0
  76. package/dist/storage/chunksAndKnownStates.d.ts.map +1 -0
  77. package/dist/storage/index.d.ts +52 -0
  78. package/dist/storage/index.d.ts.map +1 -0
  79. package/dist/streamUtils.d.ts +13 -0
  80. package/dist/streamUtils.d.ts.map +1 -0
  81. package/dist/sync.d.ts +97 -0
  82. package/dist/sync.d.ts.map +1 -0
  83. package/dist/tests/PeerKnownStates.test.d.ts +2 -0
  84. package/dist/tests/PeerKnownStates.test.d.ts.map +1 -0
  85. package/dist/tests/PeerKnownStates.test.js +82 -0
  86. package/dist/tests/PeerKnownStates.test.js.map +1 -0
  87. package/dist/tests/PeerState.test.d.ts +2 -0
  88. package/dist/tests/PeerState.test.d.ts.map +1 -0
  89. package/dist/tests/PeerState.test.js +188 -0
  90. package/dist/tests/PeerState.test.js.map +1 -0
  91. package/dist/tests/PriorityBasedMessageQueue.test.d.ts +2 -0
  92. package/dist/tests/PriorityBasedMessageQueue.test.d.ts.map +1 -0
  93. package/dist/tests/PriorityBasedMessageQueue.test.js +120 -0
  94. package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -0
  95. package/dist/tests/SyncStateManager.test.d.ts +2 -0
  96. package/dist/tests/SyncStateManager.test.d.ts.map +1 -0
  97. package/dist/tests/SyncStateManager.test.js +127 -0
  98. package/dist/tests/SyncStateManager.test.js.map +1 -0
  99. package/dist/tests/account.test.d.ts +2 -0
  100. package/dist/tests/account.test.d.ts.map +1 -0
  101. package/dist/tests/account.test.js +66 -0
  102. package/dist/tests/account.test.js.map +1 -0
  103. package/dist/tests/coList.test.d.ts +2 -0
  104. package/dist/tests/coList.test.d.ts.map +1 -0
  105. package/dist/tests/coList.test.js +120 -0
  106. package/dist/tests/coList.test.js.map +1 -0
  107. package/dist/tests/coMap.test.d.ts +2 -0
  108. package/dist/tests/coMap.test.d.ts.map +1 -0
  109. package/dist/tests/coMap.test.js +164 -0
  110. package/dist/tests/coMap.test.js.map +1 -0
  111. package/dist/tests/coPlainText.test.d.ts +2 -0
  112. package/dist/tests/coPlainText.test.d.ts.map +1 -0
  113. package/dist/tests/coPlainText.test.js +99 -0
  114. package/dist/tests/coPlainText.test.js.map +1 -0
  115. package/dist/tests/coStream.test.d.ts +2 -0
  116. package/dist/tests/coStream.test.d.ts.map +1 -0
  117. package/dist/tests/coStream.test.js +206 -0
  118. package/dist/tests/coStream.test.js.map +1 -0
  119. package/dist/tests/coValueCore.test.d.ts +2 -0
  120. package/dist/tests/coValueCore.test.d.ts.map +1 -0
  121. package/dist/tests/coValueCore.test.js +164 -0
  122. package/dist/tests/coValueCore.test.js.map +1 -0
  123. package/dist/tests/coValueState.test.d.ts +2 -0
  124. package/dist/tests/coValueState.test.d.ts.map +1 -0
  125. package/dist/tests/coValueState.test.js +364 -0
  126. package/dist/tests/coValueState.test.js.map +1 -0
  127. package/dist/tests/crypto.test.d.ts +2 -0
  128. package/dist/tests/crypto.test.d.ts.map +1 -0
  129. package/dist/tests/crypto.test.js +144 -0
  130. package/dist/tests/crypto.test.js.map +1 -0
  131. package/dist/tests/cryptoImpl.test.d.ts +2 -0
  132. package/dist/tests/cryptoImpl.test.d.ts.map +1 -0
  133. package/dist/tests/cryptoImpl.test.js +144 -0
  134. package/dist/tests/cryptoImpl.test.js.map +1 -0
  135. package/dist/tests/group.test.d.ts +2 -0
  136. package/dist/tests/group.test.d.ts.map +1 -0
  137. package/dist/tests/group.test.js +576 -0
  138. package/dist/tests/group.test.js.map +1 -0
  139. package/dist/tests/logger.test.d.ts +2 -0
  140. package/dist/tests/logger.test.d.ts.map +1 -0
  141. package/dist/tests/logger.test.js +118 -0
  142. package/dist/tests/logger.test.js.map +1 -0
  143. package/dist/tests/permissions.test.d.ts +2 -0
  144. package/dist/tests/permissions.test.d.ts.map +1 -0
  145. package/dist/tests/permissions.test.js +2051 -0
  146. package/dist/tests/permissions.test.js.map +1 -0
  147. package/dist/tests/priority.test.d.ts +2 -0
  148. package/dist/tests/priority.test.d.ts.map +1 -0
  149. package/dist/tests/priority.test.js +61 -0
  150. package/dist/tests/priority.test.js.map +1 -0
  151. package/dist/tests/sync.test.d.ts +2 -0
  152. package/dist/tests/sync.test.d.ts.map +1 -0
  153. package/dist/tests/sync.test.js +1548 -0
  154. package/dist/tests/sync.test.js.map +1 -0
  155. package/dist/tests/testUtils.d.ts +142 -0
  156. package/dist/tests/testUtils.d.ts.map +1 -0
  157. package/dist/tests/testUtils.js +315 -0
  158. package/dist/tests/testUtils.js.map +1 -0
  159. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts +4 -0
  160. package/dist/typeUtils/accountOrAgentIDfromSessionID.d.ts.map +1 -0
  161. package/dist/typeUtils/expectGroup.d.ts +4 -0
  162. package/dist/typeUtils/expectGroup.d.ts.map +1 -0
  163. package/dist/typeUtils/isAccountID.d.ts +4 -0
  164. package/dist/typeUtils/isAccountID.d.ts.map +1 -0
  165. package/dist/typeUtils/isCoValue.d.ts +4 -0
  166. package/dist/typeUtils/isCoValue.d.ts.map +1 -0
  167. package/dist/utils.d.ts +5 -0
  168. package/dist/utils.d.ts.map +1 -0
  169. package/package.json +6 -6
  170. package/src/coValueCore.ts +3 -9
  171. package/src/coValues/account.ts +15 -15
  172. package/src/coValues/group.ts +85 -12
  173. package/src/jsonValue.ts +9 -5
  174. package/src/localNode.ts +3 -5
  175. package/src/permissions.ts +5 -15
  176. package/src/tests/coValueCore.test.ts +2 -2
  177. package/src/tests/group.test.ts +187 -0
  178. package/src/tests/permissions.test.ts +330 -57
  179. package/src/tests/testUtils.ts +4 -1
  180. package/tsconfig.json +4 -2
@@ -0,0 +1,2051 @@
1
+ import { expect, test, vi } from "vitest";
2
+ import { expectMap } from "../coValue.js";
3
+ import { ControlledAgent } from "../coValues/account.js";
4
+ import { WasmCrypto } from "../crypto/WasmCrypto.js";
5
+ import { expectGroup } from "../typeUtils/expectGroup.js";
6
+ import { connectTwoPeers, createThreeConnectedNodes, createTwoConnectedNodes, groupWithTwoAdmins, groupWithTwoAdminsHighLevel, loadCoValueOrFail, newGroup, newGroupHighLevel, } from "./testUtils.js";
7
+ const Crypto = await WasmCrypto.create();
8
+ test("Initial admin can add another admin to a group", () => {
9
+ groupWithTwoAdmins();
10
+ });
11
+ test("Initial admin can add another admin to a group (high level)", () => {
12
+ groupWithTwoAdminsHighLevel();
13
+ });
14
+ test("Added admin can add a third admin to a group", () => {
15
+ const { groupCore, otherAdmin, node } = groupWithTwoAdmins();
16
+ const groupAsOtherAdmin = expectGroup(groupCore
17
+ .testWithDifferentAccount(otherAdmin, Crypto.newRandomSessionID(otherAdmin.id))
18
+ .getCurrentContent());
19
+ expect(groupAsOtherAdmin.get(otherAdmin.id)).toEqual("admin");
20
+ const thirdAdmin = node.createAccount();
21
+ groupAsOtherAdmin.set(thirdAdmin.id, "admin", "trusting");
22
+ expect(groupAsOtherAdmin.get(thirdAdmin.id)).toEqual("admin");
23
+ });
24
+ test("Added adming can add a third admin to a group (high level)", () => {
25
+ const { group, otherAdmin } = groupWithTwoAdminsHighLevel();
26
+ const groupAsOtherAdmin = expectGroup(group.core
27
+ .testWithDifferentAccount(otherAdmin, Crypto.newRandomSessionID(otherAdmin.id))
28
+ .getCurrentContent());
29
+ const thirdAdmin = groupAsOtherAdmin.core.node.createAccount();
30
+ groupAsOtherAdmin.addMember(thirdAdmin, "admin");
31
+ expect(groupAsOtherAdmin.get(thirdAdmin.id)).toEqual("admin");
32
+ });
33
+ test("Admins can't demote other admins in a group", () => {
34
+ const { groupCore, admin, otherAdmin } = groupWithTwoAdmins();
35
+ const groupContent = expectGroup(groupCore.getCurrentContent());
36
+ groupContent.set(otherAdmin.id, "writer", "trusting");
37
+ expect(groupContent.get(otherAdmin.id)).toEqual("admin");
38
+ expect(groupContent.get(otherAdmin.id)).toEqual("admin");
39
+ const groupAsOtherAdmin = expectGroup(groupCore
40
+ .testWithDifferentAccount(otherAdmin, Crypto.newRandomSessionID(otherAdmin.id))
41
+ .getCurrentContent());
42
+ groupAsOtherAdmin.set(admin.id, "writer", "trusting");
43
+ expect(groupAsOtherAdmin.get(admin.id)).toEqual("admin");
44
+ });
45
+ test("Admins can't demote other admins in a group (high level)", () => {
46
+ const { group, admin, otherAdmin } = groupWithTwoAdminsHighLevel();
47
+ const groupAsOtherAdmin = expectGroup(group.core
48
+ .testWithDifferentAccount(otherAdmin, Crypto.newRandomSessionID(otherAdmin.id))
49
+ .getCurrentContent());
50
+ expect(() => groupAsOtherAdmin.addMemberInternal(admin.id, "writer")).toThrow("Failed to set role");
51
+ expect(groupAsOtherAdmin.get(admin.id)).toEqual("admin");
52
+ });
53
+ test("Admins an add writers to a group, who can't add admins, writers, or readers", () => {
54
+ const { groupCore, node } = newGroup();
55
+ const writer = node.createAccount();
56
+ const groupContent = expectGroup(groupCore.getCurrentContent());
57
+ groupContent.set(writer.id, "writer", "trusting");
58
+ expect(groupContent.get(writer.id)).toEqual("writer");
59
+ expect(groupContent.get(writer.id)).toEqual("writer");
60
+ const groupAsWriter = expectGroup(groupCore
61
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
62
+ .getCurrentContent());
63
+ expect(groupAsWriter.get(writer.id)).toEqual("writer");
64
+ const otherAgent = node.createAccount();
65
+ groupAsWriter.set(otherAgent.id, "admin", "trusting");
66
+ expect(groupAsWriter.get(otherAgent.id)).toBeUndefined();
67
+ groupAsWriter.set(otherAgent.id, "writer", "trusting");
68
+ expect(groupAsWriter.get(otherAgent.id)).toBeUndefined();
69
+ groupAsWriter.set(otherAgent.id, "reader", "trusting");
70
+ expect(groupAsWriter.get(otherAgent.id)).toBeUndefined();
71
+ });
72
+ test("Admins an add writers to a group, who can't add admins, writers, or readers (high level)", () => {
73
+ const { group, node } = newGroupHighLevel();
74
+ const writer = node.createAccount();
75
+ group.addMember(writer, "writer");
76
+ expect(group.get(writer.id)).toEqual("writer");
77
+ const groupAsWriter = expectGroup(group.core
78
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
79
+ .getCurrentContent());
80
+ expect(groupAsWriter.get(writer.id)).toEqual("writer");
81
+ const otherAgent = groupAsWriter.core.node.createAccount();
82
+ expect(() => groupAsWriter.addMember(otherAgent, "admin")).toThrow("Failed to set role");
83
+ expect(() => groupAsWriter.addMember(otherAgent, "writer")).toThrow("Failed to set role");
84
+ expect(() => groupAsWriter.addMember(otherAgent, "reader")).toThrow("Failed to set role");
85
+ expect(groupAsWriter.get(otherAgent.id)).toBeUndefined();
86
+ });
87
+ test("Admins can add readers to a group, who can't add admins, writers, or readers", () => {
88
+ const { groupCore, node } = newGroup();
89
+ const reader = node.createAccount();
90
+ const groupContent = expectGroup(groupCore.getCurrentContent());
91
+ groupContent.set(reader.id, "reader", "trusting");
92
+ expect(groupContent.get(reader.id)).toEqual("reader");
93
+ const groupAsReader = expectGroup(groupCore
94
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
95
+ .getCurrentContent());
96
+ expect(groupAsReader.get(reader.id)).toEqual("reader");
97
+ const otherAgent = node.createAccount();
98
+ groupAsReader.set(otherAgent.id, "admin", "trusting");
99
+ expect(groupAsReader.get(otherAgent.id)).toBeUndefined();
100
+ groupAsReader.set(otherAgent.id, "writer", "trusting");
101
+ expect(groupAsReader.get(otherAgent.id)).toBeUndefined();
102
+ groupAsReader.set(otherAgent.id, "reader", "trusting");
103
+ expect(groupAsReader.get(otherAgent.id)).toBeUndefined();
104
+ });
105
+ test("Admins can add readers to a group, who can't add admins, writers, or readers (high level)", () => {
106
+ const { group, node } = newGroupHighLevel();
107
+ const reader = node.createAccount();
108
+ group.addMember(reader, "reader");
109
+ expect(group.get(reader.id)).toEqual("reader");
110
+ const groupAsReader = expectGroup(group.core
111
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
112
+ .getCurrentContent());
113
+ expect(groupAsReader.get(reader.id)).toEqual("reader");
114
+ const otherAgent = groupAsReader.core.node.createAccount();
115
+ expect(() => groupAsReader.addMember(otherAgent, "admin")).toThrow("Failed to set role");
116
+ expect(() => groupAsReader.addMember(otherAgent, "writer")).toThrow("Failed to set role");
117
+ expect(() => groupAsReader.addMember(otherAgent, "reader")).toThrow("Failed to set role");
118
+ expect(groupAsReader.get(otherAgent.id)).toBeUndefined();
119
+ });
120
+ test("Admins can write to an object that is owned by their group", () => {
121
+ const { node, groupCore } = newGroup();
122
+ const childObject = node.createCoValue({
123
+ type: "comap",
124
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
125
+ meta: null,
126
+ ...Crypto.createdNowUnique(),
127
+ });
128
+ const childContent = expectMap(childObject.getCurrentContent());
129
+ childContent.set("foo", "bar", "trusting");
130
+ expect(childContent.get("foo")).toEqual("bar");
131
+ });
132
+ test("Admins can write to an object that is owned by their group (high level)", () => {
133
+ const { group } = newGroupHighLevel();
134
+ const childObject = group.createMap();
135
+ childObject.set("foo", "bar", "trusting");
136
+ expect(childObject.get("foo")).toEqual("bar");
137
+ });
138
+ test("Writers can write to an object that is owned by their group", () => {
139
+ const { node, groupCore } = newGroup();
140
+ const writer = node.createAccount();
141
+ const group = expectGroup(groupCore.getCurrentContent());
142
+ group.set(writer.id, "writer", "trusting");
143
+ expect(group.get(writer.id)).toEqual("writer");
144
+ const childObject = node.createCoValue({
145
+ type: "comap",
146
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
147
+ meta: null,
148
+ ...Crypto.createdNowUnique(),
149
+ });
150
+ const childObjectAsWriter = childObject.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id));
151
+ const childContentAsWriter = expectMap(childObjectAsWriter.getCurrentContent());
152
+ childContentAsWriter.set("foo", "bar", "trusting");
153
+ expect(childContentAsWriter.get("foo")).toEqual("bar");
154
+ });
155
+ test("Writers can write to an object that is owned by their group (high level)", () => {
156
+ const { node, group } = newGroupHighLevel();
157
+ const writer = node.createAccount();
158
+ group.addMember(writer, "writer");
159
+ const childObject = group.createMap();
160
+ const childObjectAsWriter = expectMap(childObject.core
161
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
162
+ .getCurrentContent());
163
+ childObjectAsWriter.set("foo", "bar", "trusting");
164
+ expect(childObjectAsWriter.get("foo")).toEqual("bar");
165
+ });
166
+ test("Readers can not write to an object that is owned by their group", () => {
167
+ const { node, groupCore } = newGroup();
168
+ const reader = node.createAccount();
169
+ const group = expectGroup(groupCore.getCurrentContent());
170
+ group.set(reader.id, "reader", "trusting");
171
+ expect(group.get(reader.id)).toEqual("reader");
172
+ const childObject = node.createCoValue({
173
+ type: "comap",
174
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
175
+ meta: null,
176
+ ...Crypto.createdNowUnique(),
177
+ });
178
+ const childObjectAsReader = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
179
+ const childContentAsReader = expectMap(childObjectAsReader.getCurrentContent());
180
+ childContentAsReader.set("foo", "bar", "trusting");
181
+ expect(childContentAsReader.get("foo")).toBeUndefined();
182
+ });
183
+ test("Readers can not write to an object that is owned by their group (high level)", () => {
184
+ const { node, group } = newGroupHighLevel();
185
+ const reader = node.createAccount();
186
+ group.addMember(reader, "reader");
187
+ const childObject = group.createMap();
188
+ const childObjectAsReader = expectMap(childObject.core
189
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
190
+ .getCurrentContent());
191
+ childObjectAsReader.set("foo", "bar", "trusting");
192
+ expect(childObjectAsReader.get("foo")).toBeUndefined();
193
+ });
194
+ test("Admins can set group read key and then use it to create and read private transactions in owned objects", () => {
195
+ const { node, groupCore, admin } = newGroup();
196
+ const groupContent = expectGroup(groupCore.getCurrentContent());
197
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
198
+ const revelation = Crypto.seal({
199
+ message: readKey,
200
+ from: admin.currentSealerSecret(),
201
+ to: admin.currentSealerID(),
202
+ nOnceMaterial: {
203
+ in: groupCore.id,
204
+ tx: groupCore.nextTransactionID(),
205
+ },
206
+ });
207
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
208
+ expect(groupContent.get(`${readKeyID}_for_${admin.id}`)).toEqual(revelation);
209
+ groupContent.set("readKey", readKeyID, "trusting");
210
+ expect(groupContent.get("readKey")).toEqual(readKeyID);
211
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey);
212
+ const childObject = node.createCoValue({
213
+ type: "comap",
214
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
215
+ meta: null,
216
+ ...Crypto.createdNowUnique(),
217
+ });
218
+ const childContent = expectMap(childObject.getCurrentContent());
219
+ childContent.set("foo", "bar", "private");
220
+ expect(childContent.get("foo")).toEqual("bar");
221
+ });
222
+ test("Admins can set group read key and then use it to create and read private transactions in owned objects (high level)", () => {
223
+ const { group } = newGroupHighLevel();
224
+ const childObject = group.createMap();
225
+ childObject.set("foo", "bar", "private");
226
+ expect(childObject.get("foo")).toEqual("bar");
227
+ });
228
+ test("Admins can set group read key and then writers can use it to create and read private transactions in owned objects", () => {
229
+ const { node, groupCore, admin } = newGroup();
230
+ const writer = node.createAccount();
231
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
232
+ const groupContent = expectGroup(groupCore.getCurrentContent());
233
+ groupContent.set(writer.id, "writer", "trusting");
234
+ expect(groupContent.get(writer.id)).toEqual("writer");
235
+ const revelation1 = Crypto.seal({
236
+ message: readKey,
237
+ from: admin.currentSealerSecret(),
238
+ to: admin.currentSealerID(),
239
+ nOnceMaterial: {
240
+ in: groupCore.id,
241
+ tx: groupCore.nextTransactionID(),
242
+ },
243
+ });
244
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation1, "trusting");
245
+ const revelation2 = Crypto.seal({
246
+ message: readKey,
247
+ from: admin.currentSealerSecret(),
248
+ to: writer.currentSealerID(),
249
+ nOnceMaterial: {
250
+ in: groupCore.id,
251
+ tx: groupCore.nextTransactionID(),
252
+ },
253
+ });
254
+ groupContent.set(`${readKeyID}_for_${writer.id}`, revelation2, "trusting");
255
+ groupContent.set("readKey", readKeyID, "trusting");
256
+ const childObject = node.createCoValue({
257
+ type: "comap",
258
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
259
+ meta: null,
260
+ ...Crypto.createdNowUnique(),
261
+ });
262
+ const childObjectAsWriter = childObject.testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id));
263
+ expect(childObject.getCurrentReadKey().secret).toEqual(readKey);
264
+ const childContentAsWriter = expectMap(childObjectAsWriter.getCurrentContent());
265
+ childContentAsWriter.set("foo", "bar", "private");
266
+ expect(childContentAsWriter.get("foo")).toEqual("bar");
267
+ });
268
+ test("Admins can set group read key and then writers can use it to create and read private transactions in owned objects (high level)", () => {
269
+ const { node, group } = newGroupHighLevel();
270
+ const writer = node.createAccount();
271
+ group.addMember(writer, "writer");
272
+ const childObject = group.createMap();
273
+ const childObjectAsWriter = expectMap(childObject.core
274
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
275
+ .getCurrentContent());
276
+ childObjectAsWriter.set("foo", "bar", "private");
277
+ expect(childObjectAsWriter.get("foo")).toEqual("bar");
278
+ });
279
+ test("Admins can set group read key and then use it to create private transactions in owned objects, which readers can read", () => {
280
+ const { node, groupCore, admin } = newGroup();
281
+ const reader = node.createAccount();
282
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
283
+ const groupContent = expectGroup(groupCore.getCurrentContent());
284
+ groupContent.set(reader.id, "reader", "trusting");
285
+ expect(groupContent.get(reader.id)).toEqual("reader");
286
+ const revelation1 = Crypto.seal({
287
+ message: readKey,
288
+ from: admin.currentSealerSecret(),
289
+ to: admin.currentSealerID(),
290
+ nOnceMaterial: {
291
+ in: groupCore.id,
292
+ tx: groupCore.nextTransactionID(),
293
+ },
294
+ });
295
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation1, "trusting");
296
+ const revelation2 = Crypto.seal({
297
+ message: readKey,
298
+ from: admin.currentSealerSecret(),
299
+ to: reader.currentSealerID(),
300
+ nOnceMaterial: {
301
+ in: groupCore.id,
302
+ tx: groupCore.nextTransactionID(),
303
+ },
304
+ });
305
+ groupContent.set(`${readKeyID}_for_${reader.id}`, revelation2, "trusting");
306
+ groupContent.set("readKey", readKeyID, "trusting");
307
+ const childObject = node.createCoValue({
308
+ type: "comap",
309
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
310
+ meta: null,
311
+ ...Crypto.createdNowUnique(),
312
+ });
313
+ const childContent = expectMap(childObject.getCurrentContent());
314
+ childContent.set("foo", "bar", "private");
315
+ expect(childContent.get("foo")).toEqual("bar");
316
+ const childObjectAsReader = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
317
+ expect(childObjectAsReader.getCurrentReadKey().secret).toEqual(readKey);
318
+ const childContentAsReader = expectMap(childObjectAsReader.getCurrentContent());
319
+ expect(childContentAsReader.get("foo")).toEqual("bar");
320
+ });
321
+ test("Admins can set group read key and then use it to create private transactions in owned objects, which readers can read (high level)", () => {
322
+ const { node, group } = newGroupHighLevel();
323
+ const reader = node.createAccount();
324
+ group.addMember(reader, "reader");
325
+ const childObject = group.createMap();
326
+ childObject.set("foo", "bar", "private");
327
+ expect(childObject.get("foo")).toEqual("bar");
328
+ const childContentAsReader = expectMap(childObject.core
329
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
330
+ .getCurrentContent());
331
+ expect(childContentAsReader.get("foo")).toEqual("bar");
332
+ });
333
+ test("Admins can set group read key and then use it to create private transactions in owned objects, which readers can read, even with a separate later revelation for the same read key", () => {
334
+ const { node, groupCore, admin } = newGroup();
335
+ const reader1 = node.createAccount();
336
+ const reader2 = node.createAccount();
337
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
338
+ const groupContent = expectGroup(groupCore.getCurrentContent());
339
+ groupContent.set(reader1.id, "reader", "trusting");
340
+ expect(groupContent.get(reader1.id)).toEqual("reader");
341
+ const revelation1 = Crypto.seal({
342
+ message: readKey,
343
+ from: admin.currentSealerSecret(),
344
+ to: admin.currentSealerID(),
345
+ nOnceMaterial: {
346
+ in: groupCore.id,
347
+ tx: groupCore.nextTransactionID(),
348
+ },
349
+ });
350
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation1, "trusting");
351
+ const revelation2 = Crypto.seal({
352
+ message: readKey,
353
+ from: admin.currentSealerSecret(),
354
+ to: reader1.currentSealerID(),
355
+ nOnceMaterial: {
356
+ in: groupCore.id,
357
+ tx: groupCore.nextTransactionID(),
358
+ },
359
+ });
360
+ groupContent.set(`${readKeyID}_for_${reader1.id}`, revelation2, "trusting");
361
+ groupContent.set("readKey", readKeyID, "trusting");
362
+ const childObject = node.createCoValue({
363
+ type: "comap",
364
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
365
+ meta: null,
366
+ ...Crypto.createdNowUnique(),
367
+ });
368
+ const childContent = expectMap(childObject.getCurrentContent());
369
+ childContent.set("foo", "bar", "private");
370
+ expect(childContent.get("foo")).toEqual("bar");
371
+ const childObjectAsReader1 = childObject.testWithDifferentAccount(reader1, Crypto.newRandomSessionID(reader1.id));
372
+ expect(childObjectAsReader1.getCurrentReadKey().secret).toEqual(readKey);
373
+ const childContentAsReader1 = expectMap(childObjectAsReader1.getCurrentContent());
374
+ expect(childContentAsReader1.get("foo")).toEqual("bar");
375
+ const revelation3 = Crypto.seal({
376
+ message: readKey,
377
+ from: admin.currentSealerSecret(),
378
+ to: reader2.currentSealerID(),
379
+ nOnceMaterial: {
380
+ in: groupCore.id,
381
+ tx: groupCore.nextTransactionID(),
382
+ },
383
+ });
384
+ groupContent.set(`${readKeyID}_for_${reader2.id}`, revelation3, "trusting");
385
+ const childObjectAsReader2 = childObject.testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id));
386
+ expect(childObjectAsReader2.getCurrentReadKey().secret).toEqual(readKey);
387
+ const childContentAsReader2 = expectMap(childObjectAsReader2.getCurrentContent());
388
+ expect(childContentAsReader2.get("foo")).toEqual("bar");
389
+ });
390
+ test("Admins can set group read key and then use it to create private transactions in owned objects, which readers can read, even with a separate later revelation for the same read key (high level)", () => {
391
+ const { node, group } = newGroupHighLevel();
392
+ const reader1 = node.createAccount();
393
+ const reader2 = node.createAccount();
394
+ group.addMember(reader1, "reader");
395
+ const childObject = group.createMap();
396
+ childObject.set("foo", "bar", "private");
397
+ expect(childObject.get("foo")).toEqual("bar");
398
+ const childContentAsReader1 = expectMap(childObject.core
399
+ .testWithDifferentAccount(reader1, Crypto.newRandomSessionID(reader1.id))
400
+ .getCurrentContent());
401
+ expect(childContentAsReader1.get("foo")).toEqual("bar");
402
+ group.addMember(reader2, "reader");
403
+ const childContentAsReader2 = expectMap(childObject.core
404
+ .testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id))
405
+ .getCurrentContent());
406
+ expect(childContentAsReader2.get("foo")).toEqual("bar");
407
+ });
408
+ test("Admins can set group read key, make a private transaction in an owned object, rotate the read key, make another private transaction, and both can be read by the admin", () => {
409
+ const { node, groupCore, admin } = newGroup();
410
+ const groupContent = expectGroup(groupCore.getCurrentContent());
411
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
412
+ const revelation1 = Crypto.seal({
413
+ message: readKey,
414
+ from: admin.currentSealerSecret(),
415
+ to: admin.currentSealerID(),
416
+ nOnceMaterial: {
417
+ in: groupCore.id,
418
+ tx: groupCore.nextTransactionID(),
419
+ },
420
+ });
421
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation1, "trusting");
422
+ groupContent.set("readKey", readKeyID, "trusting");
423
+ expect(groupContent.get("readKey")).toEqual(readKeyID);
424
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey);
425
+ const childObject = node.createCoValue({
426
+ type: "comap",
427
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
428
+ meta: null,
429
+ ...Crypto.createdNowUnique(),
430
+ });
431
+ const childContent = expectMap(childObject.getCurrentContent());
432
+ childContent.set("foo", "bar", "private");
433
+ expect(childContent.get("foo")).toEqual("bar");
434
+ const { secret: readKey2, id: readKeyID2 } = Crypto.newRandomKeySecret();
435
+ const revelation2 = Crypto.seal({
436
+ message: readKey2,
437
+ from: admin.currentSealerSecret(),
438
+ to: admin.currentSealerID(),
439
+ nOnceMaterial: {
440
+ in: groupCore.id,
441
+ tx: groupCore.nextTransactionID(),
442
+ },
443
+ });
444
+ groupContent.set(`${readKeyID2}_for_${admin.id}`, revelation2, "trusting");
445
+ groupContent.set("readKey", readKeyID2, "trusting");
446
+ expect(groupContent.get("readKey")).toEqual(readKeyID2);
447
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey2);
448
+ expect(childContent.get("foo")).toEqual("bar");
449
+ childContent.set("foo2", "bar2", "private");
450
+ expect(childContent.get("foo2")).toEqual("bar2");
451
+ });
452
+ test("Admins can set group read key, make a private transaction in an owned object, rotate the read key, make another private transaction, and both can be read by the admin (high level)", () => {
453
+ const { group } = newGroupHighLevel();
454
+ const childObject = group.createMap();
455
+ const firstReadKey = childObject.core.getCurrentReadKey();
456
+ childObject.set("foo", "bar", "private");
457
+ expect(childObject.get("foo")).toEqual("bar");
458
+ group.rotateReadKey();
459
+ expect(childObject.core.getCurrentReadKey()).not.toEqual(firstReadKey);
460
+ childObject.set("foo2", "bar2", "private");
461
+ expect(childObject.get("foo2")).toEqual("bar2");
462
+ });
463
+ test("Admins can set group read key, make a private transaction in an owned object, rotate the read key, add a reader, make another private transaction in the owned object, and both can be read by the reader", () => {
464
+ const { node, groupCore, admin } = newGroup();
465
+ const childObject = node.createCoValue({
466
+ type: "comap",
467
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
468
+ meta: null,
469
+ ...Crypto.createdNowUnique(),
470
+ });
471
+ const groupContent = expectGroup(groupCore.getCurrentContent());
472
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
473
+ const revelation = Crypto.seal({
474
+ message: readKey,
475
+ from: admin.currentSealerSecret(),
476
+ to: admin.currentSealerID(),
477
+ nOnceMaterial: {
478
+ in: groupCore.id,
479
+ tx: groupCore.nextTransactionID(),
480
+ },
481
+ });
482
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
483
+ groupContent.set("readKey", readKeyID, "trusting");
484
+ expect(groupContent.get("readKey")).toEqual(readKeyID);
485
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey);
486
+ const childContent = expectMap(childObject.getCurrentContent());
487
+ childContent.set("foo", "bar", "private");
488
+ expect(childContent.get("foo")).toEqual("bar");
489
+ expect(childContent.get("foo")).toEqual("bar");
490
+ const reader = node.createAccount();
491
+ const { secret: readKey2, id: readKeyID2 } = Crypto.newRandomKeySecret();
492
+ const revelation2 = Crypto.seal({
493
+ message: readKey2,
494
+ from: admin.currentSealerSecret(),
495
+ to: admin.currentSealerID(),
496
+ nOnceMaterial: {
497
+ in: groupCore.id,
498
+ tx: groupCore.nextTransactionID(),
499
+ },
500
+ });
501
+ groupContent.set(`${readKeyID2}_for_${admin.id}`, revelation2, "trusting");
502
+ const revelation3 = Crypto.seal({
503
+ message: readKey2,
504
+ from: admin.currentSealerSecret(),
505
+ to: reader.currentSealerID(),
506
+ nOnceMaterial: {
507
+ in: groupCore.id,
508
+ tx: groupCore.nextTransactionID(),
509
+ },
510
+ });
511
+ groupContent.set(`${readKeyID2}_for_${reader.id}`, revelation3, "trusting");
512
+ groupContent.set(`${readKeyID}_for_${readKeyID2}`, Crypto.encryptKeySecret({
513
+ toEncrypt: { id: readKeyID, secret: readKey },
514
+ encrypting: { id: readKeyID2, secret: readKey2 },
515
+ }).encrypted, "trusting");
516
+ groupContent.set("readKey", readKeyID2, "trusting");
517
+ expect(groupContent.get("readKey")).toEqual(readKeyID2);
518
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey2);
519
+ groupContent.set(reader.id, "reader", "trusting");
520
+ expect(groupContent.get(reader.id)).toEqual("reader");
521
+ childContent.set("foo2", "bar2", "private");
522
+ expect(childContent.get("foo2")).toEqual("bar2");
523
+ const childObjectAsReader = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
524
+ expect(childObjectAsReader.getCurrentReadKey().secret).toEqual(readKey2);
525
+ const childContentAsReader = expectMap(childObjectAsReader.getCurrentContent());
526
+ expect(childContentAsReader.get("foo")).toEqual("bar");
527
+ expect(childContentAsReader.get("foo2")).toEqual("bar2");
528
+ });
529
+ test("Admins can set group read key, make a private transaction in an owned object, rotate the read key, add a reader, make another private transaction in the owned object, and both can be read by the reader (high level)", () => {
530
+ const { node, group } = newGroupHighLevel();
531
+ const childObject = group.createMap();
532
+ const firstReadKey = childObject.core.getCurrentReadKey();
533
+ childObject.set("foo", "bar", "private");
534
+ expect(childObject.get("foo")).toEqual("bar");
535
+ expect(childObject.get("foo")).toEqual("bar");
536
+ group.rotateReadKey();
537
+ expect(childObject.core.getCurrentReadKey()).not.toEqual(firstReadKey);
538
+ const reader = node.createAccount();
539
+ group.addMember(reader, "reader");
540
+ childObject.set("foo2", "bar2", "private");
541
+ expect(childObject.get("foo2")).toEqual("bar2");
542
+ const childContentAsReader = expectMap(childObject.core
543
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
544
+ .getCurrentContent());
545
+ expect(childContentAsReader.get("foo")).toEqual("bar");
546
+ expect(childContentAsReader.get("foo2")).toEqual("bar2");
547
+ });
548
+ test("only admins can add agent ids", () => {
549
+ const { groupCore } = newGroup();
550
+ const inviteSecret = Crypto.newRandomAgentSecret();
551
+ const inviteID = Crypto.getAgentID(inviteSecret);
552
+ const groupAsInvite = expectGroup(groupCore
553
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
554
+ .getCurrentContent());
555
+ groupAsInvite.set(inviteID, "adminInvite", "trusting");
556
+ expect(groupAsInvite.get(inviteID)).toEqual(undefined);
557
+ });
558
+ 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", () => {
559
+ const { node, groupCore, admin } = newGroup();
560
+ const childObject = node.createCoValue({
561
+ type: "comap",
562
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
563
+ meta: null,
564
+ ...Crypto.createdNowUnique(),
565
+ });
566
+ const groupContent = expectGroup(groupCore.getCurrentContent());
567
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
568
+ const reader = node.createAccount();
569
+ const reader2 = node.createAccount();
570
+ const revelation1 = Crypto.seal({
571
+ message: readKey,
572
+ from: admin.currentSealerSecret(),
573
+ to: admin.currentSealerID(),
574
+ nOnceMaterial: {
575
+ in: groupCore.id,
576
+ tx: groupCore.nextTransactionID(),
577
+ },
578
+ });
579
+ groupContent.set(`${readKeyID}_for_${admin.id}`, revelation1, "trusting");
580
+ const revelation2 = Crypto.seal({
581
+ message: readKey,
582
+ from: admin.currentSealerSecret(),
583
+ to: reader.currentSealerID(),
584
+ nOnceMaterial: {
585
+ in: groupCore.id,
586
+ tx: groupCore.nextTransactionID(),
587
+ },
588
+ });
589
+ groupContent.set(`${readKeyID}_for_${reader.id}`, revelation2, "trusting");
590
+ const revelation3 = Crypto.seal({
591
+ message: readKey,
592
+ from: admin.currentSealerSecret(),
593
+ to: reader2.currentSealerID(),
594
+ nOnceMaterial: {
595
+ in: groupCore.id,
596
+ tx: groupCore.nextTransactionID(),
597
+ },
598
+ });
599
+ groupContent.set(`${readKeyID}_for_${reader2.id}`, revelation3, "trusting");
600
+ groupContent.set("readKey", readKeyID, "trusting");
601
+ expect(groupContent.get("readKey")).toEqual(readKeyID);
602
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey);
603
+ groupContent.set(reader.id, "reader", "trusting");
604
+ expect(groupContent.get(reader.id)).toEqual("reader");
605
+ groupContent.set(reader2.id, "reader", "trusting");
606
+ expect(groupContent.get(reader2.id)).toEqual("reader");
607
+ const childContent = expectMap(childObject.getCurrentContent());
608
+ childContent.set("foo", "bar", "private");
609
+ expect(childContent.get("foo")).toEqual("bar");
610
+ let childObjectAsReader = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
611
+ expect(expectMap(childObjectAsReader.getCurrentContent()).get("foo")).toEqual("bar");
612
+ let childObjectAsReader2 = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
613
+ expect(expectMap(childObjectAsReader2.getCurrentContent()).get("foo")).toEqual("bar");
614
+ const { secret: readKey2, id: readKeyID2 } = Crypto.newRandomKeySecret();
615
+ const newRevelation1 = Crypto.seal({
616
+ message: readKey2,
617
+ from: admin.currentSealerSecret(),
618
+ to: admin.currentSealerID(),
619
+ nOnceMaterial: {
620
+ in: groupCore.id,
621
+ tx: groupCore.nextTransactionID(),
622
+ },
623
+ });
624
+ groupContent.set(`${readKeyID2}_for_${admin.id}`, newRevelation1, "trusting");
625
+ const newRevelation2 = Crypto.seal({
626
+ message: readKey2,
627
+ from: admin.currentSealerSecret(),
628
+ to: reader2.currentSealerID(),
629
+ nOnceMaterial: {
630
+ in: groupCore.id,
631
+ tx: groupCore.nextTransactionID(),
632
+ },
633
+ });
634
+ groupContent.set(`${readKeyID2}_for_${reader2.id}`, newRevelation2, "trusting");
635
+ groupContent.set("readKey", readKeyID2, "trusting");
636
+ expect(groupContent.get("readKey")).toEqual(readKeyID2);
637
+ expect(groupCore.getCurrentReadKey().secret).toEqual(readKey2);
638
+ groupContent.set(reader.id, "revoked", "trusting");
639
+ // expect(editable.get(reader.id)).toEqual("revoked");
640
+ expect(childObject.getCurrentReadKey().secret).toEqual(readKey2);
641
+ childContent.set("foo2", "bar2", "private");
642
+ expect(childContent.get("foo2")).toEqual("bar2");
643
+ // TODO: make sure these instances of coValues sync between each other so this isn't necessary?
644
+ childObjectAsReader = childObject.testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id));
645
+ childObjectAsReader2 = childObject.testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id));
646
+ expect(expectMap(childObjectAsReader.getCurrentContent()).get("foo2")).toBeUndefined();
647
+ expect(expectMap(childObjectAsReader2.getCurrentContent()).get("foo2")).toEqual("bar2");
648
+ });
649
+ 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 (high level)", async () => {
650
+ const { node, group } = newGroupHighLevel();
651
+ const childObject = group.createMap();
652
+ childObject.set("foo", "bar", "private");
653
+ expect(childObject.get("foo")).toEqual("bar");
654
+ expect(childObject.get("foo")).toEqual("bar");
655
+ group.rotateReadKey();
656
+ const secondReadKey = childObject.core.getCurrentReadKey();
657
+ const reader = node.createAccount();
658
+ const reader2 = node.createAccount();
659
+ group.addMember(reader, "reader");
660
+ group.addMember(reader2, "reader");
661
+ childObject.set("foo2", "bar2", "private");
662
+ expect(childObject.get("foo2")).toEqual("bar2");
663
+ await group.removeMember(reader);
664
+ expect(childObject.core.getCurrentReadKey()).not.toEqual(secondReadKey);
665
+ childObject.set("foo3", "bar3", "private");
666
+ expect(childObject.get("foo3")).toEqual("bar3");
667
+ const childContentAsReader2 = expectMap(childObject.core
668
+ .testWithDifferentAccount(reader2, Crypto.newRandomSessionID(reader2.id))
669
+ .getCurrentContent());
670
+ expect(childContentAsReader2.get("foo")).toEqual("bar");
671
+ expect(childContentAsReader2.get("foo2")).toEqual("bar2");
672
+ expect(childContentAsReader2.get("foo3")).toEqual("bar3");
673
+ expect(expectMap(childObject.core
674
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
675
+ .getCurrentContent()).get("foo3")).toBeUndefined();
676
+ });
677
+ test("Can create two owned objects in the same group and they will have different ids", () => {
678
+ const { node, groupCore } = newGroup();
679
+ const childObject1 = node.createCoValue({
680
+ type: "comap",
681
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
682
+ meta: null,
683
+ ...Crypto.createdNowUnique(),
684
+ });
685
+ const childObject2 = node.createCoValue({
686
+ type: "comap",
687
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
688
+ meta: null,
689
+ ...Crypto.createdNowUnique(),
690
+ });
691
+ expect(childObject1.id).not.toEqual(childObject2.id);
692
+ });
693
+ test("Admins can create an adminInvite, which can add an admin", () => {
694
+ const { groupCore, admin } = newGroup();
695
+ const inviteSecret = Crypto.newRandomAgentSecret();
696
+ const inviteID = Crypto.getAgentID(inviteSecret);
697
+ const group = expectGroup(groupCore.getCurrentContent());
698
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
699
+ const revelation = Crypto.seal({
700
+ message: readKey,
701
+ from: admin.currentSealerSecret(),
702
+ to: admin.currentSealerID(),
703
+ nOnceMaterial: {
704
+ in: groupCore.id,
705
+ tx: groupCore.nextTransactionID(),
706
+ },
707
+ });
708
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
709
+ group.set("readKey", readKeyID, "trusting");
710
+ group.set(inviteID, "adminInvite", "trusting");
711
+ expect(group.get(inviteID)).toEqual("adminInvite");
712
+ const revelationForInvite = Crypto.seal({
713
+ message: readKey,
714
+ from: admin.currentSealerSecret(),
715
+ to: Crypto.getAgentSealerID(inviteID),
716
+ nOnceMaterial: {
717
+ in: groupCore.id,
718
+ tx: groupCore.nextTransactionID(),
719
+ },
720
+ });
721
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
722
+ const groupAsInvite = expectGroup(groupCore
723
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
724
+ .getCurrentContent());
725
+ const invitedAdminSecret = Crypto.newRandomAgentSecret();
726
+ const invitedAdminID = Crypto.getAgentID(invitedAdminSecret);
727
+ groupAsInvite.set(invitedAdminID, "admin", "trusting");
728
+ expect(groupAsInvite.get(invitedAdminID)).toEqual("admin");
729
+ const readKeyAsInvite = groupAsInvite.core.getCurrentReadKey();
730
+ expect(readKeyAsInvite.secret).toBeDefined();
731
+ const revelation2 = Crypto.seal({
732
+ message: readKeyAsInvite.secret,
733
+ from: Crypto.getAgentSealerSecret(invitedAdminSecret),
734
+ to: Crypto.getAgentSealerID(invitedAdminID),
735
+ nOnceMaterial: {
736
+ in: groupCore.id,
737
+ tx: groupCore.nextTransactionID(),
738
+ },
739
+ });
740
+ groupAsInvite.set(`${readKeyAsInvite.id}_for_${invitedAdminID}`, revelation2, "trusting");
741
+ expect(groupAsInvite.get(`${readKeyAsInvite.id}_for_${invitedAdminID}`)).toEqual(revelation2);
742
+ });
743
+ test("Admins can create an adminInvite, which can add an admin (high-level)", async () => {
744
+ const { node, group } = newGroupHighLevel();
745
+ const inviteSecret = group.createInvite("admin");
746
+ const invitedAdminSecret = Crypto.newRandomAgentSecret();
747
+ const invitedAdminID = Crypto.getAgentID(invitedAdminSecret);
748
+ const nodeAsInvitedAdmin = node.testWithDifferentAccount(new ControlledAgent(invitedAdminSecret, Crypto), Crypto.newRandomSessionID(invitedAdminID));
749
+ await nodeAsInvitedAdmin.acceptInvite(group.id, inviteSecret);
750
+ const thirdAdmin = Crypto.newRandomAgentSecret();
751
+ const thirdAdminID = Crypto.getAgentID(thirdAdmin);
752
+ const groupAsInvitedAdmin = await nodeAsInvitedAdmin.load(group.id);
753
+ if (groupAsInvitedAdmin === "unavailable") {
754
+ throw new Error("groupAsInvitedAdmin is unavailable");
755
+ }
756
+ expect(groupAsInvitedAdmin.get(invitedAdminID)).toEqual("admin");
757
+ expect(groupAsInvitedAdmin.core.getCurrentReadKey().secret).toBeDefined();
758
+ groupAsInvitedAdmin.addMemberInternal(thirdAdminID, "admin");
759
+ expect(groupAsInvitedAdmin.get(thirdAdminID)).toEqual("admin");
760
+ });
761
+ test("Admins can create a writerInvite, which can add a writer", () => {
762
+ const { groupCore, admin } = newGroup();
763
+ const inviteSecret = Crypto.newRandomAgentSecret();
764
+ const inviteID = Crypto.getAgentID(inviteSecret);
765
+ const group = expectGroup(groupCore.getCurrentContent());
766
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
767
+ const revelation = Crypto.seal({
768
+ message: readKey,
769
+ from: admin.currentSealerSecret(),
770
+ to: admin.currentSealerID(),
771
+ nOnceMaterial: {
772
+ in: groupCore.id,
773
+ tx: groupCore.nextTransactionID(),
774
+ },
775
+ });
776
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
777
+ group.set("readKey", readKeyID, "trusting");
778
+ group.set(inviteID, "writerInvite", "trusting");
779
+ expect(group.get(inviteID)).toEqual("writerInvite");
780
+ const revelationForInvite = Crypto.seal({
781
+ message: readKey,
782
+ from: admin.currentSealerSecret(),
783
+ to: Crypto.getAgentSealerID(inviteID),
784
+ nOnceMaterial: {
785
+ in: groupCore.id,
786
+ tx: groupCore.nextTransactionID(),
787
+ },
788
+ });
789
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
790
+ const groupAsInvite = expectGroup(groupCore
791
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
792
+ .getCurrentContent());
793
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
794
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
795
+ groupAsInvite.set(invitedWriterID, "writer", "trusting");
796
+ expect(groupAsInvite.get(invitedWriterID)).toEqual("writer");
797
+ const readKeyAsInvite = groupAsInvite.core.getCurrentReadKey();
798
+ expect(readKeyAsInvite.secret).toBeDefined();
799
+ const revelation2 = Crypto.seal({
800
+ message: readKeyAsInvite.secret,
801
+ from: Crypto.getAgentSealerSecret(invitedWriterSecret),
802
+ to: Crypto.getAgentSealerID(invitedWriterID),
803
+ nOnceMaterial: {
804
+ in: groupCore.id,
805
+ tx: groupCore.nextTransactionID(),
806
+ },
807
+ });
808
+ groupAsInvite.set(`${readKeyAsInvite.id}_for_${invitedWriterID}`, revelation2, "trusting");
809
+ expect(groupAsInvite.get(`${readKeyAsInvite.id}_for_${invitedWriterID}`)).toEqual(revelation2);
810
+ });
811
+ test("Admins can create a writerInvite, which can add a writer (high-level)", async () => {
812
+ const { node, group } = newGroupHighLevel();
813
+ const inviteSecret = group.createInvite("writer");
814
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
815
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
816
+ const nodeAsInvitedWriter = node.testWithDifferentAccount(new ControlledAgent(invitedWriterSecret, Crypto), Crypto.newRandomSessionID(invitedWriterID));
817
+ await nodeAsInvitedWriter.acceptInvite(group.id, inviteSecret);
818
+ const groupAsInvitedWriter = await nodeAsInvitedWriter.load(group.id);
819
+ if (groupAsInvitedWriter === "unavailable") {
820
+ throw new Error("groupAsInvitedAdmin is unavailable");
821
+ }
822
+ expect(groupAsInvitedWriter.get(invitedWriterID)).toEqual("writer");
823
+ expect(groupAsInvitedWriter.core.getCurrentReadKey().secret).toBeDefined();
824
+ });
825
+ test("Admins can create a readerInvite, which can add a reader", () => {
826
+ const { groupCore, admin } = newGroup();
827
+ const inviteSecret = Crypto.newRandomAgentSecret();
828
+ const inviteID = Crypto.getAgentID(inviteSecret);
829
+ const group = expectGroup(groupCore.getCurrentContent());
830
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
831
+ const revelation = Crypto.seal({
832
+ message: readKey,
833
+ from: admin.currentSealerSecret(),
834
+ to: admin.currentSealerID(),
835
+ nOnceMaterial: {
836
+ in: groupCore.id,
837
+ tx: groupCore.nextTransactionID(),
838
+ },
839
+ });
840
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
841
+ group.set("readKey", readKeyID, "trusting");
842
+ group.set(inviteID, "readerInvite", "trusting");
843
+ expect(group.get(inviteID)).toEqual("readerInvite");
844
+ const revelationForInvite = Crypto.seal({
845
+ message: readKey,
846
+ from: admin.currentSealerSecret(),
847
+ to: Crypto.getAgentSealerID(inviteID),
848
+ nOnceMaterial: {
849
+ in: groupCore.id,
850
+ tx: groupCore.nextTransactionID(),
851
+ },
852
+ });
853
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
854
+ const groupAsInvite = expectGroup(groupCore
855
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
856
+ .getCurrentContent());
857
+ const invitedReaderSecret = Crypto.newRandomAgentSecret();
858
+ const invitedReaderID = Crypto.getAgentID(invitedReaderSecret);
859
+ groupAsInvite.set(invitedReaderID, "reader", "trusting");
860
+ expect(groupAsInvite.get(invitedReaderID)).toEqual("reader");
861
+ const readKeyAsInvite = groupAsInvite.core.getCurrentReadKey();
862
+ expect(readKeyAsInvite.secret).toBeDefined();
863
+ groupAsInvite.set(`${readKeyAsInvite.id}_for_${invitedReaderID}`, revelation, "trusting");
864
+ expect(groupAsInvite.get(`${readKeyAsInvite.id}_for_${invitedReaderID}`)).toEqual(revelation);
865
+ });
866
+ test("Admins can create a readerInvite, which can add a reader (high-level)", async () => {
867
+ const { node, group } = newGroupHighLevel();
868
+ const inviteSecret = group.createInvite("reader");
869
+ const invitedReaderSecret = Crypto.newRandomAgentSecret();
870
+ const invitedReaderID = Crypto.getAgentID(invitedReaderSecret);
871
+ const nodeAsInvitedReader = node.testWithDifferentAccount(new ControlledAgent(invitedReaderSecret, Crypto), Crypto.newRandomSessionID(invitedReaderID));
872
+ await nodeAsInvitedReader.acceptInvite(group.id, inviteSecret);
873
+ const groupAsInvitedReader = await nodeAsInvitedReader.load(group.id);
874
+ if (groupAsInvitedReader === "unavailable") {
875
+ throw new Error("groupAsInvitedAdmin is unavailable");
876
+ }
877
+ expect(groupAsInvitedReader.get(invitedReaderID)).toEqual("reader");
878
+ expect(groupAsInvitedReader.core.getCurrentReadKey().secret).toBeDefined();
879
+ });
880
+ test("WriterInvites can not invite admins", () => {
881
+ const { groupCore, admin } = newGroup();
882
+ const inviteSecret = Crypto.newRandomAgentSecret();
883
+ const inviteID = Crypto.getAgentID(inviteSecret);
884
+ const group = expectGroup(groupCore.getCurrentContent());
885
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
886
+ const revelation = Crypto.seal({
887
+ message: readKey,
888
+ from: admin.currentSealerSecret(),
889
+ to: admin.currentSealerID(),
890
+ nOnceMaterial: {
891
+ in: groupCore.id,
892
+ tx: groupCore.nextTransactionID(),
893
+ },
894
+ });
895
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
896
+ group.set("readKey", readKeyID, "trusting");
897
+ group.set(inviteID, "writerInvite", "trusting");
898
+ expect(group.get(inviteID)).toEqual("writerInvite");
899
+ const revelationForInvite = Crypto.seal({
900
+ message: readKey,
901
+ from: admin.currentSealerSecret(),
902
+ to: Crypto.getAgentSealerID(inviteID),
903
+ nOnceMaterial: {
904
+ in: groupCore.id,
905
+ tx: groupCore.nextTransactionID(),
906
+ },
907
+ });
908
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
909
+ const groupAsInvite = expectGroup(groupCore
910
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
911
+ .getCurrentContent());
912
+ const invitedAdminSecret = Crypto.newRandomAgentSecret();
913
+ const invitedAdminID = Crypto.getAgentID(invitedAdminSecret);
914
+ groupAsInvite.set(invitedAdminID, "admin", "trusting");
915
+ expect(groupAsInvite.get(invitedAdminID)).toBeUndefined();
916
+ });
917
+ test("ReaderInvites can not invite admins", () => {
918
+ const { groupCore, admin } = newGroup();
919
+ const inviteSecret = Crypto.newRandomAgentSecret();
920
+ const inviteID = Crypto.getAgentID(inviteSecret);
921
+ const group = expectGroup(groupCore.getCurrentContent());
922
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
923
+ const revelation = Crypto.seal({
924
+ message: readKey,
925
+ from: admin.currentSealerSecret(),
926
+ to: admin.currentSealerID(),
927
+ nOnceMaterial: {
928
+ in: groupCore.id,
929
+ tx: groupCore.nextTransactionID(),
930
+ },
931
+ });
932
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
933
+ group.set("readKey", readKeyID, "trusting");
934
+ group.set(inviteID, "readerInvite", "trusting");
935
+ expect(group.get(inviteID)).toEqual("readerInvite");
936
+ const revelationForInvite = Crypto.seal({
937
+ message: readKey,
938
+ from: admin.currentSealerSecret(),
939
+ to: Crypto.getAgentSealerID(inviteID),
940
+ nOnceMaterial: {
941
+ in: groupCore.id,
942
+ tx: groupCore.nextTransactionID(),
943
+ },
944
+ });
945
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
946
+ const groupAsInvite = expectGroup(groupCore
947
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
948
+ .getCurrentContent());
949
+ const invitedAdminSecret = Crypto.newRandomAgentSecret();
950
+ const invitedAdminID = Crypto.getAgentID(invitedAdminSecret);
951
+ groupAsInvite.set(invitedAdminID, "admin", "trusting");
952
+ expect(groupAsInvite.get(invitedAdminID)).toBeUndefined();
953
+ });
954
+ test("ReaderInvites can not invite writers", () => {
955
+ const { groupCore, admin } = newGroup();
956
+ const inviteSecret = Crypto.newRandomAgentSecret();
957
+ const inviteID = Crypto.getAgentID(inviteSecret);
958
+ const group = expectGroup(groupCore.getCurrentContent());
959
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
960
+ const revelation = Crypto.seal({
961
+ message: readKey,
962
+ from: admin.currentSealerSecret(),
963
+ to: admin.currentSealerID(),
964
+ nOnceMaterial: {
965
+ in: groupCore.id,
966
+ tx: groupCore.nextTransactionID(),
967
+ },
968
+ });
969
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
970
+ group.set("readKey", readKeyID, "trusting");
971
+ group.set(inviteID, "readerInvite", "trusting");
972
+ expect(group.get(inviteID)).toEqual("readerInvite");
973
+ const revelationForInvite = Crypto.seal({
974
+ message: readKey,
975
+ from: admin.currentSealerSecret(),
976
+ to: Crypto.getAgentSealerID(inviteID),
977
+ nOnceMaterial: {
978
+ in: groupCore.id,
979
+ tx: groupCore.nextTransactionID(),
980
+ },
981
+ });
982
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
983
+ const groupAsInvite = expectGroup(groupCore
984
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
985
+ .getCurrentContent());
986
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
987
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
988
+ groupAsInvite.set(invitedWriterID, "writer", "trusting");
989
+ expect(groupAsInvite.get(invitedWriterID)).toBeUndefined();
990
+ });
991
+ test("WriteOnlyInvites can not invite writers", () => {
992
+ const { groupCore, admin } = newGroup();
993
+ const inviteSecret = Crypto.newRandomAgentSecret();
994
+ const inviteID = Crypto.getAgentID(inviteSecret);
995
+ const group = expectGroup(groupCore.getCurrentContent());
996
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
997
+ const revelation = Crypto.seal({
998
+ message: readKey,
999
+ from: admin.currentSealerSecret(),
1000
+ to: admin.currentSealerID(),
1001
+ nOnceMaterial: {
1002
+ in: groupCore.id,
1003
+ tx: groupCore.nextTransactionID(),
1004
+ },
1005
+ });
1006
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1007
+ group.set("readKey", readKeyID, "trusting");
1008
+ group.set(inviteID, "writeOnlyInvite", "trusting");
1009
+ expect(group.get(inviteID)).toEqual("writeOnlyInvite");
1010
+ const revelationForInvite = Crypto.seal({
1011
+ message: readKey,
1012
+ from: admin.currentSealerSecret(),
1013
+ to: Crypto.getAgentSealerID(inviteID),
1014
+ nOnceMaterial: {
1015
+ in: groupCore.id,
1016
+ tx: groupCore.nextTransactionID(),
1017
+ },
1018
+ });
1019
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1020
+ const groupAsInvite = expectGroup(groupCore
1021
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1022
+ .getCurrentContent());
1023
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
1024
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
1025
+ groupAsInvite.set(invitedWriterID, "writer", "trusting");
1026
+ expect(groupAsInvite.get(invitedWriterID)).toBeUndefined();
1027
+ });
1028
+ test("WriteOnlyInvites can not invite admins", () => {
1029
+ const { groupCore, admin } = newGroup();
1030
+ const inviteSecret = Crypto.newRandomAgentSecret();
1031
+ const inviteID = Crypto.getAgentID(inviteSecret);
1032
+ const group = expectGroup(groupCore.getCurrentContent());
1033
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1034
+ const revelation = Crypto.seal({
1035
+ message: readKey,
1036
+ from: admin.currentSealerSecret(),
1037
+ to: admin.currentSealerID(),
1038
+ nOnceMaterial: {
1039
+ in: groupCore.id,
1040
+ tx: groupCore.nextTransactionID(),
1041
+ },
1042
+ });
1043
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1044
+ group.set("readKey", readKeyID, "trusting");
1045
+ group.set(inviteID, "writeOnlyInvite", "trusting");
1046
+ expect(group.get(inviteID)).toEqual("writeOnlyInvite");
1047
+ const revelationForInvite = Crypto.seal({
1048
+ message: readKey,
1049
+ from: admin.currentSealerSecret(),
1050
+ to: Crypto.getAgentSealerID(inviteID),
1051
+ nOnceMaterial: {
1052
+ in: groupCore.id,
1053
+ tx: groupCore.nextTransactionID(),
1054
+ },
1055
+ });
1056
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1057
+ const groupAsInvite = expectGroup(groupCore
1058
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1059
+ .getCurrentContent());
1060
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
1061
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
1062
+ groupAsInvite.set(invitedWriterID, "admin", "trusting");
1063
+ expect(groupAsInvite.get(invitedWriterID)).toBeUndefined();
1064
+ });
1065
+ test("WriteOnlyInvites can invite writeOnly", () => {
1066
+ const { groupCore, admin } = newGroup();
1067
+ const inviteSecret = Crypto.newRandomAgentSecret();
1068
+ const inviteID = Crypto.getAgentID(inviteSecret);
1069
+ const group = expectGroup(groupCore.getCurrentContent());
1070
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1071
+ const revelation = Crypto.seal({
1072
+ message: readKey,
1073
+ from: admin.currentSealerSecret(),
1074
+ to: admin.currentSealerID(),
1075
+ nOnceMaterial: {
1076
+ in: groupCore.id,
1077
+ tx: groupCore.nextTransactionID(),
1078
+ },
1079
+ });
1080
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1081
+ group.set("readKey", readKeyID, "trusting");
1082
+ group.set(inviteID, "writeOnlyInvite", "trusting");
1083
+ expect(group.get(inviteID)).toEqual("writeOnlyInvite");
1084
+ const revelationForInvite = Crypto.seal({
1085
+ message: readKey,
1086
+ from: admin.currentSealerSecret(),
1087
+ to: Crypto.getAgentSealerID(inviteID),
1088
+ nOnceMaterial: {
1089
+ in: groupCore.id,
1090
+ tx: groupCore.nextTransactionID(),
1091
+ },
1092
+ });
1093
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1094
+ const groupAsInvite = expectGroup(groupCore
1095
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1096
+ .getCurrentContent());
1097
+ const invitedWriterSecret = Crypto.newRandomAgentSecret();
1098
+ const invitedWriterID = Crypto.getAgentID(invitedWriterSecret);
1099
+ groupAsInvite.set(invitedWriterID, "writeOnly", "trusting");
1100
+ expect(groupAsInvite.get(invitedWriterID)).toEqual("writeOnly");
1101
+ });
1102
+ test("WriteOnlyInvites can set writeKeys", () => {
1103
+ const { groupCore, admin } = newGroup();
1104
+ const inviteSecret = Crypto.newRandomAgentSecret();
1105
+ const inviteID = Crypto.getAgentID(inviteSecret);
1106
+ const group = expectGroup(groupCore.getCurrentContent());
1107
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1108
+ const revelation = Crypto.seal({
1109
+ message: readKey,
1110
+ from: admin.currentSealerSecret(),
1111
+ to: admin.currentSealerID(),
1112
+ nOnceMaterial: {
1113
+ in: groupCore.id,
1114
+ tx: groupCore.nextTransactionID(),
1115
+ },
1116
+ });
1117
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1118
+ group.set("readKey", readKeyID, "trusting");
1119
+ group.set(inviteID, "writeOnlyInvite", "trusting");
1120
+ expect(group.get(inviteID)).toEqual("writeOnlyInvite");
1121
+ const revelationForInvite = Crypto.seal({
1122
+ message: readKey,
1123
+ from: admin.currentSealerSecret(),
1124
+ to: Crypto.getAgentSealerID(inviteID),
1125
+ nOnceMaterial: {
1126
+ in: groupCore.id,
1127
+ tx: groupCore.nextTransactionID(),
1128
+ },
1129
+ });
1130
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1131
+ const groupAsInvite = expectGroup(groupCore
1132
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1133
+ .getCurrentContent());
1134
+ groupAsInvite.set(`writeKeyFor_${admin.id}`, readKeyID, "trusting");
1135
+ expect(groupAsInvite.get(`writeKeyFor_${admin.id}`)).toEqual(readKeyID);
1136
+ });
1137
+ test("Invites can't override key revelations", () => {
1138
+ const { groupCore, admin } = newGroup();
1139
+ const inviteSecret = Crypto.newRandomAgentSecret();
1140
+ const inviteID = Crypto.getAgentID(inviteSecret);
1141
+ const group = expectGroup(groupCore.getCurrentContent());
1142
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1143
+ const revelation = Crypto.seal({
1144
+ message: readKey,
1145
+ from: admin.currentSealerSecret(),
1146
+ to: admin.currentSealerID(),
1147
+ nOnceMaterial: {
1148
+ in: groupCore.id,
1149
+ tx: groupCore.nextTransactionID(),
1150
+ },
1151
+ });
1152
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1153
+ group.set("readKey", readKeyID, "trusting");
1154
+ group.set(inviteID, "readerInvite", "trusting");
1155
+ expect(group.get(inviteID)).toEqual("readerInvite");
1156
+ const revelationForInvite = Crypto.seal({
1157
+ message: readKey,
1158
+ from: admin.currentSealerSecret(),
1159
+ to: Crypto.getAgentSealerID(inviteID),
1160
+ nOnceMaterial: {
1161
+ in: groupCore.id,
1162
+ tx: groupCore.nextTransactionID(),
1163
+ },
1164
+ });
1165
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1166
+ const groupAsInvite = expectGroup(groupCore
1167
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1168
+ .getCurrentContent());
1169
+ groupAsInvite.set(`${readKeyID}_for_${admin.id}`, "Evil change", "trusting");
1170
+ expect(groupAsInvite.get(`${readKeyID}_for_${admin.id}`)).toBe(revelation);
1171
+ });
1172
+ test("WriteOnlyInvites can't override writeKeys", () => {
1173
+ const { groupCore, admin } = newGroup();
1174
+ const inviteSecret = Crypto.newRandomAgentSecret();
1175
+ const inviteID = Crypto.getAgentID(inviteSecret);
1176
+ const group = expectGroup(groupCore.getCurrentContent());
1177
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1178
+ const revelation = Crypto.seal({
1179
+ message: readKey,
1180
+ from: admin.currentSealerSecret(),
1181
+ to: admin.currentSealerID(),
1182
+ nOnceMaterial: {
1183
+ in: groupCore.id,
1184
+ tx: groupCore.nextTransactionID(),
1185
+ },
1186
+ });
1187
+ group.set(`${readKeyID}_for_${admin.id}`, revelation, "trusting");
1188
+ group.set("readKey", readKeyID, "trusting");
1189
+ group.set(inviteID, "writeOnlyInvite", "trusting");
1190
+ expect(group.get(inviteID)).toEqual("writeOnlyInvite");
1191
+ const revelationForInvite = Crypto.seal({
1192
+ message: readKey,
1193
+ from: admin.currentSealerSecret(),
1194
+ to: Crypto.getAgentSealerID(inviteID),
1195
+ nOnceMaterial: {
1196
+ in: groupCore.id,
1197
+ tx: groupCore.nextTransactionID(),
1198
+ },
1199
+ });
1200
+ group.set(`${readKeyID}_for_${inviteID}`, revelationForInvite, "trusting");
1201
+ const groupAsInvite = expectGroup(groupCore
1202
+ .testWithDifferentAccount(new ControlledAgent(inviteSecret, Crypto), Crypto.newRandomSessionID(inviteID))
1203
+ .getCurrentContent());
1204
+ groupAsInvite.set(`writeKeyFor_${admin.id}`, readKeyID, "trusting");
1205
+ groupAsInvite.set(`writeKeyFor_${admin.id}`, "Evil change", "trusting");
1206
+ expect(groupAsInvite.get(`writeKeyFor_${admin.id}`)).toEqual(readKeyID);
1207
+ });
1208
+ test("Can give read permission to 'everyone'", () => {
1209
+ const { node, groupCore } = newGroup();
1210
+ const childObject = node.createCoValue({
1211
+ type: "comap",
1212
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
1213
+ meta: null,
1214
+ ...Crypto.createdNowUnique(),
1215
+ });
1216
+ const group = expectGroup(groupCore.getCurrentContent());
1217
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1218
+ group.set("everyone", "reader", "trusting");
1219
+ group.set("readKey", readKeyID, "trusting");
1220
+ group.set(`${readKeyID}_for_everyone`, readKey, "trusting");
1221
+ const childContent = expectMap(childObject.getCurrentContent());
1222
+ expect(childContent.get("foo")).toBeUndefined();
1223
+ childContent.set("foo", "bar", "private");
1224
+ expect(childContent.get("foo")).toEqual("bar");
1225
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
1226
+ const childContent2 = expectMap(childObject
1227
+ .testWithDifferentAccount(newAccount, Crypto.newRandomSessionID(newAccount.currentAgentID()))
1228
+ .getCurrentContent());
1229
+ expect(childContent2.get("foo")).toEqual("bar");
1230
+ });
1231
+ test("Can give read permissions to 'everyone' (high-level)", async () => {
1232
+ const { group } = newGroupHighLevel();
1233
+ const childObject = group.createMap();
1234
+ expect(childObject.get("foo")).toBeUndefined();
1235
+ group.addMember("everyone", "reader");
1236
+ childObject.set("foo", "bar", "private");
1237
+ expect(childObject.get("foo")).toEqual("bar");
1238
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
1239
+ const childContent2 = expectMap(childObject.core
1240
+ .testWithDifferentAccount(new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto), Crypto.newRandomSessionID(newAccount.currentAgentID()))
1241
+ .getCurrentContent());
1242
+ expect(childContent2.get("foo")).toEqual("bar");
1243
+ });
1244
+ test("Can give write permission to 'everyone'", async () => {
1245
+ const { node, groupCore } = newGroup();
1246
+ const childObject = node.createCoValue({
1247
+ type: "comap",
1248
+ ruleset: { type: "ownedByGroup", group: groupCore.id },
1249
+ meta: null,
1250
+ ...Crypto.createdNowUnique(),
1251
+ });
1252
+ const group = expectGroup(groupCore.getCurrentContent());
1253
+ const { secret: readKey, id: readKeyID } = Crypto.newRandomKeySecret();
1254
+ group.set("everyone", "writer", "trusting");
1255
+ group.set("readKey", readKeyID, "trusting");
1256
+ group.set(`${readKeyID}_for_everyone`, readKey, "trusting");
1257
+ const childContent = expectMap(childObject.getCurrentContent());
1258
+ expect(childContent.get("foo")).toBeUndefined();
1259
+ childContent.set("foo", "bar", "private");
1260
+ expect(childContent.get("foo")).toEqual("bar");
1261
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
1262
+ const childContent2 = expectMap(childObject
1263
+ .testWithDifferentAccount(newAccount, Crypto.newRandomSessionID(newAccount.currentAgentID()))
1264
+ .getCurrentContent());
1265
+ // TODO: resolve race condition
1266
+ await new Promise((resolve) => setTimeout(resolve, 50));
1267
+ expect(childContent2.get("foo")).toEqual("bar");
1268
+ childContent2.set("foo", "bar2", "private");
1269
+ expect(childContent2.get("foo")).toEqual("bar2");
1270
+ });
1271
+ test("Can give write permissions to 'everyone' (high-level)", async () => {
1272
+ const { group } = newGroupHighLevel();
1273
+ const childObject = group.createMap();
1274
+ expect(childObject.get("foo")).toBeUndefined();
1275
+ group.addMember("everyone", "writer");
1276
+ childObject.set("foo", "bar", "private");
1277
+ expect(childObject.get("foo")).toEqual("bar");
1278
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
1279
+ const childContent2 = expectMap(childObject.core
1280
+ .testWithDifferentAccount(newAccount, Crypto.newRandomSessionID(newAccount.currentAgentID()))
1281
+ .getCurrentContent());
1282
+ connectTwoPeers(group.core.node, childContent2.core.node, "server", "server");
1283
+ // Ensure that the group is available to newAccount
1284
+ await group.core.waitForSync();
1285
+ expect(childContent2.get("foo")).toEqual("bar");
1286
+ childContent2.set("foo", "bar2", "private");
1287
+ expect(childContent2.get("foo")).toEqual("bar2");
1288
+ });
1289
+ test("Admins can set parent extensions", () => {
1290
+ const { group, node } = newGroupHighLevel();
1291
+ const parentGroup = node.createGroup();
1292
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1293
+ expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
1294
+ });
1295
+ test("Writers, readers and invitees can not set parent extensions", () => {
1296
+ const { group, node } = newGroupHighLevel();
1297
+ const parentGroup = node.createGroup();
1298
+ const writer = node.createAccount();
1299
+ const reader = node.createAccount();
1300
+ const adminInvite = node.createAccount();
1301
+ const writerInvite = node.createAccount();
1302
+ const readerInvite = node.createAccount();
1303
+ group.addMember(writer, "writer");
1304
+ group.addMember(reader, "reader");
1305
+ group.addMember(adminInvite, "adminInvite");
1306
+ group.addMember(writerInvite, "writerInvite");
1307
+ group.addMember(readerInvite, "readerInvite");
1308
+ const groupAsWriter = expectGroup(group.core
1309
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
1310
+ .getCurrentContent());
1311
+ groupAsWriter.set(`parent_${parentGroup.id}`, "extend", "trusting");
1312
+ expect(groupAsWriter.get(`parent_${parentGroup.id}`)).toBeUndefined();
1313
+ const groupAsReader = expectGroup(group.core
1314
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1315
+ .getCurrentContent());
1316
+ groupAsReader.set(`parent_${parentGroup.id}`, "extend", "trusting");
1317
+ expect(groupAsReader.get(`parent_${parentGroup.id}`)).toBeUndefined();
1318
+ const groupAsAdminInvite = expectGroup(group.core
1319
+ .testWithDifferentAccount(adminInvite, Crypto.newRandomSessionID(adminInvite.currentAgentID()))
1320
+ .getCurrentContent());
1321
+ groupAsAdminInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
1322
+ expect(groupAsAdminInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
1323
+ const groupAsWriterInvite = expectGroup(group.core
1324
+ .testWithDifferentAccount(writerInvite, Crypto.newRandomSessionID(writerInvite.currentAgentID()))
1325
+ .getCurrentContent());
1326
+ groupAsWriterInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
1327
+ expect(groupAsWriterInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
1328
+ const groupAsReaderInvite = expectGroup(group.core
1329
+ .testWithDifferentAccount(readerInvite, Crypto.newRandomSessionID(readerInvite.currentAgentID()))
1330
+ .getCurrentContent());
1331
+ groupAsReaderInvite.set(`parent_${parentGroup.id}`, "extend", "trusting");
1332
+ expect(groupAsReaderInvite.get(`parent_${parentGroup.id}`)).toBeUndefined();
1333
+ });
1334
+ test("Admins can set child extensions", () => {
1335
+ const { group, node } = newGroupHighLevel();
1336
+ const childGroup = node.createGroup();
1337
+ group.set(`child_${childGroup.id}`, "extend", "trusting");
1338
+ expect(group.get(`child_${childGroup.id}`)).toEqual("extend");
1339
+ });
1340
+ test("Admins can set child extensions when the admin role is inherited", async () => {
1341
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1342
+ const node2AccountOnNode1 = await loadCoValueOrFail(node1.node, node2.accountID);
1343
+ const group = node1.node.createGroup();
1344
+ group.addMember(node2AccountOnNode1, "admin");
1345
+ const groupOnNode2 = await loadCoValueOrFail(node2.node, group.id);
1346
+ const childGroup = node2.node.createGroup();
1347
+ childGroup.extend(groupOnNode2);
1348
+ const childGroupOnNode1 = await loadCoValueOrFail(node1.node, childGroup.id);
1349
+ const grandChildGroup = node2.node.createGroup();
1350
+ grandChildGroup.extend(childGroupOnNode1);
1351
+ expect(childGroupOnNode1.get(`child_${grandChildGroup.id}`)).toEqual("extend");
1352
+ expect(grandChildGroup.get(`parent_${childGroupOnNode1.id}`)).toEqual("extend");
1353
+ });
1354
+ test("Writers, readers and writeOnly can set child extensions", () => {
1355
+ const { group, node } = newGroupHighLevel();
1356
+ const childGroup = node.createGroup();
1357
+ const writer = node.createAccount();
1358
+ const reader = node.createAccount();
1359
+ const writeOnly = node.createAccount();
1360
+ group.addMember(writer, "writer");
1361
+ group.addMember(reader, "reader");
1362
+ group.addMember(writeOnly, "writeOnly");
1363
+ const groupAsWriter = expectGroup(group.core
1364
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
1365
+ .getCurrentContent());
1366
+ groupAsWriter.set(`child_${childGroup.id}`, "extend", "trusting");
1367
+ expect(groupAsWriter.get(`child_${childGroup.id}`)).toEqual("extend");
1368
+ const groupAsReader = expectGroup(group.core
1369
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1370
+ .getCurrentContent());
1371
+ groupAsReader.set(`child_${childGroup.id}`, "extend", "trusting");
1372
+ expect(groupAsReader.get(`child_${childGroup.id}`)).toEqual("extend");
1373
+ });
1374
+ test("Invitees can not set child extensions", () => {
1375
+ const { group, node } = newGroupHighLevel();
1376
+ const childGroup = node.createGroup();
1377
+ const adminInvite = node.createAccount();
1378
+ const writerInvite = node.createAccount();
1379
+ const readerInvite = node.createAccount();
1380
+ group.addMember(adminInvite, "adminInvite");
1381
+ group.addMember(writerInvite, "writerInvite");
1382
+ group.addMember(readerInvite, "readerInvite");
1383
+ const groupAsAdminInvite = expectGroup(group.core
1384
+ .testWithDifferentAccount(adminInvite, Crypto.newRandomSessionID(adminInvite.currentAgentID()))
1385
+ .getCurrentContent());
1386
+ groupAsAdminInvite.set(`child_${childGroup.id}`, "extend", "trusting");
1387
+ expect(groupAsAdminInvite.get(`child_${childGroup.id}`)).toBeUndefined();
1388
+ const groupAsWriterInvite = expectGroup(group.core
1389
+ .testWithDifferentAccount(writerInvite, Crypto.newRandomSessionID(writerInvite.currentAgentID()))
1390
+ .getCurrentContent());
1391
+ groupAsWriterInvite.set(`child_${childGroup.id}`, "extend", "trusting");
1392
+ expect(groupAsWriterInvite.get(`child_${childGroup.id}`)).toBeUndefined();
1393
+ const groupAsReaderInvite = expectGroup(group.core
1394
+ .testWithDifferentAccount(readerInvite, Crypto.newRandomSessionID(readerInvite.currentAgentID()))
1395
+ .getCurrentContent());
1396
+ groupAsReaderInvite.set(`child_${childGroup.id}`, "extend", "trusting");
1397
+ expect(groupAsReaderInvite.get(`child_${childGroup.id}`)).toBeUndefined();
1398
+ });
1399
+ test("Member roles are inherited by child groups (except invites)", () => {
1400
+ const { group, node, admin } = newGroupHighLevel();
1401
+ const parentGroup = node.createGroup();
1402
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1403
+ const writer = node.createAccount();
1404
+ const reader = node.createAccount();
1405
+ const adminInvite = node.createAccount();
1406
+ const writerInvite = node.createAccount();
1407
+ const readerInvite = node.createAccount();
1408
+ parentGroup.addMember(writer, "writer");
1409
+ parentGroup.addMember(reader, "reader");
1410
+ parentGroup.addMember(adminInvite, "adminInvite");
1411
+ parentGroup.addMember(writerInvite, "writerInvite");
1412
+ parentGroup.addMember(readerInvite, "readerInvite");
1413
+ expect(group.roleOfInternal(admin.id)).toEqual({
1414
+ role: "admin",
1415
+ via: undefined,
1416
+ });
1417
+ expect(group.roleOfInternal(writer.id)).toEqual({
1418
+ role: "writer",
1419
+ via: parentGroup.id,
1420
+ });
1421
+ expect(group.roleOf(writer.id)).toEqual("writer");
1422
+ expect(group.roleOfInternal(reader.id)).toEqual({
1423
+ role: "reader",
1424
+ via: parentGroup.id,
1425
+ });
1426
+ expect(group.roleOf(reader.id)).toEqual("reader");
1427
+ expect(group.roleOfInternal(adminInvite.id)).toEqual(undefined);
1428
+ expect(group.roleOf(adminInvite.id)).toEqual(undefined);
1429
+ expect(group.roleOfInternal(writerInvite.id)).toEqual(undefined);
1430
+ expect(group.roleOf(writerInvite.id)).toEqual(undefined);
1431
+ expect(group.roleOfInternal(readerInvite.id)).toEqual(undefined);
1432
+ expect(group.roleOf(readerInvite.id)).toEqual(undefined);
1433
+ });
1434
+ test("Member roles are inherited by grand-children groups (except invites)", () => {
1435
+ const { group, node, admin } = newGroupHighLevel();
1436
+ const parentGroup = node.createGroup();
1437
+ const grandParentGroup = node.createGroup();
1438
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1439
+ parentGroup.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
1440
+ const writer = node.createAccount();
1441
+ const reader = node.createAccount();
1442
+ const adminInvite = node.createAccount();
1443
+ const writerInvite = node.createAccount();
1444
+ const readerInvite = node.createAccount();
1445
+ grandParentGroup.addMember(writer, "writer");
1446
+ grandParentGroup.addMember(reader, "reader");
1447
+ grandParentGroup.addMember(adminInvite, "adminInvite");
1448
+ grandParentGroup.addMember(writerInvite, "writerInvite");
1449
+ grandParentGroup.addMember(readerInvite, "readerInvite");
1450
+ expect(group.roleOfInternal(admin.id)).toEqual({
1451
+ role: "admin",
1452
+ via: undefined,
1453
+ });
1454
+ expect(group.roleOfInternal(writer.id)).toEqual({
1455
+ role: "writer",
1456
+ via: parentGroup.id,
1457
+ });
1458
+ expect(group.roleOf(writer.id)).toEqual("writer");
1459
+ expect(group.roleOfInternal(reader.id)).toEqual({
1460
+ role: "reader",
1461
+ via: parentGroup.id,
1462
+ });
1463
+ expect(group.roleOf(reader.id)).toEqual("reader");
1464
+ expect(group.roleOfInternal(adminInvite.id)).toEqual(undefined);
1465
+ expect(group.roleOf(adminInvite.id)).toEqual(undefined);
1466
+ expect(group.roleOfInternal(writerInvite.id)).toEqual(undefined);
1467
+ expect(group.roleOf(writerInvite.id)).toEqual(undefined);
1468
+ expect(group.roleOfInternal(readerInvite.id)).toEqual(undefined);
1469
+ expect(group.roleOf(readerInvite.id)).toEqual(undefined);
1470
+ });
1471
+ test("Admins can reveal parent read keys to child groups", () => {
1472
+ const { group, node } = newGroupHighLevel();
1473
+ const parentGroup = node.createGroup();
1474
+ const parentReadKeyID = parentGroup.get("readKey");
1475
+ if (!parentReadKeyID) {
1476
+ throw new Error("Can't get parent group read key");
1477
+ }
1478
+ const readKeyID = group.get("readKey");
1479
+ if (!readKeyID) {
1480
+ throw new Error("Can't get group read key");
1481
+ }
1482
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1483
+ const encrypted = "fake_encrypted_key_secret";
1484
+ group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1485
+ expect(group.get(`${readKeyID}_for_${parentReadKeyID}`)).toEqual(encrypted);
1486
+ });
1487
+ test("Writers, readers and invites can't reveal parent read keys to child groups", () => {
1488
+ const { group, node } = newGroupHighLevel();
1489
+ const parentGroup = node.createGroup();
1490
+ const parentReadKeyID = parentGroup.get("readKey");
1491
+ if (!parentReadKeyID) {
1492
+ throw new Error("Can't get parent group read key");
1493
+ }
1494
+ const readKeyID = group.get("readKey");
1495
+ if (!readKeyID) {
1496
+ throw new Error("Can't get group read key");
1497
+ }
1498
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1499
+ const encrypted = "fake_encrypted_key_secret";
1500
+ const writer = node.createAccount();
1501
+ const reader = node.createAccount();
1502
+ const adminInvite = node.createAccount();
1503
+ const writerInvite = node.createAccount();
1504
+ const readerInvite = node.createAccount();
1505
+ group.addMember(writer, "writer");
1506
+ group.addMember(reader, "reader");
1507
+ group.addMember(adminInvite, "adminInvite");
1508
+ group.addMember(writerInvite, "writerInvite");
1509
+ group.addMember(readerInvite, "readerInvite");
1510
+ const groupAsWriter = expectGroup(group.core
1511
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
1512
+ .getCurrentContent());
1513
+ groupAsWriter.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1514
+ expect(groupAsWriter.get(`${readKeyID}_for_${parentReadKeyID}`)).toBeUndefined();
1515
+ const groupAsReader = expectGroup(group.core
1516
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1517
+ .getCurrentContent());
1518
+ groupAsReader.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1519
+ expect(groupAsReader.get(`${readKeyID}_for_${parentReadKeyID}`)).toBeUndefined();
1520
+ const groupAsAdminInvite = expectGroup(group.core
1521
+ .testWithDifferentAccount(adminInvite, Crypto.newRandomSessionID(adminInvite.currentAgentID()))
1522
+ .getCurrentContent());
1523
+ groupAsAdminInvite.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1524
+ expect(groupAsAdminInvite.get(`${readKeyID}_for_${parentReadKeyID}`)).toBeUndefined();
1525
+ const groupAsWriterInvite = expectGroup(group.core
1526
+ .testWithDifferentAccount(writerInvite, Crypto.newRandomSessionID(writerInvite.currentAgentID()))
1527
+ .getCurrentContent());
1528
+ groupAsWriterInvite.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1529
+ expect(groupAsWriterInvite.get(`${readKeyID}_for_${parentReadKeyID}`)).toBeUndefined();
1530
+ const groupAsReaderInvite = expectGroup(group.core
1531
+ .testWithDifferentAccount(readerInvite, Crypto.newRandomSessionID(readerInvite.currentAgentID()))
1532
+ .getCurrentContent());
1533
+ groupAsReaderInvite.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1534
+ expect(groupAsReaderInvite.get(`${readKeyID}_for_${parentReadKeyID}`)).toBeUndefined();
1535
+ });
1536
+ test("Writers and readers in a parent group can read from an object owned by a child group", () => {
1537
+ const { group, node } = newGroupHighLevel();
1538
+ const parentGroup = node.createGroup();
1539
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1540
+ const parentReadKeyID = parentGroup.get("readKey");
1541
+ const parentKey = parentReadKeyID && parentGroup.core.getReadKey(parentReadKeyID);
1542
+ if (!parentReadKeyID || !parentKey) {
1543
+ throw new Error("Can't get parent group read key");
1544
+ }
1545
+ const readKeyID = group.get("readKey");
1546
+ const readKey = readKeyID && group.core.getReadKey(readKeyID);
1547
+ if (!readKeyID || !readKey) {
1548
+ throw new Error("Can't get group read key");
1549
+ }
1550
+ const encrypted = node.crypto.encryptKeySecret({
1551
+ toEncrypt: {
1552
+ id: readKeyID,
1553
+ secret: readKey,
1554
+ },
1555
+ encrypting: {
1556
+ id: parentReadKeyID,
1557
+ secret: parentKey,
1558
+ },
1559
+ }).encrypted;
1560
+ group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1561
+ const writer = node.createAccount();
1562
+ const reader = node.createAccount();
1563
+ parentGroup.addMember(writer, "writer");
1564
+ parentGroup.addMember(reader, "reader");
1565
+ const childObject = node.createCoValue({
1566
+ type: "comap",
1567
+ ruleset: { type: "ownedByGroup", group: group.id },
1568
+ meta: null,
1569
+ ...Crypto.createdNowUnique(),
1570
+ });
1571
+ const childContent = expectMap(childObject.getCurrentContent());
1572
+ childContent.set("foo", "bar", "private");
1573
+ expect(childContent.get("foo")).toEqual("bar");
1574
+ const childContentAsWriter = expectMap(childObject
1575
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
1576
+ .getCurrentContent());
1577
+ expect(childContentAsWriter.get("foo")).toEqual("bar");
1578
+ const childContentAsReader = expectMap(childObject
1579
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1580
+ .getCurrentContent());
1581
+ expect(childContentAsReader.get("foo")).toEqual("bar");
1582
+ });
1583
+ test("Writers in a parent group can write to an object owned by a child group", () => {
1584
+ const { group, node } = newGroupHighLevel();
1585
+ const parentGroup = node.createGroup();
1586
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1587
+ const parentReadKeyID = parentGroup.get("readKey");
1588
+ const parentKey = parentReadKeyID && parentGroup.core.getReadKey(parentReadKeyID);
1589
+ if (!parentReadKeyID || !parentKey) {
1590
+ throw new Error("Can't get parent group read key");
1591
+ }
1592
+ const readKeyID = group.get("readKey");
1593
+ const readKey = readKeyID && group.core.getReadKey(readKeyID);
1594
+ if (!readKeyID || !readKey) {
1595
+ throw new Error("Can't get group read key");
1596
+ }
1597
+ const encrypted = node.crypto.encryptKeySecret({
1598
+ toEncrypt: {
1599
+ id: readKeyID,
1600
+ secret: readKey,
1601
+ },
1602
+ encrypting: {
1603
+ id: parentReadKeyID,
1604
+ secret: parentKey,
1605
+ },
1606
+ }).encrypted;
1607
+ group.set(`${readKeyID}_for_${parentReadKeyID}`, encrypted, "trusting");
1608
+ const writer = node.createAccount();
1609
+ parentGroup.addMember(writer, "writer");
1610
+ const childObject = node.createCoValue({
1611
+ type: "comap",
1612
+ ruleset: { type: "ownedByGroup", group: group.id },
1613
+ meta: null,
1614
+ ...Crypto.createdNowUnique(),
1615
+ });
1616
+ const childContentAsWriter = expectMap(childObject
1617
+ .testWithDifferentAccount(writer, Crypto.newRandomSessionID(writer.id))
1618
+ .getCurrentContent());
1619
+ childContentAsWriter.set("foo", "bar", "private");
1620
+ expect(childContentAsWriter.get("foo")).toEqual("bar");
1621
+ });
1622
+ test("When rotating the key of a child group, the new child key is exposed to the parent group", () => {
1623
+ const { group, node } = newGroupHighLevel();
1624
+ const parentGroup = node.createGroup();
1625
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1626
+ const currentReadKeyID = group.get("readKey");
1627
+ if (!currentReadKeyID) {
1628
+ throw new Error("Can't get group read key");
1629
+ }
1630
+ group.rotateReadKey();
1631
+ const newReadKeyID = group.get("readKey");
1632
+ if (!newReadKeyID) {
1633
+ throw new Error("Can't get new group read key");
1634
+ }
1635
+ expect(newReadKeyID).not.toEqual(currentReadKeyID);
1636
+ const parentReadKeyID = parentGroup.get("readKey");
1637
+ if (!parentReadKeyID) {
1638
+ throw new Error("Can't get parent group read key");
1639
+ }
1640
+ console.log("Checking", `${newReadKeyID}_for_${parentReadKeyID}`);
1641
+ expect(group.get(`${newReadKeyID}_for_${parentReadKeyID}`)).toBeDefined();
1642
+ });
1643
+ test("When rotating the key of a parent group, the keys of all child groups are also rotated", () => {
1644
+ const { group, node } = newGroupHighLevel();
1645
+ const parentGroup = node.createGroup();
1646
+ parentGroup.set(`child_${group.id}`, "extend", "trusting");
1647
+ group.set(`parent_${parentGroup.id}`, "extend", "trusting");
1648
+ group.rotateReadKey();
1649
+ const currentChildReadKeyID = group.get("readKey");
1650
+ if (!currentChildReadKeyID) {
1651
+ throw new Error("Can't get group read key");
1652
+ }
1653
+ console.log("child id", group.id);
1654
+ parentGroup.rotateReadKey();
1655
+ const newChildReadKeyID = expectGroup(group.core.getCurrentContent()).get("readKey");
1656
+ if (!newChildReadKeyID) {
1657
+ throw new Error("Can't get new group read key");
1658
+ }
1659
+ expect(newChildReadKeyID).not.toEqual(currentChildReadKeyID);
1660
+ });
1661
+ test("When rotating the key of a parent group, the old transactions should still be valid", async () => {
1662
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1663
+ const group = node1.node.createGroup();
1664
+ const parentGroup = node1.node.createGroup();
1665
+ group.extend(parentGroup);
1666
+ const node2AccountOnNode1 = await loadCoValueOrFail(node1.node, node2.accountID);
1667
+ parentGroup.addMember(node2AccountOnNode1, "writer");
1668
+ const map = group.createMap();
1669
+ map.set("from", "node1", "private");
1670
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
1671
+ mapOnNode2.set("from", "node2", "private");
1672
+ await new Promise((resolve) => setTimeout(resolve, 10));
1673
+ parentGroup.removeMember(node2AccountOnNode1);
1674
+ await new Promise((resolve) => setTimeout(resolve, 10));
1675
+ const mapOnNode1 = await loadCoValueOrFail(node1.node, map.id);
1676
+ expect(mapOnNode1.get("from")).toEqual("node2");
1677
+ });
1678
+ test("When rotating the key of a grand-parent group, the keys of all child and grand-child groups are also rotated", () => {
1679
+ const { group, node } = newGroupHighLevel();
1680
+ const grandParentGroup = node.createGroup();
1681
+ const parentGroup = node.createGroup();
1682
+ grandParentGroup.set(`child_${parentGroup.id}`, "extend", "trusting");
1683
+ parentGroup.set(`child_${group.id}`, "extend", "trusting");
1684
+ parentGroup.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
1685
+ group.set(`parent_${grandParentGroup.id}`, "extend", "trusting");
1686
+ const currentGrandParentReadKeyID = grandParentGroup.get("readKey");
1687
+ if (!currentGrandParentReadKeyID) {
1688
+ throw new Error("Can't get grand-parent group read key");
1689
+ }
1690
+ const currentParentReadKeyID = parentGroup.get("readKey");
1691
+ if (!currentParentReadKeyID) {
1692
+ throw new Error("Can't get parent group read key");
1693
+ }
1694
+ const currentChildReadKeyID = group.get("readKey");
1695
+ if (!currentChildReadKeyID) {
1696
+ throw new Error("Can't get group read key");
1697
+ }
1698
+ grandParentGroup.rotateReadKey();
1699
+ const newGrandParentReadKeyID = grandParentGroup.get("readKey");
1700
+ if (!newGrandParentReadKeyID) {
1701
+ throw new Error("Can't get new grand-parent group read key");
1702
+ }
1703
+ expect(newGrandParentReadKeyID).not.toEqual(currentGrandParentReadKeyID);
1704
+ const newParentReadKeyID = expectGroup(parentGroup.core.getCurrentContent()).get("readKey");
1705
+ if (!newParentReadKeyID) {
1706
+ throw new Error("Can't get new parent group read key");
1707
+ }
1708
+ expect(newParentReadKeyID).not.toEqual(currentParentReadKeyID);
1709
+ const newChildReadKeyID = expectGroup(group.core.getCurrentContent()).get("readKey");
1710
+ if (!newChildReadKeyID) {
1711
+ throw new Error("Can't get new group read key");
1712
+ }
1713
+ expect(newChildReadKeyID).not.toEqual(currentChildReadKeyID);
1714
+ });
1715
+ test("Calling extend on group sets up parent and child references and reveals child key to parent", () => {
1716
+ const { group, node } = newGroupHighLevel();
1717
+ const parentGroup = node.createGroup();
1718
+ group.extend(parentGroup);
1719
+ expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
1720
+ expect(parentGroup.get(`child_${group.id}`)).toEqual("extend");
1721
+ const parentReadKeyID = parentGroup.get("readKey");
1722
+ if (!parentReadKeyID) {
1723
+ throw new Error("Can't get parent group read key");
1724
+ }
1725
+ const childReadKeyID = group.get("readKey");
1726
+ if (!childReadKeyID) {
1727
+ throw new Error("Can't get group read key");
1728
+ }
1729
+ expect(group.get(`${childReadKeyID}_for_${parentReadKeyID}`)).toBeDefined();
1730
+ const reader = node.createAccount();
1731
+ parentGroup.addMember(reader, "reader");
1732
+ const childObject = node.createCoValue({
1733
+ type: "comap",
1734
+ ruleset: { type: "ownedByGroup", group: group.id },
1735
+ meta: null,
1736
+ ...Crypto.createdNowUnique(),
1737
+ });
1738
+ const childMap = expectMap(childObject.getCurrentContent());
1739
+ childMap.set("foo", "bar", "private");
1740
+ const childContentAsReader = expectMap(childObject
1741
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1742
+ .getCurrentContent());
1743
+ expect(childContentAsReader.get("foo")).toEqual("bar");
1744
+ });
1745
+ test("Calling extend to create grand-child groups parent and child references and reveals child key to parent(s)", () => {
1746
+ const { group, node } = newGroupHighLevel();
1747
+ const parentGroup = node.createGroup();
1748
+ const grandParentGroup = node.createGroup();
1749
+ group.extend(parentGroup);
1750
+ parentGroup.extend(grandParentGroup);
1751
+ expect(group.get(`parent_${parentGroup.id}`)).toEqual("extend");
1752
+ expect(parentGroup.get(`parent_${grandParentGroup.id}`)).toEqual("extend");
1753
+ expect(parentGroup.get(`child_${group.id}`)).toEqual("extend");
1754
+ expect(grandParentGroup.get(`child_${parentGroup.id}`)).toEqual("extend");
1755
+ const reader = node.createAccount();
1756
+ grandParentGroup.addMember(reader, "reader");
1757
+ const childObject = node.createCoValue({
1758
+ type: "comap",
1759
+ ruleset: { type: "ownedByGroup", group: group.id },
1760
+ meta: null,
1761
+ ...Crypto.createdNowUnique(),
1762
+ });
1763
+ const childMap = expectMap(childObject.getCurrentContent());
1764
+ childMap.set("foo", "bar", "private");
1765
+ const childContentAsReader = expectMap(childObject
1766
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1767
+ .getCurrentContent());
1768
+ expect(childContentAsReader.get("foo")).toEqual("bar");
1769
+ });
1770
+ test("revoking access on a child group doesn't block access to that group if a more permissive role is inheritable", async () => {
1771
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1772
+ const group = node1.node.createGroup();
1773
+ const parentGroup = node1.node.createGroup();
1774
+ group.extend(parentGroup);
1775
+ const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
1776
+ parentGroup.addMember(randomUser, "writer");
1777
+ group.addMember(randomUser, "writer");
1778
+ await group.removeMember(randomUser);
1779
+ const childMap = group.createMap();
1780
+ childMap.set("foo", "bar", "private");
1781
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
1782
+ mapOnNode2.set("foo", "baz", "private");
1783
+ expect(mapOnNode2.get("foo")).toEqual("baz");
1784
+ });
1785
+ 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 () => {
1786
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1787
+ const group = node1.node.createGroup();
1788
+ const parentGroup = node1.node.createGroup();
1789
+ const grandParentGroup = node1.node.createGroup();
1790
+ group.extend(parentGroup);
1791
+ parentGroup.extend(grandParentGroup);
1792
+ const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
1793
+ grandParentGroup.addMember(randomUser, "writer");
1794
+ parentGroup.addMember(randomUser, "writer");
1795
+ await parentGroup.removeMember(randomUser);
1796
+ const childMap = group.createMap();
1797
+ childMap.set("foo", "bar", "private");
1798
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
1799
+ mapOnNode2.set("foo", "baz", "private");
1800
+ expect(mapOnNode2.get("foo")).toEqual("baz");
1801
+ });
1802
+ 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 () => {
1803
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1804
+ const group = node1.node.createGroup();
1805
+ const parentGroup1 = node1.node.createGroup();
1806
+ const parentGroup2 = node1.node.createGroup();
1807
+ group.extend(parentGroup1);
1808
+ group.extend(parentGroup2);
1809
+ const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
1810
+ parentGroup1.addMember(randomUser, "writer");
1811
+ parentGroup2.addMember(randomUser, "writer");
1812
+ await parentGroup1.removeMember(randomUser);
1813
+ const childMap = group.createMap();
1814
+ childMap.set("foo", "bar", "private");
1815
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
1816
+ mapOnNode2.set("foo", "baz", "private");
1817
+ expect(mapOnNode2.get("foo")).toEqual("baz");
1818
+ });
1819
+ test("revoking write access to parent group", async () => {
1820
+ // Start with a node and a group
1821
+ const { group, node } = newGroupHighLevel();
1822
+ // Create a parent group and relate it to the existing group
1823
+ const parentGroup = node.createGroup();
1824
+ group.extend(parentGroup);
1825
+ // Create an account (`alice`) that can write to the parent group
1826
+ // Create an account (`bob`) that can write to the child group
1827
+ const alice = node.createAccount();
1828
+ const bob = node.createAccount();
1829
+ parentGroup.addMember(alice, "writer");
1830
+ group.addMember(bob, "writer");
1831
+ // The child group has a map that can be written to by `bob`
1832
+ const mapCore = node.createCoValue({
1833
+ type: "comap",
1834
+ ruleset: { type: "ownedByGroup", group: group.id },
1835
+ meta: null,
1836
+ ...Crypto.createdNowUnique(),
1837
+ });
1838
+ const bobMap = expectMap(mapCore
1839
+ .testWithDifferentAccount(bob, Crypto.newRandomSessionID(bob.id))
1840
+ .getCurrentContent());
1841
+ // `bob` sets `foo` to `bar`
1842
+ bobMap.set("foo", "bar", "private");
1843
+ // `bob`'s change is made successfully
1844
+ expect(bobMap.get("foo")).toEqual("bar");
1845
+ const aliceMap = expectMap(mapCore
1846
+ .testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
1847
+ .getCurrentContent());
1848
+ // `alice` sets `foo` to `baz`
1849
+ aliceMap.set("foo", "baz", "private");
1850
+ // `alice`'s change is made successfully
1851
+ expect(aliceMap.get("foo")).toEqual("baz");
1852
+ // The two groups are no longer related
1853
+ await group.revokeExtend(parentGroup);
1854
+ // `bob` sets `foo` to `abc`
1855
+ bobMap.set("foo", "abc", "private");
1856
+ // `bob`'s change is made successfully
1857
+ expect(bobMap.get("foo")).toEqual("abc");
1858
+ const aliceMapAfterUnextend = expectMap(mapCore
1859
+ .testWithDifferentAccount(alice, Crypto.newRandomSessionID(alice.id))
1860
+ .getCurrentContent());
1861
+ // `alice` attempts to set `foo` to `def`, but fails
1862
+ expect(() => aliceMapAfterUnextend.set("foo", "def", "private")).toThrow("Can't make transaction without read key secret");
1863
+ // `alice`'s change is not made successfully
1864
+ expect(aliceMapAfterUnextend.get("foo")).not.toEqual("def");
1865
+ });
1866
+ test("revoking read access to parent group", async () => {
1867
+ // Start with two nodes
1868
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1869
+ const group = node1.node.createGroup();
1870
+ // Create a parent group and relate it to the existing group
1871
+ const parentGroup = node1.node.createGroup();
1872
+ group.extend(parentGroup);
1873
+ // Create an account (`alice`) that can read from the parent group
1874
+ // Create an account (`bob`) that can write to the child group
1875
+ const alice = await loadCoValueOrFail(node1.node, node2.accountID);
1876
+ const bob = await loadCoValueOrFail(node1.node, node1.accountID);
1877
+ parentGroup.addMember(alice, "reader");
1878
+ group.addMember(bob, "writer");
1879
+ // The child group has a map that can be written to by `bob`
1880
+ const bobMap = group.createMap();
1881
+ // `bob` sets `foo` to `bar`
1882
+ bobMap.set("foo", "bar", "private");
1883
+ // `bob`'s change is made successfully
1884
+ expect(bobMap.get("foo")).toEqual("bar");
1885
+ const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
1886
+ // `alice` reads `foo` as `bar`
1887
+ expect(aliceMap.get("foo")).toEqual("bar");
1888
+ // The two groups are no longer related
1889
+ await group.revokeExtend(parentGroup);
1890
+ // `bob` sets `foo` to `abc`
1891
+ bobMap.set("foo", "abc", "private");
1892
+ // `bob`'s change is made successfully
1893
+ expect(bobMap.get("foo")).toEqual("abc");
1894
+ // `alice` reads `foo` as `bar`
1895
+ expect(aliceMap.get("foo")).toEqual("bar");
1896
+ });
1897
+ test("revoking read access to grandparent group", async () => {
1898
+ // Start with two nodes
1899
+ const { node1, node2, node3 } = await createThreeConnectedNodes("server", "server", "server");
1900
+ const group = node1.node.createGroup();
1901
+ // Create group hierarchy
1902
+ const parentGroup = node1.node.createGroup();
1903
+ const grandParentGroup = node1.node.createGroup();
1904
+ group.extend(parentGroup);
1905
+ parentGroup.extend(grandParentGroup);
1906
+ // Create an account (`alice`) that can read from the parent group
1907
+ // Create an account (`bob`) that can write to the child group
1908
+ // Create an account (`charlie`) that can read from the grandparent group
1909
+ const alice = await loadCoValueOrFail(node1.node, node2.accountID);
1910
+ const bob = await loadCoValueOrFail(node1.node, node1.accountID);
1911
+ const charlie = await loadCoValueOrFail(node1.node, node3.accountID);
1912
+ parentGroup.addMember(alice, "reader");
1913
+ group.addMember(bob, "writer");
1914
+ grandParentGroup.addMember(charlie, "reader");
1915
+ // The child group has a map that can be written to by `bob`
1916
+ const bobMap = group.createMap();
1917
+ // `bob` sets `foo` to `bar`
1918
+ bobMap.set("foo", "bar", "private");
1919
+ // `bob`'s change is made successfully
1920
+ expect(bobMap.get("foo")).toEqual("bar");
1921
+ const aliceMap = await loadCoValueOrFail(node2.node, bobMap.id);
1922
+ // `alice` reads `foo` as `bar`
1923
+ expect(aliceMap.get("foo")).toEqual("bar");
1924
+ const charlieMap = await loadCoValueOrFail(node3.node, bobMap.id);
1925
+ // `charlie` reads `foo` as `bar`
1926
+ expect(charlieMap.get("foo")).toEqual("bar");
1927
+ // The groups are no longer related
1928
+ await parentGroup.revokeExtend(grandParentGroup);
1929
+ await group.revokeExtend(parentGroup);
1930
+ // `bob` sets `foo` to `abc`
1931
+ bobMap.set("foo", "abc", "private");
1932
+ // `bob`'s change is made successfully
1933
+ expect(bobMap.get("foo")).toEqual("abc");
1934
+ // `alice` reads `foo` as `bar`
1935
+ expect(aliceMap.get("foo")).toEqual("bar");
1936
+ // `charlie` reads `foo` as `bar`
1937
+ expect(charlieMap.get("foo")).toEqual("bar");
1938
+ });
1939
+ test("a user should have write access if the parent group has everyone as a writer", async () => {
1940
+ const { node1, node2 } = await createTwoConnectedNodes("server", "server");
1941
+ const group = node1.node.createGroup();
1942
+ const parentGroup = node1.node.createGroup();
1943
+ group.extend(parentGroup);
1944
+ parentGroup.addMember("everyone", "writer");
1945
+ const randomUser = await loadCoValueOrFail(node1.node, node2.accountID);
1946
+ group.addMember(randomUser, "reader");
1947
+ const childMap = group.createMap();
1948
+ childMap.set("foo", "bar", "private");
1949
+ const mapOnNode2 = await loadCoValueOrFail(node2.node, childMap.id);
1950
+ mapOnNode2.set("foo", "baz", "private");
1951
+ expect(mapOnNode2.get("foo")).toEqual("baz");
1952
+ });
1953
+ test("High-level permissions work correctly when a group is extended", async () => {
1954
+ const { group, node } = newGroupHighLevel();
1955
+ const parentGroup = node.createGroup();
1956
+ group.extend(parentGroup);
1957
+ const reader = node.createAccount();
1958
+ parentGroup.addMember(reader, "reader");
1959
+ const mapCore = node.createCoValue({
1960
+ type: "comap",
1961
+ ruleset: { type: "ownedByGroup", group: group.id },
1962
+ meta: null,
1963
+ ...Crypto.createdNowUnique(),
1964
+ });
1965
+ const map = expectMap(mapCore.getCurrentContent());
1966
+ map.set("foo", "bar", "private");
1967
+ const mapAsReader = expectMap(mapCore
1968
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1969
+ .getCurrentContent());
1970
+ expect(mapAsReader.get("foo")).toEqual("bar");
1971
+ const groupKeyBeforeRemove = group.core.getCurrentReadKey().id;
1972
+ await parentGroup.removeMember(reader);
1973
+ const groupKeyAfterRemove = group.core.getCurrentReadKey().id;
1974
+ expect(groupKeyAfterRemove).not.toEqual(groupKeyBeforeRemove);
1975
+ map.set("foo", "baz", "private");
1976
+ const mapAsReaderAfterRemove = expectMap(mapCore
1977
+ .testWithDifferentAccount(reader, Crypto.newRandomSessionID(reader.id))
1978
+ .getCurrentContent());
1979
+ expect(mapAsReaderAfterRemove.get("foo")).not.toEqual("baz");
1980
+ });
1981
+ test("self-extensions should not break the permissions checks", () => {
1982
+ const { group } = newGroupHighLevel();
1983
+ group.set(`child_${group.id}`, "extend", "trusting");
1984
+ group.set(`parent_${group.id}`, "extend", "trusting");
1985
+ const map = group.createMap();
1986
+ map.set("test", "Hello!");
1987
+ expect(map.get("test")).toEqual("Hello!");
1988
+ });
1989
+ test("extend cycles should not break the permissions checks", () => {
1990
+ const { group, node } = newGroupHighLevel();
1991
+ const group2 = node.createGroup();
1992
+ const group3 = node.createGroup();
1993
+ group.set(`child_${group2.id}`, "extend", "trusting");
1994
+ group2.set(`child_${group3.id}`, "extend", "trusting");
1995
+ group3.set(`child_${group.id}`, "extend", "trusting");
1996
+ group.set(`parent_${group2.id}`, "extend", "trusting");
1997
+ group2.set(`parent_${group3.id}`, "extend", "trusting");
1998
+ group3.set(`parent_${group.id}`, "extend", "trusting");
1999
+ const map = group.createMap();
2000
+ map.set("test", "Hello!");
2001
+ expect(map.get("test")).toEqual("Hello!");
2002
+ });
2003
+ test("extend cycles should not break the keys rotation", () => {
2004
+ const { group, node } = newGroupHighLevel();
2005
+ const group2 = node.createGroup();
2006
+ const group3 = node.createGroup();
2007
+ group.set(`child_${group2.id}`, "extend", "trusting");
2008
+ group2.set(`child_${group3.id}`, "extend", "trusting");
2009
+ group3.set(`child_${group.id}`, "extend", "trusting");
2010
+ group.set(`parent_${group2.id}`, "extend", "trusting");
2011
+ group2.set(`parent_${group3.id}`, "extend", "trusting");
2012
+ group3.set(`parent_${group.id}`, "extend", "trusting");
2013
+ group.rotateReadKey();
2014
+ const map = group.createMap();
2015
+ map.set("test", "Hello!");
2016
+ expect(map.get("test")).toEqual("Hello!");
2017
+ });
2018
+ test("Admin can remove themselves from a group", async () => {
2019
+ const warnSpy = vi.spyOn(console, "warn");
2020
+ const { group, admin } = newGroupHighLevel();
2021
+ // Admin removes themselves
2022
+ await group.removeMember(admin);
2023
+ expect(group.myRole()).toBeUndefined();
2024
+ expect(warnSpy).not.toHaveBeenCalled();
2025
+ });
2026
+ test("Can revoke read permission from 'everyone'", async () => {
2027
+ const { group } = newGroupHighLevel();
2028
+ const childObject = group.createMap();
2029
+ // Give everyone read access
2030
+ group.addMember("everyone", "reader");
2031
+ childObject.set("foo", "bar", "private");
2032
+ expect(childObject.get("foo")).toEqual("bar");
2033
+ // Create a new account to verify access
2034
+ const newAccount = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
2035
+ const childContent = expectMap(childObject.core
2036
+ .testWithDifferentAccount(newAccount, Crypto.newRandomSessionID(newAccount.currentAgentID()))
2037
+ .getCurrentContent());
2038
+ // Verify the new account can read
2039
+ expect(childContent.get("foo")).toEqual("bar");
2040
+ // Revoke everyone's access
2041
+ await group.removeMember("everyone");
2042
+ childObject.set("foo", "updated after revoke", "private");
2043
+ // Create another new account to verify access is revoked
2044
+ const newAccount2 = new ControlledAgent(Crypto.newRandomAgentSecret(), Crypto);
2045
+ const childContent2 = expectMap(childObject.core
2046
+ .testWithDifferentAccount(newAccount2, Crypto.newRandomSessionID(newAccount2.currentAgentID()))
2047
+ .getCurrentContent());
2048
+ // Verify the new account cannot read after revocation
2049
+ expect(childContent2.get("foo")).toEqual("bar");
2050
+ });
2051
+ //# sourceMappingURL=permissions.test.js.map