@secrecy/lib 1.62.0-feat-node-sharing.1 → 1.62.0-feat-node-sharing.2
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.
|
@@ -72,12 +72,17 @@ export class SecrecyAppClient {
|
|
|
72
72
|
publicKeysCache.get(`userPublicKey:${userId}-${appId}`),
|
|
73
73
|
])
|
|
74
74
|
.filter(([_, key]) => !!key));
|
|
75
|
-
const missingKeys =
|
|
75
|
+
const missingKeys = [
|
|
76
|
+
...new Set(userIds.filter((userId) => publicKeys[userId] === undefined)),
|
|
77
|
+
];
|
|
76
78
|
if (missingKeys.length > 0) {
|
|
77
79
|
const userKeysMap = await this.#apiClient.application.userPublicKey.query(userIds);
|
|
78
80
|
if ('publicKey' in userKeysMap) {
|
|
79
81
|
throw Error('Should not happen!');
|
|
80
82
|
}
|
|
83
|
+
if (Object.keys(userKeysMap).length !== missingKeys.length) {
|
|
84
|
+
throw new Error("Unable to load some user's public keys!");
|
|
85
|
+
}
|
|
81
86
|
for (const userId in userKeysMap) {
|
|
82
87
|
publicKeys[userId] = userKeysMap[userId];
|
|
83
88
|
publicKeysCache.set(`userPublicKey:${userId}-${appId}`, userKeysMap[userId]);
|
|
@@ -296,15 +296,10 @@ export class SecrecyCloudClient {
|
|
|
296
296
|
const users = folder.parent?.users?.filter(([u]) => u.id !== this.#client.app.userId) ??
|
|
297
297
|
[];
|
|
298
298
|
if (users.length > 0) {
|
|
299
|
-
await
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
userId: u.id,
|
|
304
|
-
nodeId: folder.id,
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
})));
|
|
299
|
+
await this.shareNode({
|
|
300
|
+
nodeId: folder.id,
|
|
301
|
+
users: users.map(([user, rights]) => ({ id: user.id, rights })),
|
|
302
|
+
});
|
|
308
303
|
}
|
|
309
304
|
return folder;
|
|
310
305
|
}
|
|
@@ -322,23 +317,33 @@ export class SecrecyCloudClient {
|
|
|
322
317
|
return apiDataToExternal(data, this.#keys);
|
|
323
318
|
}
|
|
324
319
|
async shareNode(input) {
|
|
325
|
-
const userIds =
|
|
326
|
-
? input.nodes
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
320
|
+
const userIds = 'rights' in input
|
|
321
|
+
? Array.isArray(input.nodes)
|
|
322
|
+
? input.nodes.map(({ userId }) => userId)
|
|
323
|
+
: 'userIds' in input.nodes
|
|
324
|
+
? input.nodes.userIds
|
|
325
|
+
: [input.nodes.userId]
|
|
326
|
+
: input.users.map(({ id }) => id);
|
|
327
|
+
const nodesToShare = 'nodes' in input
|
|
328
|
+
? input.nodes
|
|
329
|
+
: {
|
|
330
|
+
nodeId: input.nodeId,
|
|
331
|
+
userIds: input.users.map(({ id }) => id),
|
|
332
|
+
};
|
|
330
333
|
const [publicKeysMap, nodesIdsMap] = await Promise.all([
|
|
331
334
|
this.#client.app.userPublicKey(userIds),
|
|
332
|
-
this.#apiClient.cloud.shareNode.mutate(
|
|
335
|
+
this.#apiClient.cloud.shareNode.mutate(nodesToShare),
|
|
333
336
|
]);
|
|
334
|
-
const nodesInfos = await this.
|
|
335
|
-
const sharingState = await this.#apiClient.cloud.shareNodeFinish.mutate(Object.entries(nodesInfos).map(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
337
|
+
const nodesInfos = await this.encryptNodesForUsers(nodesIdsMap, publicKeysMap);
|
|
338
|
+
const sharingState = await this.#apiClient.cloud.shareNodeFinish.mutate(Object.entries(nodesInfos).map('rights' in input
|
|
339
|
+
? ([userId, nodes]) => ({ userId, nodes, rights: input.rights })
|
|
340
|
+
: ([userId, nodes]) => {
|
|
341
|
+
const user = input.users.find((user) => user.id === userId);
|
|
342
|
+
if (!user) {
|
|
343
|
+
throw new Error(`Unable to find rights for user: ${userId}`);
|
|
344
|
+
}
|
|
345
|
+
return { userId, nodes, rights: user.rights };
|
|
346
|
+
}));
|
|
342
347
|
return sharingState;
|
|
343
348
|
}
|
|
344
349
|
async updateNode({ nodeId, name, isFavorite, deletedAt, }) {
|
|
@@ -524,19 +529,16 @@ export class SecrecyCloudClient {
|
|
|
524
529
|
if (me !== undefined && ['admin', 'write'].includes(me[1])) {
|
|
525
530
|
const others = node.parent?.users.filter(([u]) => u.id !== this.#client.app.userId) ??
|
|
526
531
|
[];
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
{
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
})));
|
|
532
|
+
if (others.length > 0) {
|
|
533
|
+
await this.shareNode({
|
|
534
|
+
nodeId: node.id,
|
|
535
|
+
users: others.map(([user, rights]) => ({ id: user.id, rights })),
|
|
536
|
+
});
|
|
537
|
+
}
|
|
536
538
|
}
|
|
537
539
|
return node;
|
|
538
540
|
}
|
|
539
|
-
|
|
541
|
+
encryptNodesForUsers = async (userNodes, // { [userId]: nodeIds[] }
|
|
540
542
|
userPublicKeys) => {
|
|
541
543
|
const userIds = Object.keys(userNodes).map((userId) => userId);
|
|
542
544
|
const nodeIds = Object.values(userNodes).flatMap((nodeIds) => nodeIds);
|
|
@@ -3,15 +3,6 @@ import { decryptCryptoBox } from '../../crypto/index.js';
|
|
|
3
3
|
import { nodesCache } from '../../cache.js';
|
|
4
4
|
import { decryptSecretStream } from '../../crypto/data.js';
|
|
5
5
|
import { apiDataToInternal, internalDataToExternalData } from './data.js';
|
|
6
|
-
async function readName({ encryptedName, encryptedNameKey, sharedByPubKey, privateKey, }) {
|
|
7
|
-
const nameKeyBuffer = decryptCryptoBox(sodium.from_hex(encryptedNameKey), sharedByPubKey, privateKey);
|
|
8
|
-
const nameKey = sodium.to_hex(nameKeyBuffer);
|
|
9
|
-
const name = sodium.to_string(await decryptSecretStream(nameKeyBuffer, sodium.from_hex(encryptedName)));
|
|
10
|
-
return {
|
|
11
|
-
name,
|
|
12
|
-
nameKey,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
6
|
async function apiNodeToInternal(apiNode, keyPair) {
|
|
16
7
|
const internal = {
|
|
17
8
|
id: apiNode.id,
|
|
@@ -34,6 +25,7 @@ async function apiNodeToInternal(apiNode, keyPair) {
|
|
|
34
25
|
parentId: apiNode.parentId ?? null,
|
|
35
26
|
currentDataId: apiNode.currentDataId ?? null,
|
|
36
27
|
};
|
|
28
|
+
internal.access = { ...apiNode.access };
|
|
37
29
|
if (apiNode.access.nameKey !== null) {
|
|
38
30
|
const key = decryptCryptoBox(sodium.from_hex(apiNode.access.nameKey), apiNode.access.sharedByPubKey, keyPair.privateKey);
|
|
39
31
|
internal.name = sodium.to_string(await decryptSecretStream(key, sodium.from_hex(internal.name)));
|
|
@@ -56,7 +56,25 @@ export declare class SecrecyCloudClient {
|
|
|
56
56
|
}): Promise<DataMetadata>;
|
|
57
57
|
shareNode(input: {
|
|
58
58
|
rights: Rights;
|
|
59
|
-
nodes:
|
|
59
|
+
nodes: {
|
|
60
|
+
nodeId: string;
|
|
61
|
+
userId: string;
|
|
62
|
+
}[] | {
|
|
63
|
+
nodeId: string;
|
|
64
|
+
userIds: string[];
|
|
65
|
+
} | {
|
|
66
|
+
nodeIds: string[];
|
|
67
|
+
userId: string;
|
|
68
|
+
} | {
|
|
69
|
+
nodeIds: string[];
|
|
70
|
+
userIds: string[];
|
|
71
|
+
};
|
|
72
|
+
} | {
|
|
73
|
+
nodeId: string;
|
|
74
|
+
users: {
|
|
75
|
+
id: string;
|
|
76
|
+
rights: Rights;
|
|
77
|
+
}[];
|
|
60
78
|
}): Promise<RouterOutputs['cloud']['shareNodeFinish']>;
|
|
61
79
|
updateNode({ nodeId, name, isFavorite, deletedAt, }: {
|
|
62
80
|
nodeId: string;
|
|
@@ -99,7 +117,7 @@ export declare class SecrecyCloudClient {
|
|
|
99
117
|
name: string;
|
|
100
118
|
nodeId?: string;
|
|
101
119
|
}): Promise<NodeFull>;
|
|
102
|
-
private readonly
|
|
120
|
+
private readonly encryptNodesForUsers;
|
|
103
121
|
reportData({ id, reasons, }: Omit<RouterInputs['cloud']['reportData'], 'encryptedDataKey'>): Promise<RouterOutputs['cloud']['reportData']>;
|
|
104
122
|
updateDataStorageType(input: RouterInputs['cloud']['moveToStorageType']): Promise<{
|
|
105
123
|
isMoved: boolean;
|
|
@@ -61,4 +61,12 @@ export type ApiNode = RouterOutputs['cloud']['nodeById'];
|
|
|
61
61
|
export type ApiNodeFull = RouterOutputs['cloud']['nodeFullById'];
|
|
62
62
|
export type ApiNodeParent = NonNullable<RouterOutputs['cloud']['nodeFullById']['parent']>;
|
|
63
63
|
export type NodeType = ApiNode['type'];
|
|
64
|
+
export type EncryptedNodeInfos = {
|
|
65
|
+
id: string;
|
|
66
|
+
nameKey: string | null;
|
|
67
|
+
data: {
|
|
68
|
+
id: string;
|
|
69
|
+
key: string | null;
|
|
70
|
+
}[];
|
|
71
|
+
};
|
|
64
72
|
export {};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@secrecy/lib",
|
|
3
3
|
"author": "Anonymize <anonymize@gmail.com>",
|
|
4
4
|
"description": "Anonymize Secrecy Library",
|
|
5
|
-
"version": "1.62.0-feat-node-sharing.
|
|
5
|
+
"version": "1.62.0-feat-node-sharing.2",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/anonymize-org/lib.git"
|