@secrecy/lib 1.85.1 → 1.85.2-feat-folder-perms.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.
- package/dist/lib/client/SecrecyCloudClient.js +59 -72
- package/dist/lib/client/convert/node.js +2 -1
- package/dist/lib/crypto/helpers.js +4 -1
- package/dist/types/client/SecrecyCloudClient.d.ts +2 -2
- package/dist/types/client/types/node.d.ts +6 -1
- package/dist/types/client.d.ts +534 -148
- package/dist/types/crypto/helpers.d.ts +5 -0
- package/package.json +2 -2
|
@@ -9,38 +9,32 @@ import { apiNodeForEncryptionToInternal, apiNodeFullToInternalFull, apiNodeToExt
|
|
|
9
9
|
import { buildBytesFromApiData } from '../crypto/domain.js';
|
|
10
10
|
import { chunkByTotalItems } from '../utils/object.js';
|
|
11
11
|
import { uploadData } from './upload.js';
|
|
12
|
-
import { encryptName,
|
|
12
|
+
import { encryptName, encryptNameKey } from '../crypto/helpers.js';
|
|
13
13
|
import { downloadDataFromLink, } from './data-link.js';
|
|
14
|
+
import { secretStreamKeygen } from '../crypto/data.js';
|
|
14
15
|
export class SecrecyCloudClient {
|
|
15
16
|
#client;
|
|
16
17
|
constructor(client) {
|
|
17
18
|
this.#client = client;
|
|
18
19
|
}
|
|
19
20
|
async addDataToHistory({ dataId, nodeId, }) {
|
|
21
|
+
const currentNode = await apiNodeFullToInternalFull(await this.#client.apiClient.cloud.nodeFullById.query({ id: nodeId }), this.#client.keyPairs);
|
|
22
|
+
const currentData = currentNode.history.find((d) => d.id === dataId);
|
|
23
|
+
if (currentData === undefined) {
|
|
24
|
+
throw new Error(`Data ${dataId} is not in node ${nodeId}`);
|
|
25
|
+
}
|
|
26
|
+
const encryptedAccesses = Object.keys(currentNode.identities).map((pubKey) => ({
|
|
27
|
+
pubKey,
|
|
28
|
+
key: currentData.key
|
|
29
|
+
? sodium.to_hex(encryptCryptoBox(sodium.from_hex(currentData.key), pubKey, this.#client.currentIdentityPrivateKey))
|
|
30
|
+
: null,
|
|
31
|
+
}));
|
|
20
32
|
const addDataToHistory = await this.#client.apiClient.cloud.addDataToHistory.mutate({
|
|
21
33
|
dataId,
|
|
22
34
|
nodeId,
|
|
23
|
-
|
|
35
|
+
encryptedAccesses,
|
|
24
36
|
});
|
|
25
37
|
const node = await apiNodeFullToInternalFull(addDataToHistory, this.#client.keyPairs);
|
|
26
|
-
const data = node.history.find((d) => d.id === dataId);
|
|
27
|
-
if (data !== undefined) {
|
|
28
|
-
const othersIdentities = Object.entries(node.identities).filter(([id]) => id !== this.#client.currentIdentity.identityPubKey);
|
|
29
|
-
const shares = othersIdentities.map(([publicKey]) => {
|
|
30
|
-
return {
|
|
31
|
-
pubKey: publicKey,
|
|
32
|
-
key: data.key
|
|
33
|
-
? sodium.to_hex(encryptCryptoBox(sodium.from_hex(data.key), publicKey, this.#client.currentIdentityPrivateKey))
|
|
34
|
-
: null,
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
await this.#client.apiClient.cloud.shareDataInHistory.mutate({
|
|
38
|
-
dataId: data.id,
|
|
39
|
-
nodeId,
|
|
40
|
-
users: shares,
|
|
41
|
-
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
38
|
return internalNodeFullToNodeFull(node);
|
|
45
39
|
}
|
|
46
40
|
async uploadData(opts) {
|
|
@@ -81,17 +75,20 @@ export class SecrecyCloudClient {
|
|
|
81
75
|
return await Promise.all(nodesSharedWithMe.map(async (node) => await apiNodeToExternal(node, this.#client.keyPairs)));
|
|
82
76
|
}
|
|
83
77
|
async deleteNodeSharing({ nodeId, destPubKey, }) {
|
|
78
|
+
const node = await this.node({ id: nodeId });
|
|
79
|
+
const rootAccessSharedByPubKey = node.accesses.find((access) => access.identityPubKey === destPubKey && access.type === 'ROOT')?.sharedByPubKey;
|
|
84
80
|
const { isDeleted } = await this.#client.apiClient.cloud.deleteNodeSharing.mutate({
|
|
85
81
|
nodeId,
|
|
86
82
|
destPubKey,
|
|
83
|
+
rootAccessSharedByPubKey,
|
|
87
84
|
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
88
85
|
});
|
|
89
86
|
return isDeleted;
|
|
90
87
|
}
|
|
91
|
-
async leaveNodeSharing({ nodeId,
|
|
88
|
+
async leaveNodeSharing({ nodeId, sharedByPubKey, }) {
|
|
92
89
|
const { isDeleted } = await this.#client.apiClient.cloud.leaveSharing.mutate({
|
|
93
90
|
nodeId,
|
|
94
|
-
|
|
91
|
+
sharedByPubKey,
|
|
95
92
|
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
96
93
|
});
|
|
97
94
|
return isDeleted;
|
|
@@ -128,34 +125,23 @@ export class SecrecyCloudClient {
|
|
|
128
125
|
return isDeleted;
|
|
129
126
|
}
|
|
130
127
|
async createFolder({ name, parentFolderId, }) {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
128
|
+
const nameKey = secretStreamKeygen();
|
|
129
|
+
const encName = await encryptName(name, sodium.to_hex(nameKey));
|
|
130
|
+
const parentFolder = await this.node({ id: parentFolderId });
|
|
131
|
+
const encryptedAccesses = await Promise.all(Object.keys(parentFolder.identities).map(async (pubKey) => ({
|
|
132
|
+
pubKey,
|
|
133
|
+
nameKey: await encryptNameKey({
|
|
134
|
+
nameKey,
|
|
135
|
+
privateKey: this.#client.currentIdentityPrivateKey,
|
|
136
|
+
publicKey: pubKey,
|
|
137
|
+
}),
|
|
138
|
+
})));
|
|
136
139
|
const createdFolder = await this.#client.apiClient.cloud.createFolder.mutate({
|
|
137
|
-
name:
|
|
140
|
+
name: encName,
|
|
138
141
|
parentId: parentFolderId ?? null,
|
|
139
|
-
|
|
140
|
-
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
142
|
+
encryptedAccesses,
|
|
141
143
|
});
|
|
142
|
-
|
|
143
|
-
if (folder.parent) {
|
|
144
|
-
const othersIdentities = Object.entries(folder.parent.identities).filter(([id]) => id !== this.#client.currentIdentity.identityPubKey);
|
|
145
|
-
// TODO: ??
|
|
146
|
-
if (othersIdentities.length > 0) {
|
|
147
|
-
await this.shareNode(othersIdentities.map(([identityPubKey, permissions]) => ({
|
|
148
|
-
pubKey: identityPubKey,
|
|
149
|
-
nodeId: folder.id,
|
|
150
|
-
rights: permissions.rights,
|
|
151
|
-
addAccess: permissions.addAccess,
|
|
152
|
-
delAccess: permissions.delAccess,
|
|
153
|
-
sharingAddAccess: permissions.sharingAddAccess,
|
|
154
|
-
sharingDelAccess: permissions.sharingDelAccess,
|
|
155
|
-
})));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return folder;
|
|
144
|
+
return apiNodeToExternalNodeFull(createdFolder, this.#client.keyPairs);
|
|
159
145
|
}
|
|
160
146
|
async node({ id, deleted, } = {}) {
|
|
161
147
|
const node = await this.#client.apiClient.cloud.nodeById.query({
|
|
@@ -286,6 +272,7 @@ export class SecrecyCloudClient {
|
|
|
286
272
|
if (!item) {
|
|
287
273
|
throw new Error('Unable to retrieve user mapped nodes!');
|
|
288
274
|
}
|
|
275
|
+
console.dir({ node, item });
|
|
289
276
|
return {
|
|
290
277
|
id: node.id,
|
|
291
278
|
data: node.data,
|
|
@@ -534,35 +521,35 @@ export class SecrecyCloudClient {
|
|
|
534
521
|
else {
|
|
535
522
|
key = data.key;
|
|
536
523
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
524
|
+
const rawDataKey = key ? sodium.from_hex(key) : null;
|
|
525
|
+
const nameKey = secretStreamKeygen();
|
|
526
|
+
const encName = await encryptName(name, sodium.to_hex(nameKey));
|
|
527
|
+
const parentFolder = nodeId ? nodesCache.get(nodeId) : await this.node();
|
|
528
|
+
if (!parentFolder) {
|
|
529
|
+
throw new Error('Unable to retrieve parent');
|
|
530
|
+
}
|
|
531
|
+
const encryptedAccesses = await Promise.all(Object.keys(parentFolder.identities).map(async (pubKey) => {
|
|
532
|
+
const encryptedNameKey = await encryptNameKey({
|
|
533
|
+
nameKey,
|
|
534
|
+
privateKey: this.#client.currentIdentityPrivateKey,
|
|
535
|
+
publicKey: pubKey,
|
|
536
|
+
});
|
|
537
|
+
const dataKey = rawDataKey
|
|
538
|
+
? sodium.to_hex(encryptCryptoBox(rawDataKey, pubKey, this.#client.currentIdentityPrivateKey))
|
|
539
|
+
: null;
|
|
540
|
+
return {
|
|
541
|
+
pubKey,
|
|
542
|
+
nameKey: encryptedNameKey,
|
|
543
|
+
key: dataKey,
|
|
544
|
+
};
|
|
545
|
+
}));
|
|
545
546
|
const saveInCloud = await this.#client.apiClient.cloud.saveInCloud.mutate({
|
|
546
547
|
dataId,
|
|
547
|
-
key,
|
|
548
548
|
nodeId: nodeId ?? null,
|
|
549
|
-
fileName:
|
|
550
|
-
|
|
551
|
-
fromIdentityPubKey: this.#client.currentIdentity.identityPubKey,
|
|
549
|
+
fileName: encName,
|
|
550
|
+
encryptedAccesses,
|
|
552
551
|
});
|
|
553
|
-
|
|
554
|
-
if (node.parent) {
|
|
555
|
-
const othersIdentities = Object.entries(node.parent.identities).filter(([id]) => id !== this.#client.currentIdentity.identityPubKey);
|
|
556
|
-
// TODO: ??
|
|
557
|
-
if (othersIdentities.length > 0) {
|
|
558
|
-
await this.shareNode(othersIdentities.map(([identityPubKey, permissions]) => ({
|
|
559
|
-
pubKey: identityPubKey,
|
|
560
|
-
nodeId: node.id,
|
|
561
|
-
...permissions,
|
|
562
|
-
})));
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return node;
|
|
552
|
+
return apiNodeToExternalNodeFull(saveInCloud, this.#client.keyPairs);
|
|
566
553
|
}
|
|
567
554
|
encryptNodesForIdentities = async (identityNodes) => {
|
|
568
555
|
const nodeIds = Object.values(identityNodes).flatMap((nodeIds) => nodeIds);
|
|
@@ -84,9 +84,10 @@ function internalNodeToNode(internal) {
|
|
|
84
84
|
name: b.name,
|
|
85
85
|
})),
|
|
86
86
|
accesses: internal.accesses.map((a) => ({
|
|
87
|
-
|
|
87
|
+
type: a.type,
|
|
88
88
|
identityPubKey: a.identityPubKey,
|
|
89
89
|
sharedByPubKey: a.sharedByPubKey,
|
|
90
|
+
rootAccess: a.rootAccess,
|
|
90
91
|
rights: a.rights,
|
|
91
92
|
addAccess: a.addAccess,
|
|
92
93
|
delAccess: a.delAccess,
|
|
@@ -14,10 +14,13 @@ export async function encryptName(name, nameKey) {
|
|
|
14
14
|
export async function generateAndEncryptNameAndKey(args) {
|
|
15
15
|
const nameKey = secretStreamKeygen();
|
|
16
16
|
const encryptedName = await encryptName(args.name, sodium.to_hex(nameKey));
|
|
17
|
-
const encryptedNameKey =
|
|
17
|
+
const encryptedNameKey = await encryptNameKey({ ...args, nameKey });
|
|
18
18
|
return {
|
|
19
19
|
nameKey,
|
|
20
20
|
encryptedName,
|
|
21
21
|
encryptedNameKey,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
+
export async function encryptNameKey(args) {
|
|
25
|
+
return sodium.to_hex(encryptCryptoBox(args.nameKey, args.publicKey, args.privateKey));
|
|
26
|
+
}
|
|
@@ -32,9 +32,9 @@ export declare class SecrecyCloudClient {
|
|
|
32
32
|
nodeId: string;
|
|
33
33
|
destPubKey: string;
|
|
34
34
|
}): Promise<boolean>;
|
|
35
|
-
leaveNodeSharing({ nodeId,
|
|
35
|
+
leaveNodeSharing({ nodeId, sharedByPubKey, }: {
|
|
36
36
|
nodeId: string;
|
|
37
|
-
|
|
37
|
+
sharedByPubKey: string;
|
|
38
38
|
}): Promise<boolean>;
|
|
39
39
|
duplicateNode({ nodeId, folderId, name, }: {
|
|
40
40
|
nodeId: string;
|
|
@@ -2,10 +2,15 @@ import { type RouterOutputs } from '../../client.js';
|
|
|
2
2
|
import type { DataMetadata, InternalData, UserAppOrg } from './index.js';
|
|
3
3
|
export type Permissions = ApiNode['identities'][number];
|
|
4
4
|
export type Rights = Permissions['rights'];
|
|
5
|
+
export type NodeAccessType = 'ROOT' | 'INHERITED';
|
|
5
6
|
export type NodeAccess<T extends Record<string, unknown> = Record<string, unknown>> = T & Permissions & {
|
|
6
|
-
|
|
7
|
+
type: NodeAccessType;
|
|
7
8
|
sharedByPubKey: string;
|
|
8
9
|
identityPubKey: string;
|
|
10
|
+
rootAccess: {
|
|
11
|
+
type: NodeAccessType;
|
|
12
|
+
sharedByPubKey: string;
|
|
13
|
+
} | null;
|
|
9
14
|
};
|
|
10
15
|
export interface NodeBreadcrumbItem {
|
|
11
16
|
id: string;
|