@protontech/drive-sdk 0.6.2 → 0.7.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.
- package/dist/internal/apiService/apiService.d.ts +2 -2
- package/dist/internal/apiService/apiService.js.map +1 -1
- package/dist/internal/apiService/errors.js +4 -3
- package/dist/internal/apiService/errors.js.map +1 -1
- package/dist/internal/download/cryptoService.js +8 -6
- package/dist/internal/download/cryptoService.js.map +1 -1
- package/dist/internal/download/fileDownloader.d.ts +2 -1
- package/dist/internal/download/fileDownloader.js +6 -3
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/download/index.d.ts +1 -1
- package/dist/internal/download/index.js +3 -3
- package/dist/internal/download/index.js.map +1 -1
- package/dist/internal/nodes/apiService.d.ts +9 -8
- package/dist/internal/nodes/apiService.js +14 -5
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +5 -5
- package/dist/internal/nodes/apiService.test.js.map +1 -1
- package/dist/internal/nodes/cryptoReporter.d.ts +3 -3
- package/dist/internal/nodes/cryptoReporter.js.map +1 -1
- package/dist/internal/nodes/cryptoService.d.ts +12 -21
- package/dist/internal/nodes/cryptoService.js +45 -14
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/cryptoService.test.js +262 -17
- package/dist/internal/nodes/cryptoService.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +13 -3
- package/dist/internal/nodes/nodesAccess.d.ts +8 -1
- package/dist/internal/nodes/nodesAccess.js +13 -0
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.d.ts +4 -4
- package/dist/internal/nodes/nodesManagement.js +16 -20
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +21 -10
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/photos/upload.d.ts +2 -2
- package/dist/internal/photos/upload.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +6 -6
- package/dist/internal/sharingPublic/index.js +8 -7
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/nodes.d.ts +16 -3
- package/dist/internal/sharingPublic/nodes.js +34 -2
- package/dist/internal/sharingPublic/nodes.js.map +1 -1
- package/dist/internal/sharingPublic/unauthApiService.d.ts +17 -0
- package/dist/internal/sharingPublic/unauthApiService.js +31 -0
- package/dist/internal/sharingPublic/unauthApiService.js.map +1 -0
- package/dist/internal/sharingPublic/unauthApiService.test.d.ts +1 -0
- package/dist/internal/sharingPublic/unauthApiService.test.js +27 -0
- package/dist/internal/sharingPublic/unauthApiService.test.js.map +1 -0
- package/dist/internal/upload/apiService.d.ts +4 -3
- package/dist/internal/upload/apiService.js.map +1 -1
- package/dist/internal/upload/cryptoService.d.ts +8 -3
- package/dist/internal/upload/cryptoService.js +45 -9
- package/dist/internal/upload/cryptoService.js.map +1 -1
- package/dist/internal/upload/fileUploader.test.js +1 -1
- package/dist/internal/upload/fileUploader.test.js.map +1 -1
- package/dist/internal/upload/interface.d.ts +25 -13
- package/dist/internal/upload/manager.js +7 -4
- package/dist/internal/upload/manager.js.map +1 -1
- package/dist/internal/upload/manager.test.js +5 -4
- package/dist/internal/upload/manager.test.js.map +1 -1
- package/dist/internal/upload/streamUploader.js +9 -4
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +8 -5
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +1 -1
- package/dist/protonDriveClient.js +2 -1
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +2 -1
- package/dist/protonDrivePublicLinkClient.js +7 -5
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/apiService/apiService.ts +2 -2
- package/src/internal/apiService/errors.ts +5 -4
- package/src/internal/download/cryptoService.ts +13 -6
- package/src/internal/download/fileDownloader.ts +4 -2
- package/src/internal/download/index.ts +3 -0
- package/src/internal/nodes/apiService.test.ts +5 -5
- package/src/internal/nodes/apiService.ts +23 -10
- package/src/internal/nodes/cryptoReporter.ts +3 -3
- package/src/internal/nodes/cryptoService.test.ts +370 -18
- package/src/internal/nodes/cryptoService.ts +73 -32
- package/src/internal/nodes/interface.ts +16 -2
- package/src/internal/nodes/nodesAccess.ts +17 -0
- package/src/internal/nodes/nodesManagement.test.ts +21 -10
- package/src/internal/nodes/nodesManagement.ts +20 -24
- package/src/internal/photos/upload.ts +3 -3
- package/src/internal/sharingPublic/index.ts +7 -3
- package/src/internal/sharingPublic/nodes.ts +43 -2
- package/src/internal/sharingPublic/unauthApiService.test.ts +29 -0
- package/src/internal/sharingPublic/unauthApiService.ts +32 -0
- package/src/internal/upload/apiService.ts +4 -3
- package/src/internal/upload/cryptoService.ts +73 -12
- package/src/internal/upload/fileUploader.test.ts +1 -1
- package/src/internal/upload/interface.ts +24 -13
- package/src/internal/upload/manager.test.ts +5 -4
- package/src/internal/upload/manager.ts +7 -4
- package/src/internal/upload/streamUploader.test.ts +8 -5
- package/src/internal/upload/streamUploader.ts +10 -4
- package/src/protonDriveClient.ts +7 -2
- package/src/protonDrivePublicLinkClient.ts +8 -3
|
@@ -21,6 +21,7 @@ export function initDownloadModule(
|
|
|
21
21
|
sharesService: SharesService,
|
|
22
22
|
nodesService: NodesService,
|
|
23
23
|
revisionsService: RevisionsService,
|
|
24
|
+
ignoreManifestVerification = false,
|
|
24
25
|
) {
|
|
25
26
|
const queue = new DownloadQueue();
|
|
26
27
|
const api = new DownloadAPIService(apiService);
|
|
@@ -63,6 +64,7 @@ export function initDownloadModule(
|
|
|
63
64
|
node.activeRevision.value,
|
|
64
65
|
signal,
|
|
65
66
|
onFinish,
|
|
67
|
+
ignoreManifestVerification,
|
|
66
68
|
);
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -102,6 +104,7 @@ export function initDownloadModule(
|
|
|
102
104
|
revision,
|
|
103
105
|
signal,
|
|
104
106
|
onFinish,
|
|
107
|
+
ignoreManifestVerification,
|
|
105
108
|
);
|
|
106
109
|
}
|
|
107
110
|
|
|
@@ -577,8 +577,8 @@ describe('nodeAPIService', () => {
|
|
|
577
577
|
});
|
|
578
578
|
});
|
|
579
579
|
|
|
580
|
-
describe('
|
|
581
|
-
it('should delete nodes', async () => {
|
|
580
|
+
describe('deleteTrashedNodes', () => {
|
|
581
|
+
it('should delete trashed nodes', async () => {
|
|
582
582
|
// @ts-expect-error Mocking for testing purposes
|
|
583
583
|
apiMock.post = jest.fn(async () =>
|
|
584
584
|
Promise.resolve({
|
|
@@ -600,14 +600,14 @@ describe('nodeAPIService', () => {
|
|
|
600
600
|
}),
|
|
601
601
|
);
|
|
602
602
|
|
|
603
|
-
const result = await Array.fromAsync(api.
|
|
603
|
+
const result = await Array.fromAsync(api.deleteTrashedNodes(['volumeId~nodeId1', 'volumeId~nodeId2']));
|
|
604
604
|
expect(result).toEqual([
|
|
605
605
|
{ uid: 'volumeId~nodeId1', ok: true },
|
|
606
606
|
{ uid: 'volumeId~nodeId2', ok: false, error: 'INSUFFICIENT_SCOPE' },
|
|
607
607
|
]);
|
|
608
608
|
});
|
|
609
609
|
|
|
610
|
-
it('should delete nodes from multiple volumes', async () => {
|
|
610
|
+
it('should delete trashed nodes from multiple volumes', async () => {
|
|
611
611
|
// @ts-expect-error Mocking for testing purposes
|
|
612
612
|
apiMock.post = jest.fn(async (_, { LinkIDs }) =>
|
|
613
613
|
Promise.resolve({
|
|
@@ -620,7 +620,7 @@ describe('nodeAPIService', () => {
|
|
|
620
620
|
}),
|
|
621
621
|
);
|
|
622
622
|
|
|
623
|
-
const result = await Array.fromAsync(api.
|
|
623
|
+
const result = await Array.fromAsync(api.deleteTrashedNodes(['volumeId1~nodeId1', 'volumeId2~nodeId2']));
|
|
624
624
|
expect(result).toEqual([
|
|
625
625
|
{ uid: 'volumeId1~nodeId1', ok: true },
|
|
626
626
|
{ uid: 'volumeId2~nodeId2', ok: true },
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { c } from 'ttag';
|
|
2
2
|
|
|
3
3
|
import { NodeWithSameNameExistsValidationError, ProtonDriveError, ValidationError } from '../../errors';
|
|
4
|
-
import { Logger, NodeResult } from '../../interface';
|
|
5
|
-
import { MemberRole, RevisionState } from '../../interface/nodes';
|
|
4
|
+
import { Logger, NodeResult, MemberRole, RevisionState, AnonymousUser } from '../../interface';
|
|
6
5
|
import {
|
|
7
6
|
DriveAPIService,
|
|
8
7
|
drivePaths,
|
|
@@ -102,7 +101,6 @@ type PostRestoreRevisionResponse =
|
|
|
102
101
|
type DeleteRevisionResponse =
|
|
103
102
|
drivePaths['/drive/v2/volumes/{volumeID}/files/{linkID}/revisions/{revisionID}']['delete']['responses']['200']['content']['application/json'];
|
|
104
103
|
|
|
105
|
-
|
|
106
104
|
type PostCheckAvailableHashesRequest = Extract<
|
|
107
105
|
drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['requestBody'],
|
|
108
106
|
{ content: object }
|
|
@@ -292,7 +290,7 @@ export class NodeAPIService {
|
|
|
292
290
|
},
|
|
293
291
|
newNode: {
|
|
294
292
|
encryptedName: string;
|
|
295
|
-
nameSignatureEmail: string;
|
|
293
|
+
nameSignatureEmail: string | AnonymousUser;
|
|
296
294
|
hash?: string;
|
|
297
295
|
},
|
|
298
296
|
signal?: AbortSignal,
|
|
@@ -332,9 +330,9 @@ export class NodeAPIService {
|
|
|
332
330
|
parentUid: string;
|
|
333
331
|
armoredNodePassphrase: string;
|
|
334
332
|
armoredNodePassphraseSignature?: string;
|
|
335
|
-
signatureEmail?: string;
|
|
333
|
+
signatureEmail?: string | AnonymousUser;
|
|
336
334
|
encryptedName: string;
|
|
337
|
-
nameSignatureEmail?: string;
|
|
335
|
+
nameSignatureEmail?: string | AnonymousUser;
|
|
338
336
|
hash: string;
|
|
339
337
|
contentHash?: string;
|
|
340
338
|
},
|
|
@@ -369,9 +367,9 @@ export class NodeAPIService {
|
|
|
369
367
|
parentUid: string;
|
|
370
368
|
armoredNodePassphrase: string;
|
|
371
369
|
armoredNodePassphraseSignature?: string;
|
|
372
|
-
signatureEmail?: string;
|
|
370
|
+
signatureEmail?: string | AnonymousUser;
|
|
373
371
|
encryptedName: string;
|
|
374
|
-
nameSignatureEmail?: string;
|
|
372
|
+
nameSignatureEmail?: string | AnonymousUser;
|
|
375
373
|
hash: string;
|
|
376
374
|
},
|
|
377
375
|
signal?: AbortSignal,
|
|
@@ -430,7 +428,7 @@ export class NodeAPIService {
|
|
|
430
428
|
}
|
|
431
429
|
}
|
|
432
430
|
|
|
433
|
-
async *
|
|
431
|
+
async *deleteTrashedNodes(nodeUids: string[], signal?: AbortSignal): AsyncGenerator<NodeResult> {
|
|
434
432
|
for (const { volumeId, batchNodeIds, batchNodeUids } of groupNodeUidsByVolumeAndIteratePerBatch(nodeUids)) {
|
|
435
433
|
const response = await this.apiService.post<PostDeleteNodesRequest, PostDeleteNodesResponse>(
|
|
436
434
|
`drive/v2/volumes/${volumeId}/trash/delete_multiple`,
|
|
@@ -445,6 +443,21 @@ export class NodeAPIService {
|
|
|
445
443
|
}
|
|
446
444
|
}
|
|
447
445
|
|
|
446
|
+
async *deleteExistingNodes(nodeUids: string[], signal?: AbortSignal): AsyncGenerator<NodeResult> {
|
|
447
|
+
for (const { volumeId, batchNodeIds, batchNodeUids } of groupNodeUidsByVolumeAndIteratePerBatch(nodeUids)) {
|
|
448
|
+
const response = await this.apiService.post<PostDeleteNodesRequest, PostDeleteNodesResponse>(
|
|
449
|
+
`drive/v2/volumes/${volumeId}/delete_multiple`,
|
|
450
|
+
{
|
|
451
|
+
LinkIDs: batchNodeIds,
|
|
452
|
+
},
|
|
453
|
+
signal,
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// TODO: remove `as` when backend fixes OpenAPI schema.
|
|
457
|
+
yield* handleResponseErrors(batchNodeUids, volumeId, response.Responses as LinkResponse[]);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
448
461
|
async createFolder(
|
|
449
462
|
parentUid: string,
|
|
450
463
|
newNode: {
|
|
@@ -452,7 +465,7 @@ export class NodeAPIService {
|
|
|
452
465
|
armoredHashKey: string;
|
|
453
466
|
armoredNodePassphrase: string;
|
|
454
467
|
armoredNodePassphraseSignature: string;
|
|
455
|
-
signatureEmail: string;
|
|
468
|
+
signatureEmail: string | AnonymousUser;
|
|
456
469
|
encryptedName: string;
|
|
457
470
|
hash: string;
|
|
458
471
|
armoredExtendedAttributes?: string;
|
|
@@ -34,7 +34,7 @@ export class NodesCryptoReporter {
|
|
|
34
34
|
signatureType: string,
|
|
35
35
|
verified: VERIFICATION_STATUS,
|
|
36
36
|
verificationErrors?: Error[],
|
|
37
|
-
claimedAuthor?: string,
|
|
37
|
+
claimedAuthor?: string | AnonymousUser,
|
|
38
38
|
notAvailableVerificationKeys = false,
|
|
39
39
|
): Promise<Author> {
|
|
40
40
|
const author = handleClaimedAuthor(
|
|
@@ -54,7 +54,7 @@ export class NodesCryptoReporter {
|
|
|
54
54
|
node: { uid: string; creationTime: Date },
|
|
55
55
|
field: MetricVerificationErrorField,
|
|
56
56
|
verificationErrors?: Error[],
|
|
57
|
-
claimedAuthor?: string,
|
|
57
|
+
claimedAuthor?: string | AnonymousUser,
|
|
58
58
|
) {
|
|
59
59
|
if (this.reportedVerificationErrors.has(node.uid)) {
|
|
60
60
|
return;
|
|
@@ -128,7 +128,7 @@ function handleClaimedAuthor(
|
|
|
128
128
|
signatureType: string,
|
|
129
129
|
verified: VERIFICATION_STATUS,
|
|
130
130
|
verificationErrors?: Error[],
|
|
131
|
-
claimedAuthor?: string,
|
|
131
|
+
claimedAuthor?: string | AnonymousUser,
|
|
132
132
|
notAvailableVerificationKeys = false,
|
|
133
133
|
): Author {
|
|
134
134
|
if (!claimedAuthor && notAvailableVerificationKeys) {
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { DriveCrypto, PrivateKey, SessionKey, VERIFICATION_STATUS } from '../../crypto';
|
|
2
2
|
import { MemberRole, ProtonDriveAccount, ProtonDriveTelemetry, RevisionState } from '../../interface';
|
|
3
3
|
import { getMockTelemetry } from '../../tests/telemetry';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DecryptedNode,
|
|
6
|
+
DecryptedNodeKeys,
|
|
7
|
+
DecryptedUnparsedNode,
|
|
8
|
+
EncryptedNode,
|
|
9
|
+
NodeSigningKeys,
|
|
10
|
+
SharesService,
|
|
11
|
+
} from './interface';
|
|
5
12
|
import { NodesCryptoService } from './cryptoService';
|
|
6
13
|
import { NodesCryptoReporter } from './cryptoReporter';
|
|
7
14
|
|
|
@@ -250,6 +257,48 @@ describe('nodesCryptoService', () => {
|
|
|
250
257
|
});
|
|
251
258
|
});
|
|
252
259
|
|
|
260
|
+
it('on older node name ignores NOT_SIGNED', async () => {
|
|
261
|
+
encryptedNode.creationTime = new Date('2020-12-31');
|
|
262
|
+
driveCrypto.decryptNodeName = jest.fn(async () =>
|
|
263
|
+
Promise.resolve({
|
|
264
|
+
name: 'name',
|
|
265
|
+
verified: VERIFICATION_STATUS.NOT_SIGNED,
|
|
266
|
+
verificationErrors: [new Error('missing signature')],
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const result = await cryptoService.decryptNode(encryptedNode, parentKey);
|
|
271
|
+
verifyResult(result, {
|
|
272
|
+
nameAuthor: {
|
|
273
|
+
ok: true,
|
|
274
|
+
value: 'nameSignatureEmail',
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('on newer node name does not ignore NOT_SIGNED', async () => {
|
|
281
|
+
encryptedNode.creationTime = new Date('2021-01-01');
|
|
282
|
+
driveCrypto.decryptNodeName = jest.fn(async () =>
|
|
283
|
+
Promise.resolve({
|
|
284
|
+
name: 'name',
|
|
285
|
+
verified: VERIFICATION_STATUS.NOT_SIGNED,
|
|
286
|
+
verificationErrors: [new Error('missing signature')],
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const result = await cryptoService.decryptNode(encryptedNode, parentKey);
|
|
291
|
+
verifyResult(result, {
|
|
292
|
+
nameAuthor: {
|
|
293
|
+
ok: false,
|
|
294
|
+
error: {
|
|
295
|
+
claimedAuthor: 'nameSignatureEmail',
|
|
296
|
+
error: 'Missing signature for name',
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
253
302
|
it('on hash key', async () => {
|
|
254
303
|
driveCrypto.decryptNodeHashKey = jest.fn(async () =>
|
|
255
304
|
Promise.resolve({
|
|
@@ -275,6 +324,48 @@ describe('nodesCryptoService', () => {
|
|
|
275
324
|
});
|
|
276
325
|
});
|
|
277
326
|
|
|
327
|
+
it('on older node hash key ignores NOT_SIGNED', async () => {
|
|
328
|
+
encryptedNode.creationTime = new Date('2021-07-31');
|
|
329
|
+
driveCrypto.decryptNodeHashKey = jest.fn(async () =>
|
|
330
|
+
Promise.resolve({
|
|
331
|
+
hashKey: new Uint8Array(),
|
|
332
|
+
verified: VERIFICATION_STATUS.NOT_SIGNED,
|
|
333
|
+
verificationErrors: [new Error('missing signature')],
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
const result = await cryptoService.decryptNode(encryptedNode, parentKey);
|
|
338
|
+
verifyResult(result, {
|
|
339
|
+
keyAuthor: {
|
|
340
|
+
ok: true,
|
|
341
|
+
value: 'signatureEmail',
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
expect(telemetry.recordMetric).not.toHaveBeenCalled();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('on newer node hash key does not ignore NOT_SIGNED', async () => {
|
|
348
|
+
encryptedNode.creationTime = new Date('2021-08-01');
|
|
349
|
+
driveCrypto.decryptNodeHashKey = jest.fn(async () =>
|
|
350
|
+
Promise.resolve({
|
|
351
|
+
hashKey: new Uint8Array(),
|
|
352
|
+
verified: VERIFICATION_STATUS.NOT_SIGNED,
|
|
353
|
+
verificationErrors: [new Error('missing signature')],
|
|
354
|
+
}),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const result = await cryptoService.decryptNode(encryptedNode, parentKey);
|
|
358
|
+
verifyResult(result, {
|
|
359
|
+
keyAuthor: {
|
|
360
|
+
ok: false,
|
|
361
|
+
error: {
|
|
362
|
+
claimedAuthor: 'signatureEmail',
|
|
363
|
+
error: 'Missing signature for hash key',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
278
369
|
it('on node key and hash key reports error from node key', async () => {
|
|
279
370
|
driveCrypto.decryptKey = jest.fn(async () =>
|
|
280
371
|
Promise.resolve({
|
|
@@ -985,24 +1076,239 @@ describe('nodesCryptoService', () => {
|
|
|
985
1076
|
});
|
|
986
1077
|
});
|
|
987
1078
|
|
|
1079
|
+
describe('createFolder', () => {
|
|
1080
|
+
let parentKeys: any;
|
|
1081
|
+
|
|
1082
|
+
beforeEach(() => {
|
|
1083
|
+
parentKeys = {
|
|
1084
|
+
key: 'parentKey' as any,
|
|
1085
|
+
hashKey: new Uint8Array([1, 2, 3]),
|
|
1086
|
+
};
|
|
1087
|
+
driveCrypto.generateKey = jest.fn().mockResolvedValue({
|
|
1088
|
+
encrypted: {
|
|
1089
|
+
armoredKey: 'encryptedNodeKey',
|
|
1090
|
+
armoredPassphrase: 'encryptedPassphrase',
|
|
1091
|
+
armoredPassphraseSignature: 'passphraseSignature',
|
|
1092
|
+
},
|
|
1093
|
+
decrypted: {
|
|
1094
|
+
key: 'nodeKey' as any,
|
|
1095
|
+
passphrase: 'nodePassphrase',
|
|
1096
|
+
passphraseSessionKey: 'passphraseSessionKey' as any,
|
|
1097
|
+
},
|
|
1098
|
+
});
|
|
1099
|
+
driveCrypto.encryptNodeName = jest.fn().mockResolvedValue({
|
|
1100
|
+
armoredNodeName: 'encryptedNodeName',
|
|
1101
|
+
});
|
|
1102
|
+
driveCrypto.generateLookupHash = jest.fn().mockResolvedValue('lookupHash');
|
|
1103
|
+
driveCrypto.generateHashKey = jest.fn().mockResolvedValue({
|
|
1104
|
+
armoredHashKey: 'encryptedHashKey',
|
|
1105
|
+
hashKey: new Uint8Array([4, 5, 6]),
|
|
1106
|
+
});
|
|
1107
|
+
driveCrypto.encryptExtendedAttributes = jest.fn().mockResolvedValue({
|
|
1108
|
+
armoredExtendedAttributes: 'encryptedAttributes',
|
|
1109
|
+
});
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
it('should encrypt new folder with account key', async () => {
|
|
1113
|
+
const signingKeys: NodeSigningKeys = {
|
|
1114
|
+
type: 'userAddress',
|
|
1115
|
+
email: 'test@example.com',
|
|
1116
|
+
addressId: 'addressId',
|
|
1117
|
+
key: 'addressKey' as any,
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
const result = await cryptoService.createFolder(
|
|
1121
|
+
parentKeys,
|
|
1122
|
+
signingKeys,
|
|
1123
|
+
'New Folder',
|
|
1124
|
+
'{"modificationTime": 1234567890}',
|
|
1125
|
+
);
|
|
1126
|
+
|
|
1127
|
+
expect(result).toEqual({
|
|
1128
|
+
encryptedCrypto: {
|
|
1129
|
+
encryptedName: 'encryptedNodeName',
|
|
1130
|
+
hash: 'lookupHash',
|
|
1131
|
+
armoredKey: 'encryptedNodeKey',
|
|
1132
|
+
armoredNodePassphrase: 'encryptedPassphrase',
|
|
1133
|
+
armoredNodePassphraseSignature: 'passphraseSignature',
|
|
1134
|
+
folder: {
|
|
1135
|
+
armoredExtendedAttributes: 'encryptedAttributes',
|
|
1136
|
+
armoredHashKey: 'encryptedHashKey',
|
|
1137
|
+
},
|
|
1138
|
+
signatureEmail: 'test@example.com',
|
|
1139
|
+
nameSignatureEmail: 'test@example.com',
|
|
1140
|
+
},
|
|
1141
|
+
keys: {
|
|
1142
|
+
passphrase: 'nodePassphrase',
|
|
1143
|
+
key: 'nodeKey',
|
|
1144
|
+
passphraseSessionKey: 'passphraseSessionKey',
|
|
1145
|
+
hashKey: new Uint8Array([4, 5, 6]),
|
|
1146
|
+
},
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
expect(driveCrypto.generateKey).toHaveBeenCalledWith([parentKeys.key], signingKeys.key);
|
|
1150
|
+
expect(driveCrypto.encryptNodeName).toHaveBeenCalledWith(
|
|
1151
|
+
'New Folder',
|
|
1152
|
+
undefined,
|
|
1153
|
+
parentKeys.key,
|
|
1154
|
+
signingKeys.key,
|
|
1155
|
+
);
|
|
1156
|
+
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('New Folder', parentKeys.hashKey);
|
|
1157
|
+
expect(driveCrypto.generateHashKey).toHaveBeenCalledWith('nodeKey');
|
|
1158
|
+
expect(driveCrypto.encryptExtendedAttributes).toHaveBeenCalledWith(
|
|
1159
|
+
'{"modificationTime": 1234567890}',
|
|
1160
|
+
'nodeKey',
|
|
1161
|
+
signingKeys.key,
|
|
1162
|
+
);
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
it('should encrypt new folder with node key', async () => {
|
|
1166
|
+
const signingKeys: NodeSigningKeys = {
|
|
1167
|
+
type: 'nodeKey',
|
|
1168
|
+
nodeKey: 'nodeSigningKey' as any,
|
|
1169
|
+
parentNodeKey: 'parentNodeKey' as any,
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
const result = await cryptoService.createFolder(
|
|
1173
|
+
parentKeys,
|
|
1174
|
+
signingKeys,
|
|
1175
|
+
'New Folder',
|
|
1176
|
+
'{"modificationTime": 1234567890}',
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
expect(result).toEqual({
|
|
1180
|
+
encryptedCrypto: {
|
|
1181
|
+
encryptedName: 'encryptedNodeName',
|
|
1182
|
+
hash: 'lookupHash',
|
|
1183
|
+
armoredKey: 'encryptedNodeKey',
|
|
1184
|
+
armoredNodePassphrase: 'encryptedPassphrase',
|
|
1185
|
+
armoredNodePassphraseSignature: 'passphraseSignature',
|
|
1186
|
+
folder: {
|
|
1187
|
+
armoredExtendedAttributes: 'encryptedAttributes',
|
|
1188
|
+
armoredHashKey: 'encryptedHashKey',
|
|
1189
|
+
},
|
|
1190
|
+
signatureEmail: null,
|
|
1191
|
+
nameSignatureEmail: null,
|
|
1192
|
+
},
|
|
1193
|
+
keys: {
|
|
1194
|
+
passphrase: 'nodePassphrase',
|
|
1195
|
+
key: 'nodeKey',
|
|
1196
|
+
passphraseSessionKey: 'passphraseSessionKey',
|
|
1197
|
+
hashKey: new Uint8Array([4, 5, 6]),
|
|
1198
|
+
},
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
expect(driveCrypto.generateKey).toHaveBeenCalledWith([parentKeys.key], signingKeys.parentNodeKey);
|
|
1202
|
+
expect(driveCrypto.encryptNodeName).toHaveBeenCalledWith(
|
|
1203
|
+
'New Folder',
|
|
1204
|
+
undefined,
|
|
1205
|
+
parentKeys.key,
|
|
1206
|
+
signingKeys.parentNodeKey,
|
|
1207
|
+
);
|
|
1208
|
+
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('New Folder', parentKeys.hashKey);
|
|
1209
|
+
expect(driveCrypto.generateHashKey).toHaveBeenCalledWith('nodeKey');
|
|
1210
|
+
expect(driveCrypto.encryptExtendedAttributes).toHaveBeenCalledWith(
|
|
1211
|
+
'{"modificationTime": 1234567890}',
|
|
1212
|
+
'nodeKey',
|
|
1213
|
+
signingKeys.nodeKey,
|
|
1214
|
+
);
|
|
1215
|
+
});
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
describe('encryptNewName', () => {
|
|
1219
|
+
let parentKeys: any;
|
|
1220
|
+
let nodeNameSessionKey: SessionKey;
|
|
1221
|
+
|
|
1222
|
+
beforeEach(() => {
|
|
1223
|
+
parentKeys = {
|
|
1224
|
+
key: 'parentKey' as any,
|
|
1225
|
+
hashKey: new Uint8Array([1, 2, 3]),
|
|
1226
|
+
};
|
|
1227
|
+
nodeNameSessionKey = 'nameSessionKey' as any;
|
|
1228
|
+
driveCrypto.encryptNodeName = jest.fn().mockResolvedValue({
|
|
1229
|
+
armoredNodeName: 'encryptedNewNodeName',
|
|
1230
|
+
});
|
|
1231
|
+
driveCrypto.generateLookupHash = jest.fn().mockResolvedValue('newHash');
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1234
|
+
it('should encrypt new name with account key', async () => {
|
|
1235
|
+
const signingKeys: NodeSigningKeys = {
|
|
1236
|
+
type: 'userAddress',
|
|
1237
|
+
email: 'test@example.com',
|
|
1238
|
+
addressId: 'addressId',
|
|
1239
|
+
key: 'addressKey' as any,
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
const result = await cryptoService.encryptNewName(
|
|
1243
|
+
parentKeys,
|
|
1244
|
+
nodeNameSessionKey,
|
|
1245
|
+
signingKeys,
|
|
1246
|
+
'Renamed File.txt',
|
|
1247
|
+
);
|
|
1248
|
+
|
|
1249
|
+
expect(result).toEqual({
|
|
1250
|
+
signatureEmail: 'test@example.com',
|
|
1251
|
+
armoredNodeName: 'encryptedNewNodeName',
|
|
1252
|
+
hash: 'newHash',
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
expect(driveCrypto.encryptNodeName).toHaveBeenCalledWith(
|
|
1256
|
+
'Renamed File.txt',
|
|
1257
|
+
nodeNameSessionKey,
|
|
1258
|
+
parentKeys.key,
|
|
1259
|
+
signingKeys.key,
|
|
1260
|
+
);
|
|
1261
|
+
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('Renamed File.txt', parentKeys.hashKey);
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
it('should encrypt new name with node key', async () => {
|
|
1265
|
+
const signingKeys: NodeSigningKeys = {
|
|
1266
|
+
type: 'nodeKey',
|
|
1267
|
+
nodeKey: 'nodeSigningKey' as any,
|
|
1268
|
+
parentNodeKey: 'parentNodeKey' as any,
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
const result = await cryptoService.encryptNewName(
|
|
1272
|
+
parentKeys,
|
|
1273
|
+
nodeNameSessionKey,
|
|
1274
|
+
signingKeys,
|
|
1275
|
+
'Renamed File.txt',
|
|
1276
|
+
);
|
|
1277
|
+
|
|
1278
|
+
expect(result).toEqual({
|
|
1279
|
+
signatureEmail: null,
|
|
1280
|
+
armoredNodeName: 'encryptedNewNodeName',
|
|
1281
|
+
hash: 'newHash',
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
expect(driveCrypto.encryptNodeName).toHaveBeenCalledWith(
|
|
1285
|
+
'Renamed File.txt',
|
|
1286
|
+
nodeNameSessionKey,
|
|
1287
|
+
parentKeys.key,
|
|
1288
|
+
signingKeys.parentNodeKey,
|
|
1289
|
+
);
|
|
1290
|
+
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('Renamed File.txt', parentKeys.hashKey);
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
|
|
988
1294
|
describe('encryptNodeWithNewParent', () => {
|
|
989
|
-
|
|
990
|
-
|
|
1295
|
+
let node: DecryptedNode;
|
|
1296
|
+
let keys: any;
|
|
1297
|
+
let parentKeys: any;
|
|
1298
|
+
|
|
1299
|
+
beforeEach(() => {
|
|
1300
|
+
node = {
|
|
991
1301
|
name: { ok: true, value: 'testFile.txt' },
|
|
992
1302
|
} as DecryptedNode;
|
|
993
|
-
|
|
1303
|
+
keys = {
|
|
994
1304
|
passphrase: 'nodePassphrase',
|
|
995
1305
|
passphraseSessionKey: 'nodePassphraseSessionKey',
|
|
996
1306
|
nameSessionKey: 'nameSessionKey' as any,
|
|
997
1307
|
};
|
|
998
|
-
|
|
1308
|
+
parentKeys = {
|
|
999
1309
|
key: 'newParentKey' as any,
|
|
1000
1310
|
hashKey: new Uint8Array([1, 2, 3]),
|
|
1001
1311
|
};
|
|
1002
|
-
const address = {
|
|
1003
|
-
email: 'test@example.com',
|
|
1004
|
-
addressKey: 'addressKey' as any,
|
|
1005
|
-
};
|
|
1006
1312
|
driveCrypto.encryptNodeName = jest.fn().mockResolvedValue({
|
|
1007
1313
|
armoredNodeName: 'encryptedNodeName',
|
|
1008
1314
|
});
|
|
@@ -1011,8 +1317,17 @@ describe('nodesCryptoService', () => {
|
|
|
1011
1317
|
armoredPassphrase: 'encryptedPassphrase',
|
|
1012
1318
|
armoredPassphraseSignature: 'passphraseSignature',
|
|
1013
1319
|
});
|
|
1320
|
+
});
|
|
1014
1321
|
|
|
1015
|
-
|
|
1322
|
+
it('should encrypt node data for move operation with account key (logged in context)', async () => {
|
|
1323
|
+
const signingKeys: NodeSigningKeys = {
|
|
1324
|
+
type: 'userAddress',
|
|
1325
|
+
email: 'test@example.com',
|
|
1326
|
+
addressId: 'addressId',
|
|
1327
|
+
key: 'addressKey' as any,
|
|
1328
|
+
};
|
|
1329
|
+
|
|
1330
|
+
const result = await cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys, signingKeys);
|
|
1016
1331
|
|
|
1017
1332
|
expect(result).toEqual({
|
|
1018
1333
|
encryptedName: 'encryptedNodeName',
|
|
@@ -1027,14 +1342,47 @@ describe('nodesCryptoService', () => {
|
|
|
1027
1342
|
'testFile.txt',
|
|
1028
1343
|
keys.nameSessionKey,
|
|
1029
1344
|
parentKeys.key,
|
|
1030
|
-
|
|
1345
|
+
signingKeys.key,
|
|
1346
|
+
);
|
|
1347
|
+
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('testFile.txt', parentKeys.hashKey);
|
|
1348
|
+
expect(driveCrypto.encryptPassphrase).toHaveBeenCalledWith(
|
|
1349
|
+
keys.passphrase,
|
|
1350
|
+
keys.passphraseSessionKey,
|
|
1351
|
+
[parentKeys.key],
|
|
1352
|
+
signingKeys.key,
|
|
1353
|
+
);
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
it('should encrypt node data for move operation with node key (anonymous context)', async () => {
|
|
1357
|
+
const signingKeys: NodeSigningKeys = {
|
|
1358
|
+
type: 'nodeKey',
|
|
1359
|
+
nodeKey: 'addressKey' as any,
|
|
1360
|
+
parentNodeKey: 'parentNodeKey' as any,
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1363
|
+
const result = await cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys, signingKeys);
|
|
1364
|
+
|
|
1365
|
+
expect(result).toEqual({
|
|
1366
|
+
encryptedName: 'encryptedNodeName',
|
|
1367
|
+
hash: 'newHash',
|
|
1368
|
+
armoredNodePassphrase: 'encryptedPassphrase',
|
|
1369
|
+
armoredNodePassphraseSignature: 'passphraseSignature',
|
|
1370
|
+
signatureEmail: null,
|
|
1371
|
+
nameSignatureEmail: null,
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
expect(driveCrypto.encryptNodeName).toHaveBeenCalledWith(
|
|
1375
|
+
'testFile.txt',
|
|
1376
|
+
keys.nameSessionKey,
|
|
1377
|
+
parentKeys.key,
|
|
1378
|
+
signingKeys.nodeKey,
|
|
1031
1379
|
);
|
|
1032
1380
|
expect(driveCrypto.generateLookupHash).toHaveBeenCalledWith('testFile.txt', parentKeys.hashKey);
|
|
1033
1381
|
expect(driveCrypto.encryptPassphrase).toHaveBeenCalledWith(
|
|
1034
1382
|
keys.passphrase,
|
|
1035
1383
|
keys.passphraseSessionKey,
|
|
1036
1384
|
[parentKeys.key],
|
|
1037
|
-
|
|
1385
|
+
signingKeys.nodeKey,
|
|
1038
1386
|
);
|
|
1039
1387
|
});
|
|
1040
1388
|
|
|
@@ -1051,13 +1399,15 @@ describe('nodesCryptoService', () => {
|
|
|
1051
1399
|
key: 'newParentKey' as any,
|
|
1052
1400
|
hashKey: undefined,
|
|
1053
1401
|
} as any;
|
|
1054
|
-
const
|
|
1402
|
+
const signingKeys: NodeSigningKeys = {
|
|
1403
|
+
type: 'userAddress',
|
|
1055
1404
|
email: 'test@example.com',
|
|
1056
|
-
|
|
1405
|
+
addressId: 'addressId',
|
|
1406
|
+
key: 'addressKey' as any,
|
|
1057
1407
|
};
|
|
1058
1408
|
|
|
1059
1409
|
await expect(
|
|
1060
|
-
cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys,
|
|
1410
|
+
cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys, signingKeys),
|
|
1061
1411
|
).rejects.toThrow('Moving item to a non-folder is not allowed');
|
|
1062
1412
|
});
|
|
1063
1413
|
|
|
@@ -1074,13 +1424,15 @@ describe('nodesCryptoService', () => {
|
|
|
1074
1424
|
key: 'newParentKey' as any,
|
|
1075
1425
|
hashKey: new Uint8Array([1, 2, 3]),
|
|
1076
1426
|
};
|
|
1077
|
-
const
|
|
1427
|
+
const signingKeys: NodeSigningKeys = {
|
|
1428
|
+
type: 'userAddress',
|
|
1078
1429
|
email: 'test@example.com',
|
|
1079
|
-
|
|
1430
|
+
addressId: 'addressId',
|
|
1431
|
+
key: 'addressKey' as any,
|
|
1080
1432
|
};
|
|
1081
1433
|
|
|
1082
1434
|
await expect(
|
|
1083
|
-
cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys,
|
|
1435
|
+
cryptoService.encryptNodeWithNewParent(node, keys as any, parentKeys, signingKeys),
|
|
1084
1436
|
).rejects.toThrow('Cannot move item without a valid name, please rename the item first');
|
|
1085
1437
|
});
|
|
1086
1438
|
});
|