@protontech/drive-sdk 0.3.1 → 0.4.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/crypto/driveCrypto.d.ts +1 -1
- package/dist/crypto/driveCrypto.js.map +1 -1
- package/dist/crypto/interface.d.ts +1 -1
- package/dist/crypto/openPGPCrypto.d.ts +1 -1
- package/dist/crypto/openPGPCrypto.js +4 -1
- package/dist/crypto/openPGPCrypto.js.map +1 -1
- package/dist/internal/apiService/errorCodes.d.ts +1 -0
- package/dist/internal/apiService/errors.d.ts +3 -0
- package/dist/internal/apiService/errors.js +7 -1
- package/dist/internal/apiService/errors.js.map +1 -1
- package/dist/internal/devices/interface.d.ts +1 -1
- package/dist/internal/devices/manager.js +1 -1
- package/dist/internal/devices/manager.js.map +1 -1
- package/dist/internal/devices/manager.test.js +3 -3
- package/dist/internal/devices/manager.test.js.map +1 -1
- package/dist/internal/download/cryptoService.js +2 -2
- package/dist/internal/download/cryptoService.js.map +1 -1
- package/dist/internal/download/fileDownloader.js +2 -2
- package/dist/internal/download/fileDownloader.js.map +1 -1
- package/dist/internal/download/fileDownloader.test.js +3 -1
- package/dist/internal/download/fileDownloader.test.js.map +1 -1
- package/dist/internal/events/apiService.js +1 -1
- package/dist/internal/events/apiService.js.map +1 -1
- package/dist/internal/events/coreEventManager.js +1 -1
- package/dist/internal/events/coreEventManager.js.map +1 -1
- package/dist/internal/events/coreEventManager.test.js +18 -24
- package/dist/internal/events/coreEventManager.test.js.map +1 -1
- package/dist/internal/events/index.d.ts +3 -4
- package/dist/internal/events/index.js +4 -4
- package/dist/internal/events/index.js.map +1 -1
- package/dist/internal/events/interface.d.ts +3 -0
- package/dist/internal/nodes/apiService.d.ts +12 -3
- package/dist/internal/nodes/apiService.js +53 -13
- package/dist/internal/nodes/apiService.js.map +1 -1
- package/dist/internal/nodes/apiService.test.js +19 -2
- package/dist/internal/nodes/apiService.test.js.map +1 -1
- package/dist/internal/nodes/cache.js +3 -1
- package/dist/internal/nodes/cache.js.map +1 -1
- package/dist/internal/nodes/cryptoReporter.d.ts +20 -0
- package/dist/internal/nodes/cryptoReporter.js +96 -0
- package/dist/internal/nodes/cryptoReporter.js.map +1 -0
- package/dist/internal/nodes/cryptoService.d.ts +18 -13
- package/dist/internal/nodes/cryptoService.js +18 -98
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/cryptoService.test.js +7 -5
- package/dist/internal/nodes/cryptoService.test.js.map +1 -1
- package/dist/internal/nodes/errors.d.ts +4 -0
- package/dist/internal/nodes/errors.js +9 -0
- package/dist/internal/nodes/errors.js.map +1 -0
- package/dist/internal/nodes/index.js +3 -1
- package/dist/internal/nodes/index.js.map +1 -1
- package/dist/internal/nodes/index.test.js +1 -1
- package/dist/internal/nodes/index.test.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +5 -2
- package/dist/internal/nodes/nodesAccess.d.ts +4 -4
- package/dist/internal/nodes/nodesAccess.js +77 -69
- package/dist/internal/nodes/nodesAccess.js.map +1 -1
- package/dist/internal/nodes/nodesAccess.test.js +48 -8
- package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.d.ts +2 -0
- package/dist/internal/nodes/nodesManagement.js +86 -9
- package/dist/internal/nodes/nodesManagement.js.map +1 -1
- package/dist/internal/nodes/nodesManagement.test.js +81 -5
- package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
- package/dist/internal/photos/albums.d.ts +9 -7
- package/dist/internal/photos/albums.js +26 -13
- package/dist/internal/photos/albums.js.map +1 -1
- package/dist/internal/photos/apiService.d.ts +34 -3
- package/dist/internal/photos/apiService.js +96 -3
- package/dist/internal/photos/apiService.js.map +1 -1
- package/dist/internal/photos/index.d.ts +20 -4
- package/dist/internal/photos/index.js +30 -7
- package/dist/internal/photos/index.js.map +1 -1
- package/dist/internal/photos/interface.d.ts +25 -1
- package/dist/internal/photos/shares.d.ts +43 -0
- package/dist/internal/photos/shares.js +112 -0
- package/dist/internal/photos/shares.js.map +1 -0
- package/dist/internal/photos/timeline.d.ts +15 -0
- package/dist/internal/photos/timeline.js +22 -0
- package/dist/internal/photos/timeline.js.map +1 -0
- package/dist/internal/shares/manager.d.ts +1 -1
- package/dist/internal/shares/manager.js +4 -4
- package/dist/internal/shares/manager.js.map +1 -1
- package/dist/internal/shares/manager.test.js +7 -7
- package/dist/internal/shares/manager.test.js.map +1 -1
- package/dist/internal/sharing/cache.d.ts +3 -0
- package/dist/internal/sharing/cache.js +17 -2
- package/dist/internal/sharing/cache.js.map +1 -1
- package/dist/internal/sharing/interface.d.ts +2 -2
- package/dist/internal/sharing/interface.js +1 -1
- package/dist/internal/sharing/sharingAccess.js +7 -1
- package/dist/internal/sharing/sharingAccess.js.map +1 -1
- package/dist/internal/sharing/sharingAccess.test.js +243 -34
- package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
- package/dist/internal/sharingPublic/apiService.d.ts +1 -1
- package/dist/internal/sharingPublic/apiService.js +9 -2
- package/dist/internal/sharingPublic/apiService.js.map +1 -1
- package/dist/internal/sharingPublic/cryptoService.d.ts +6 -20
- package/dist/internal/sharingPublic/cryptoService.js +40 -103
- package/dist/internal/sharingPublic/cryptoService.js.map +1 -1
- package/dist/internal/sharingPublic/index.d.ts +2 -2
- package/dist/internal/sharingPublic/index.js +2 -2
- package/dist/internal/sharingPublic/index.js.map +1 -1
- package/dist/internal/sharingPublic/interface.d.ts +1 -43
- package/dist/internal/sharingPublic/manager.d.ts +1 -1
- package/dist/internal/sharingPublic/manager.js +9 -7
- package/dist/internal/sharingPublic/manager.js.map +1 -1
- package/dist/internal/upload/streamUploader.js +1 -1
- package/dist/internal/upload/streamUploader.js.map +1 -1
- package/dist/internal/upload/streamUploader.test.js +3 -1
- package/dist/internal/upload/streamUploader.test.js.map +1 -1
- package/dist/protonDriveClient.d.ts +20 -3
- package/dist/protonDriveClient.js +24 -4
- package/dist/protonDriveClient.js.map +1 -1
- package/dist/protonDrivePhotosClient.d.ts +86 -12
- package/dist/protonDrivePhotosClient.js +132 -29
- package/dist/protonDrivePhotosClient.js.map +1 -1
- package/dist/protonDrivePublicLinkClient.d.ts +13 -4
- package/dist/protonDrivePublicLinkClient.js +13 -11
- package/dist/protonDrivePublicLinkClient.js.map +1 -1
- package/package.json +1 -1
- package/src/crypto/driveCrypto.ts +1 -1
- package/src/crypto/interface.ts +1 -1
- package/src/crypto/openPGPCrypto.ts +5 -2
- package/src/internal/apiService/errorCodes.ts +1 -0
- package/src/internal/apiService/errors.ts +6 -0
- package/src/internal/devices/interface.ts +1 -1
- package/src/internal/devices/manager.test.ts +3 -3
- package/src/internal/devices/manager.ts +1 -1
- package/src/internal/download/cryptoService.ts +2 -2
- package/src/internal/download/fileDownloader.test.ts +3 -1
- package/src/internal/download/fileDownloader.ts +2 -2
- package/src/internal/events/apiService.ts +1 -1
- package/src/internal/events/coreEventManager.test.ts +21 -27
- package/src/internal/events/coreEventManager.ts +1 -1
- package/src/internal/events/index.ts +3 -4
- package/src/internal/events/interface.ts +4 -0
- package/src/internal/nodes/apiService.test.ts +35 -1
- package/src/internal/nodes/apiService.ts +103 -17
- package/src/internal/nodes/cache.ts +3 -1
- package/src/internal/nodes/cryptoReporter.ts +145 -0
- package/src/internal/nodes/cryptoService.test.ts +11 -9
- package/src/internal/nodes/cryptoService.ts +45 -138
- package/src/internal/nodes/errors.ts +5 -0
- package/src/internal/nodes/index.test.ts +1 -1
- package/src/internal/nodes/index.ts +3 -1
- package/src/internal/nodes/interface.ts +6 -2
- package/src/internal/nodes/nodesAccess.test.ts +68 -8
- package/src/internal/nodes/nodesAccess.ts +101 -76
- package/src/internal/nodes/nodesManagement.test.ts +100 -5
- package/src/internal/nodes/nodesManagement.ts +100 -13
- package/src/internal/photos/albums.ts +31 -12
- package/src/internal/photos/apiService.ts +159 -4
- package/src/internal/photos/index.ts +54 -9
- package/src/internal/photos/interface.ts +23 -1
- package/src/internal/photos/shares.ts +134 -0
- package/src/internal/photos/timeline.ts +24 -0
- package/src/internal/shares/manager.test.ts +7 -7
- package/src/internal/shares/manager.ts +4 -4
- package/src/internal/sharing/cache.ts +19 -2
- package/src/internal/sharing/interface.ts +2 -2
- package/src/internal/sharing/sharingAccess.test.ts +283 -35
- package/src/internal/sharing/sharingAccess.ts +7 -1
- package/src/internal/sharingPublic/apiService.ts +11 -2
- package/src/internal/sharingPublic/cryptoService.ts +71 -135
- package/src/internal/sharingPublic/index.ts +3 -2
- package/src/internal/sharingPublic/interface.ts +8 -53
- package/src/internal/sharingPublic/manager.ts +9 -8
- package/src/internal/upload/streamUploader.test.ts +3 -1
- package/src/internal/upload/streamUploader.ts +1 -1
- package/src/protonDriveClient.ts +34 -4
- package/src/protonDrivePhotosClient.ts +211 -32
- package/src/protonDrivePublicLinkClient.ts +26 -12
- package/dist/internal/photos/cache.d.ts +0 -6
- package/dist/internal/photos/cache.js +0 -15
- package/dist/internal/photos/cache.js.map +0 -1
- package/dist/internal/photos/photosTimeline.d.ts +0 -10
- package/dist/internal/photos/photosTimeline.js +0 -19
- package/dist/internal/photos/photosTimeline.js.map +0 -1
- package/src/internal/photos/cache.ts +0 -11
- package/src/internal/photos/photosTimeline.ts +0 -17
|
@@ -1,26 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { c } from 'ttag';
|
|
2
|
+
|
|
3
|
+
import { DriveCrypto, PrivateKey, VERIFICATION_STATUS } from '../../crypto';
|
|
4
|
+
import { getVerificationMessage } from '../errors';
|
|
5
|
+
import {
|
|
6
|
+
resultOk,
|
|
7
|
+
resultError,
|
|
8
|
+
Author,
|
|
9
|
+
AnonymousUser,
|
|
10
|
+
ProtonDriveTelemetry,
|
|
11
|
+
MetricVerificationErrorField,
|
|
12
|
+
MetricVolumeType,
|
|
13
|
+
MetricsDecryptionErrorField,
|
|
14
|
+
Logger,
|
|
15
|
+
ProtonDriveAccount,
|
|
16
|
+
} from '../../interface';
|
|
17
|
+
import { NodesCryptoService } from '../nodes/cryptoService';
|
|
18
|
+
import { EncryptedShareCrypto } from './interface';
|
|
19
|
+
|
|
20
|
+
export class SharingPublicCryptoService extends NodesCryptoService {
|
|
15
21
|
constructor(
|
|
16
|
-
|
|
22
|
+
telemetry: ProtonDriveTelemetry,
|
|
23
|
+
driveCrypto: DriveCrypto,
|
|
24
|
+
account: ProtonDriveAccount,
|
|
17
25
|
private password: string,
|
|
18
26
|
) {
|
|
19
|
-
|
|
27
|
+
super(telemetry, driveCrypto, account, new SharingPublicCryptoReporter(telemetry));
|
|
20
28
|
this.password = password;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
async
|
|
31
|
+
async decryptPublicLinkShareKey(encryptedShare: EncryptedShareCrypto): Promise<PrivateKey> {
|
|
24
32
|
const { key: shareKey } = await this.driveCrypto.decryptKeyWithSrpPassword(
|
|
25
33
|
this.password,
|
|
26
34
|
encryptedShare.base64UrlPasswordSalt,
|
|
@@ -29,134 +37,62 @@ export class SharingPublicCryptoService {
|
|
|
29
37
|
);
|
|
30
38
|
return shareKey;
|
|
31
39
|
}
|
|
40
|
+
}
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
parentKey: PrivateKey,
|
|
37
|
-
): Promise<{ node: DecryptedNode; keys?: DecryptedNodeKeys }> {
|
|
38
|
-
const commonNodeMetadata = {
|
|
39
|
-
...node,
|
|
40
|
-
encryptedCrypto: undefined,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const { name } = await this.decryptName(node, parentKey);
|
|
44
|
-
|
|
45
|
-
let passphrase, key, passphraseSessionKey;
|
|
46
|
-
try {
|
|
47
|
-
const keyResult = await this.decryptKey(node, parentKey);
|
|
48
|
-
passphrase = keyResult.passphrase;
|
|
49
|
-
key = keyResult.key;
|
|
50
|
-
passphraseSessionKey = keyResult.passphraseSessionKey;
|
|
51
|
-
} catch (error: unknown) {
|
|
52
|
-
return {
|
|
53
|
-
node: {
|
|
54
|
-
...commonNodeMetadata,
|
|
55
|
-
name,
|
|
56
|
-
errors: [error],
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const errors = [];
|
|
62
|
-
|
|
63
|
-
let hashKey;
|
|
64
|
-
if ('folder' in node.encryptedCrypto) {
|
|
65
|
-
try {
|
|
66
|
-
const hashKeyResult = await this.decryptHashKey(node, key);
|
|
67
|
-
hashKey = hashKeyResult.hashKey;
|
|
68
|
-
} catch (error: unknown) {
|
|
69
|
-
errors.push(error);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
42
|
+
class SharingPublicCryptoReporter {
|
|
43
|
+
private logger: Logger;
|
|
44
|
+
private telemetry: ProtonDriveTelemetry;
|
|
72
45
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
node.encryptedCrypto.file.base64ContentKeyPacket,
|
|
78
|
-
'',
|
|
79
|
-
key,
|
|
80
|
-
[],
|
|
81
|
-
);
|
|
46
|
+
constructor(telemetry: ProtonDriveTelemetry) {
|
|
47
|
+
this.telemetry = telemetry;
|
|
48
|
+
this.logger = telemetry.getLogger('sharingPublic-crypto');
|
|
49
|
+
}
|
|
82
50
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
51
|
+
async handleClaimedAuthor(
|
|
52
|
+
node: { uid: string; creationTime: Date },
|
|
53
|
+
field: MetricVerificationErrorField,
|
|
54
|
+
signatureType: string,
|
|
55
|
+
verified: VERIFICATION_STATUS,
|
|
56
|
+
verificationErrors?: Error[],
|
|
57
|
+
claimedAuthor?: string,
|
|
58
|
+
notAvailableVerificationKeys = false,
|
|
59
|
+
): Promise<Author> {
|
|
60
|
+
if (verified === VERIFICATION_STATUS.SIGNED_AND_VALID) {
|
|
61
|
+
return resultOk(claimedAuthor || (null as AnonymousUser));
|
|
87
62
|
}
|
|
88
63
|
|
|
89
|
-
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
keys: {
|
|
96
|
-
passphrase,
|
|
97
|
-
key,
|
|
98
|
-
passphraseSessionKey,
|
|
99
|
-
contentKeyPacketSessionKey,
|
|
100
|
-
hashKey,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
64
|
+
return resultError({
|
|
65
|
+
claimedAuthor,
|
|
66
|
+
error: !claimedAuthor
|
|
67
|
+
? c('Info').t`Author is not provided on public link`
|
|
68
|
+
: getVerificationMessage(verified, verificationErrors, signatureType, notAvailableVerificationKeys),
|
|
69
|
+
});
|
|
103
70
|
}
|
|
104
71
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
[],
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
passphrase: key.passphrase,
|
|
116
|
-
key: key.key,
|
|
117
|
-
passphraseSessionKey: key.passphraseSessionKey,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
72
|
+
reportDecryptionError(
|
|
73
|
+
node: { uid: string; creationTime: Date },
|
|
74
|
+
field: MetricsDecryptionErrorField,
|
|
75
|
+
error: unknown,
|
|
76
|
+
) {
|
|
77
|
+
const fromBefore2024 = node.creationTime < new Date('2024-01-01');
|
|
120
78
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
name: Result<string, Error>;
|
|
126
|
-
}> {
|
|
127
|
-
try {
|
|
128
|
-
const { name } = await this.driveCrypto.decryptNodeName(node.encryptedName, parentKey, []);
|
|
79
|
+
this.logger.error(
|
|
80
|
+
`Failed to decrypt public link node ${node.uid} (from before 2024: ${fromBefore2024})`,
|
|
81
|
+
error,
|
|
82
|
+
);
|
|
129
83
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
84
|
+
this.telemetry.recordMetric({
|
|
85
|
+
eventName: 'decryptionError',
|
|
86
|
+
volumeType: MetricVolumeType.SharedPublic,
|
|
87
|
+
field,
|
|
88
|
+
fromBefore2024,
|
|
89
|
+
error,
|
|
90
|
+
uid: node.uid,
|
|
91
|
+
});
|
|
139
92
|
}
|
|
140
93
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
): Promise<{
|
|
145
|
-
hashKey: Uint8Array;
|
|
146
|
-
}> {
|
|
147
|
-
if (!('folder' in node.encryptedCrypto)) {
|
|
148
|
-
// This is developer error.
|
|
149
|
-
throw new Error('Node is not a folder');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const { hashKey } = await this.driveCrypto.decryptNodeHashKey(
|
|
153
|
-
node.encryptedCrypto.folder.armoredHashKey,
|
|
154
|
-
nodeKey,
|
|
155
|
-
[],
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
hashKey,
|
|
160
|
-
};
|
|
94
|
+
reportVerificationError() {
|
|
95
|
+
// Authors or signatures are not provided on public links.
|
|
96
|
+
// We do not report any signature verification errors at this moment.
|
|
161
97
|
}
|
|
162
98
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DriveCrypto } from '../../crypto';
|
|
2
|
-
import { ProtonDriveCryptoCache, ProtonDriveTelemetry } from '../../interface';
|
|
2
|
+
import { ProtonDriveCryptoCache, ProtonDriveTelemetry, ProtonDriveAccount } from '../../interface';
|
|
3
3
|
import { DriveAPIService } from '../apiService';
|
|
4
4
|
import { SharingPublicAPIService } from './apiService';
|
|
5
5
|
import { SharingPublicCryptoCache } from './cryptoCache';
|
|
@@ -22,12 +22,13 @@ export function initSharingPublicModule(
|
|
|
22
22
|
apiService: DriveAPIService,
|
|
23
23
|
driveCryptoCache: ProtonDriveCryptoCache,
|
|
24
24
|
driveCrypto: DriveCrypto,
|
|
25
|
+
account: ProtonDriveAccount,
|
|
25
26
|
token: string,
|
|
26
27
|
password: string,
|
|
27
28
|
) {
|
|
28
29
|
const api = new SharingPublicAPIService(telemetry.getLogger('sharingPublic-api'), apiService);
|
|
29
30
|
const cryptoCache = new SharingPublicCryptoCache(telemetry.getLogger('sharingPublic-crypto'), driveCryptoCache);
|
|
30
|
-
const cryptoService = new SharingPublicCryptoService(driveCrypto, password);
|
|
31
|
+
const cryptoService = new SharingPublicCryptoService(telemetry, driveCrypto, account, password);
|
|
31
32
|
const manager = new SharingPublicManager(
|
|
32
33
|
telemetry.getLogger('sharingPublic-nodes'),
|
|
33
34
|
api,
|
|
@@ -1,59 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// TODO: use them directly, or avoid them completely
|
|
2
|
+
export type {
|
|
3
|
+
EncryptedNode,
|
|
4
|
+
EncryptedNodeFolderCrypto,
|
|
5
|
+
EncryptedNodeFileCrypto,
|
|
6
|
+
DecryptedNode,
|
|
7
|
+
DecryptedNodeKeys,
|
|
8
|
+
} from '../nodes/interface';
|
|
3
9
|
|
|
4
10
|
export interface EncryptedShareCrypto {
|
|
5
11
|
base64UrlPasswordSalt: string;
|
|
6
12
|
armoredKey: string;
|
|
7
13
|
armoredPassphrase: string;
|
|
8
14
|
}
|
|
9
|
-
|
|
10
|
-
// TODO: reuse node entity, or keep custom?
|
|
11
|
-
interface BaseNode {
|
|
12
|
-
// Internal metadata
|
|
13
|
-
hash?: string; // root node doesn't have any hash
|
|
14
|
-
encryptedName: string;
|
|
15
|
-
|
|
16
|
-
// Basic node metadata
|
|
17
|
-
uid: string;
|
|
18
|
-
parentUid?: string;
|
|
19
|
-
type: NodeType;
|
|
20
|
-
mediaType?: string;
|
|
21
|
-
totalStorageSize?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface EncryptedNode extends BaseNode {
|
|
25
|
-
encryptedCrypto: EncryptedNodeFolderCrypto | EncryptedNodeFileCrypto;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface EncryptedNodeCrypto {
|
|
29
|
-
signatureEmail?: string;
|
|
30
|
-
armoredKey: string;
|
|
31
|
-
armoredNodePassphrase: string;
|
|
32
|
-
armoredNodePassphraseSignature?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface EncryptedNodeFileCrypto extends EncryptedNodeCrypto {
|
|
36
|
-
file: {
|
|
37
|
-
base64ContentKeyPacket: string;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface EncryptedNodeFolderCrypto extends EncryptedNodeCrypto {
|
|
42
|
-
folder: {
|
|
43
|
-
armoredExtendedAttributes?: string;
|
|
44
|
-
armoredHashKey: string;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface DecryptedNode extends BaseNode {
|
|
49
|
-
name: Result<string, Error | InvalidNameError>;
|
|
50
|
-
errors?: unknown[];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface DecryptedNodeKeys {
|
|
54
|
-
passphrase: string;
|
|
55
|
-
key: PrivateKey;
|
|
56
|
-
passphraseSessionKey: SessionKey;
|
|
57
|
-
contentKeyPacketSessionKey?: SessionKey;
|
|
58
|
-
hashKey?: Uint8Array;
|
|
59
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PrivateKey } from '../../crypto';
|
|
2
2
|
import { Logger } from '../../interface';
|
|
3
|
+
import { parseNode } from '../nodes/nodesAccess';
|
|
3
4
|
import { SharingPublicAPIService } from './apiService';
|
|
4
5
|
import { SharingPublicCryptoCache } from './cryptoCache';
|
|
5
6
|
import { SharingPublicCryptoService } from './cryptoService';
|
|
@@ -27,35 +28,35 @@ export class SharingPublicManager {
|
|
|
27
28
|
return this.decryptNode(encryptedNode);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
async *
|
|
31
|
+
async *iterateFolderChildren(parentUid: string, signal?: AbortSignal): AsyncGenerator<DecryptedNode> {
|
|
31
32
|
// TODO: optimise this - decrypt in parallel
|
|
32
|
-
for await (const node of this.api.
|
|
33
|
+
for await (const node of this.api.iterateFolderChildren(parentUid, signal)) {
|
|
33
34
|
const decryptedNode = await this.decryptNode(node);
|
|
34
35
|
yield decryptedNode;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
private async decryptShare(encryptedShare: EncryptedShareCrypto): Promise<void> {
|
|
39
|
-
const shareKey = await this.cryptoService.
|
|
40
|
+
const shareKey = await this.cryptoService.decryptPublicLinkShareKey(encryptedShare);
|
|
40
41
|
await this.cryptoCache.setShareKey(shareKey);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
private async decryptNode(encryptedNode: EncryptedNode): Promise<DecryptedNode> {
|
|
44
45
|
const parentKey = await this.getParentKey(encryptedNode);
|
|
45
46
|
|
|
46
|
-
const { node:
|
|
47
|
+
const { node: unparsedNode, keys } = await this.cryptoService.decryptNode(encryptedNode, parentKey);
|
|
48
|
+
const node = await parseNode(this.logger, unparsedNode);
|
|
47
49
|
|
|
48
50
|
// TODO: cache of metadata?
|
|
49
|
-
|
|
50
51
|
if (keys) {
|
|
51
52
|
try {
|
|
52
|
-
await this.cryptoCache.setNodeKeys(
|
|
53
|
+
await this.cryptoCache.setNodeKeys(node.uid, keys);
|
|
53
54
|
} catch (error: unknown) {
|
|
54
|
-
this.logger.error(`Failed to cache node keys ${
|
|
55
|
+
this.logger.error(`Failed to cache node keys ${node.uid}`, error);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
return
|
|
59
|
+
return node;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
private async getParentKey(node: Pick<DecryptedNode, 'parentUid'>): Promise<PrivateKey> {
|
|
@@ -189,8 +189,10 @@ describe('StreamUploader', () => {
|
|
|
189
189
|
|
|
190
190
|
const verifyOnProgress = async (uploadedBytes: number[]) => {
|
|
191
191
|
expect(onProgress).toHaveBeenCalledTimes(uploadedBytes.length);
|
|
192
|
+
let fileProgress = 0;
|
|
192
193
|
for (let i = 0; i < uploadedBytes.length; i++) {
|
|
193
|
-
|
|
194
|
+
fileProgress += uploadedBytes[i];
|
|
195
|
+
expect(onProgress).toHaveBeenNthCalledWith(i + 1, fileProgress);
|
|
194
196
|
}
|
|
195
197
|
};
|
|
196
198
|
|
|
@@ -122,7 +122,7 @@ export class StreamUploader {
|
|
|
122
122
|
this.logger.info(`Starting upload`);
|
|
123
123
|
await this.encryptAndUploadBlocks(stream, thumbnails, (uploadedBytes) => {
|
|
124
124
|
fileProgress += uploadedBytes;
|
|
125
|
-
onProgress?.(
|
|
125
|
+
onProgress?.(fileProgress);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
this.logger.debug(`All blocks uploaded, committing`);
|
package/src/protonDriveClient.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
ThumbnailType,
|
|
28
28
|
ThumbnailResult,
|
|
29
29
|
SDKEvent,
|
|
30
|
+
NodeType,
|
|
30
31
|
} from './interface';
|
|
31
32
|
import {
|
|
32
33
|
getUid,
|
|
@@ -208,6 +209,7 @@ export class ProtonDriveClient {
|
|
|
208
209
|
return new ProtonDrivePublicLinkClient({
|
|
209
210
|
httpClient,
|
|
210
211
|
cryptoCache,
|
|
212
|
+
account,
|
|
211
213
|
openPGPCryptoModule,
|
|
212
214
|
srpModule,
|
|
213
215
|
config,
|
|
@@ -251,7 +253,7 @@ export class ProtonDriveClient {
|
|
|
251
253
|
}
|
|
252
254
|
|
|
253
255
|
/**
|
|
254
|
-
* Subscribes to
|
|
256
|
+
* Subscribes to the remote general data updates.
|
|
255
257
|
*
|
|
256
258
|
* Only one instance of the SDK should subscribe to updates.
|
|
257
259
|
*/
|
|
@@ -284,7 +286,7 @@ export class ProtonDriveClient {
|
|
|
284
286
|
*/
|
|
285
287
|
async getMyFilesRootFolder(): Promise<MaybeNode> {
|
|
286
288
|
this.logger.info('Getting my files root folder');
|
|
287
|
-
return convertInternalNodePromise(this.nodes.access.
|
|
289
|
+
return convertInternalNodePromise(this.nodes.access.getVolumeRootFolder());
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
/**
|
|
@@ -296,9 +298,14 @@ export class ProtonDriveClient {
|
|
|
296
298
|
* @param signal - Signal to abort the operation.
|
|
297
299
|
* @returns An async generator of the children of the given parent node.
|
|
298
300
|
*/
|
|
299
|
-
async *iterateFolderChildren(
|
|
301
|
+
async *iterateFolderChildren(
|
|
302
|
+
parentNodeUid: NodeOrUid,
|
|
303
|
+
filterOptions?: { type?: NodeType },
|
|
304
|
+
signal?: AbortSignal,
|
|
305
|
+
): AsyncGenerator<MaybeNode> {
|
|
300
306
|
this.logger.info(`Iterating children of ${getUid(parentNodeUid)}`);
|
|
301
|
-
|
|
307
|
+
const iterator = this.nodes.access.iterateFolderChildren(getUid(parentNodeUid), filterOptions, signal);
|
|
308
|
+
yield* convertInternalNodeIterator(iterator);
|
|
302
309
|
}
|
|
303
310
|
|
|
304
311
|
/**
|
|
@@ -383,6 +390,29 @@ export class ProtonDriveClient {
|
|
|
383
390
|
yield* this.nodes.management.moveNodes(getUids(nodeUids), getUid(newParentNodeUid), signal);
|
|
384
391
|
}
|
|
385
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Copy the nodes to a new parent node.
|
|
395
|
+
*
|
|
396
|
+
* The operation is performed node by node and the results are yielded
|
|
397
|
+
* as they are available. Order of the results is not guaranteed.
|
|
398
|
+
*
|
|
399
|
+
* If one of the nodes fails to copy, the operation continues with the
|
|
400
|
+
* rest of the nodes. Use `NodeResult` to check the status of the action.
|
|
401
|
+
*
|
|
402
|
+
* @param nodeUids - List of node entities or their UIDs.
|
|
403
|
+
* @param newParentNodeUid - Node entity or its UID string.
|
|
404
|
+
* @param signal - Signal to abort the operation.
|
|
405
|
+
* @returns An async generator of the results of the copy operation
|
|
406
|
+
*/
|
|
407
|
+
async *copyNodes(
|
|
408
|
+
nodeUids: NodeOrUid[],
|
|
409
|
+
newParentNodeUid: NodeOrUid,
|
|
410
|
+
signal?: AbortSignal,
|
|
411
|
+
): AsyncGenerator<NodeResult> {
|
|
412
|
+
this.logger.info(`Copying ${nodeUids.length} nodes to ${getUid(newParentNodeUid)}`);
|
|
413
|
+
yield* this.nodes.management.copyNodes(getUids(nodeUids), getUid(newParentNodeUid), signal);
|
|
414
|
+
}
|
|
415
|
+
|
|
386
416
|
/**
|
|
387
417
|
* Trash the nodes.
|
|
388
418
|
*
|