@protontech/drive-sdk 0.3.1 → 0.3.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/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/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/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 +17 -12
- package/dist/internal/nodes/cryptoService.js +17 -97
- package/dist/internal/nodes/cryptoService.js.map +1 -1
- package/dist/internal/nodes/cryptoService.test.js +3 -1
- package/dist/internal/nodes/cryptoService.test.js.map +1 -1
- package/dist/internal/nodes/index.js +3 -1
- package/dist/internal/nodes/index.js.map +1 -1
- package/dist/internal/nodes/interface.d.ts +1 -1
- package/dist/internal/nodes/nodesAccess.d.ts +2 -2
- package/dist/internal/nodes/nodesAccess.js +52 -54
- package/dist/internal/nodes/nodesAccess.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 +1 -1
- package/dist/internal/sharing/interface.js +1 -1
- package/dist/internal/sharing/sharingAccess.js +6 -0
- package/dist/internal/sharing/sharingAccess.js.map +1 -1
- package/dist/internal/sharing/sharingAccess.test.js +242 -33
- 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.js +1 -0
- package/dist/protonDriveClient.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/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/nodes/cache.ts +3 -1
- package/src/internal/nodes/cryptoReporter.ts +145 -0
- package/src/internal/nodes/cryptoService.test.ts +3 -1
- package/src/internal/nodes/cryptoService.ts +44 -137
- package/src/internal/nodes/index.ts +3 -1
- package/src/internal/nodes/interface.ts +1 -1
- package/src/internal/nodes/nodesAccess.ts +59 -61
- package/src/internal/sharing/cache.ts +19 -2
- package/src/internal/sharing/interface.ts +1 -1
- package/src/internal/sharing/sharingAccess.test.ts +282 -34
- package/src/internal/sharing/sharingAccess.ts +6 -0
- 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 +1 -0
- package/src/protonDrivePublicLinkClient.ts +26 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DriveAPIService, drivePaths, nodeTypeNumberToNodeType } from '../apiService';
|
|
2
|
-
import { Logger } from '../../interface';
|
|
2
|
+
import { Logger, MemberRole } from '../../interface';
|
|
3
3
|
import { makeNodeUid, splitNodeUid } from '../uids';
|
|
4
4
|
import { EncryptedShareCrypto, EncryptedNode } from './interface';
|
|
5
5
|
|
|
@@ -42,13 +42,14 @@ export class SharingPublicAPIService {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
async *
|
|
45
|
+
async *iterateFolderChildren(parentUid: string, signal?: AbortSignal): AsyncGenerator<EncryptedNode> {
|
|
46
46
|
const { volumeId: token, nodeId } = splitNodeUid(parentUid);
|
|
47
47
|
|
|
48
48
|
let page = 0;
|
|
49
49
|
while (true) {
|
|
50
50
|
const response = await this.apiService.get<GetTokenFolderChildrenResponse>(
|
|
51
51
|
`drive/urls/${token}/folders/${nodeId}/children?Page=${page}&PageSize=${PAGE_SIZE}`,
|
|
52
|
+
signal,
|
|
52
53
|
);
|
|
53
54
|
|
|
54
55
|
for (const link of response.Links) {
|
|
@@ -72,6 +73,10 @@ function tokenToEncryptedNode(logger: Logger, token: GetTokenInfoResponse['Token
|
|
|
72
73
|
uid: makeNodeUid(token.Token, token.LinkID),
|
|
73
74
|
parentUid: undefined,
|
|
74
75
|
type: nodeTypeNumberToNodeType(logger, token.LinkType),
|
|
76
|
+
creationTime: new Date(), // TODO
|
|
77
|
+
|
|
78
|
+
isShared: false,
|
|
79
|
+
directRole: MemberRole.Viewer, // TODO
|
|
75
80
|
};
|
|
76
81
|
|
|
77
82
|
const baseCryptoNodeMetadata = {
|
|
@@ -124,7 +129,11 @@ function linkToEncryptedNode(
|
|
|
124
129
|
uid: makeNodeUid(token, link.LinkID),
|
|
125
130
|
parentUid: link.ParentLinkID ? makeNodeUid(token, link.ParentLinkID) : undefined,
|
|
126
131
|
type: nodeTypeNumberToNodeType(logger, link.Type),
|
|
132
|
+
creationTime: new Date(), // TODO
|
|
127
133
|
totalStorageSize: link.TotalSize,
|
|
134
|
+
|
|
135
|
+
isShared: false,
|
|
136
|
+
directRole: MemberRole.Viewer, // TODO
|
|
128
137
|
};
|
|
129
138
|
|
|
130
139
|
const baseCryptoNodeMetadata = {
|
|
@@ -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
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
Logger,
|
|
8
8
|
ProtonDriveCryptoCache,
|
|
9
9
|
NodeOrUid,
|
|
10
|
+
ProtonDriveAccount,
|
|
11
|
+
MaybeNode,
|
|
10
12
|
} from './interface';
|
|
11
13
|
import { Telemetry } from './telemetry';
|
|
12
|
-
import { getUid } from './transformers';
|
|
14
|
+
import { getUid, convertInternalNodePromise, convertInternalNodeIterator } from './transformers';
|
|
13
15
|
import { DriveAPIService } from './internal/apiService';
|
|
14
16
|
import { SDKEvents } from './internal/sdkEvents';
|
|
15
17
|
import { initSharingPublicModule } from './internal/sharingPublic';
|
|
@@ -52,6 +54,7 @@ export class ProtonDrivePublicLinkClient {
|
|
|
52
54
|
constructor({
|
|
53
55
|
httpClient,
|
|
54
56
|
cryptoCache,
|
|
57
|
+
account,
|
|
55
58
|
openPGPCryptoModule,
|
|
56
59
|
srpModule,
|
|
57
60
|
config,
|
|
@@ -61,6 +64,7 @@ export class ProtonDrivePublicLinkClient {
|
|
|
61
64
|
}: {
|
|
62
65
|
httpClient: ProtonDriveHTTPClient;
|
|
63
66
|
cryptoCache: ProtonDriveCryptoCache;
|
|
67
|
+
account: ProtonDriveAccount;
|
|
64
68
|
openPGPCryptoModule: OpenPGPCrypto;
|
|
65
69
|
srpModule: SRPModule;
|
|
66
70
|
config?: ProtonDriveConfig;
|
|
@@ -84,7 +88,15 @@ export class ProtonDrivePublicLinkClient {
|
|
|
84
88
|
fullConfig.language,
|
|
85
89
|
);
|
|
86
90
|
const driveCrypto = new DriveCrypto(openPGPCryptoModule, srpModule);
|
|
87
|
-
this.sharingPublic = initSharingPublicModule(
|
|
91
|
+
this.sharingPublic = initSharingPublicModule(
|
|
92
|
+
telemetry,
|
|
93
|
+
apiService,
|
|
94
|
+
cryptoCache,
|
|
95
|
+
driveCrypto,
|
|
96
|
+
account,
|
|
97
|
+
token,
|
|
98
|
+
password,
|
|
99
|
+
);
|
|
88
100
|
|
|
89
101
|
this.experimental = {
|
|
90
102
|
getNodeUrl: async (nodeUid: NodeOrUid) => {
|
|
@@ -103,19 +115,21 @@ export class ProtonDrivePublicLinkClient {
|
|
|
103
115
|
};
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
/**
|
|
119
|
+
* @returns The root folder to the public link.
|
|
120
|
+
*/
|
|
121
|
+
async getRootNode(): Promise<MaybeNode> {
|
|
109
122
|
this.logger.info(`Getting root node`);
|
|
110
|
-
|
|
111
|
-
return this.sharingPublic.getRootNode();
|
|
123
|
+
return convertInternalNodePromise(this.sharingPublic.getRootNode());
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Iterates the children of the given parent node.
|
|
128
|
+
*
|
|
129
|
+
* See `ProtonDriveClient.iterateFolderChildren` for more information.
|
|
130
|
+
*/
|
|
131
|
+
async *iterateFolderChildren(parentUid: NodeOrUid, signal?: AbortSignal): AsyncGenerator<MaybeNode> {
|
|
117
132
|
this.logger.info(`Iterating children of ${getUid(parentUid)}`);
|
|
118
|
-
|
|
119
|
-
yield * this.sharingPublic.iterateChildren(getUid(parentUid));
|
|
133
|
+
yield * convertInternalNodeIterator(this.sharingPublic.iterateFolderChildren(getUid(parentUid), signal));
|
|
120
134
|
}
|
|
121
135
|
}
|