@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.
Files changed (181) hide show
  1. package/dist/crypto/driveCrypto.d.ts +1 -1
  2. package/dist/crypto/driveCrypto.js.map +1 -1
  3. package/dist/crypto/interface.d.ts +1 -1
  4. package/dist/crypto/openPGPCrypto.d.ts +1 -1
  5. package/dist/crypto/openPGPCrypto.js +4 -1
  6. package/dist/crypto/openPGPCrypto.js.map +1 -1
  7. package/dist/internal/apiService/errorCodes.d.ts +1 -0
  8. package/dist/internal/apiService/errors.d.ts +3 -0
  9. package/dist/internal/apiService/errors.js +7 -1
  10. package/dist/internal/apiService/errors.js.map +1 -1
  11. package/dist/internal/devices/interface.d.ts +1 -1
  12. package/dist/internal/devices/manager.js +1 -1
  13. package/dist/internal/devices/manager.js.map +1 -1
  14. package/dist/internal/devices/manager.test.js +3 -3
  15. package/dist/internal/devices/manager.test.js.map +1 -1
  16. package/dist/internal/download/cryptoService.js +2 -2
  17. package/dist/internal/download/cryptoService.js.map +1 -1
  18. package/dist/internal/download/fileDownloader.js +2 -2
  19. package/dist/internal/download/fileDownloader.js.map +1 -1
  20. package/dist/internal/download/fileDownloader.test.js +3 -1
  21. package/dist/internal/download/fileDownloader.test.js.map +1 -1
  22. package/dist/internal/events/apiService.js +1 -1
  23. package/dist/internal/events/apiService.js.map +1 -1
  24. package/dist/internal/events/coreEventManager.js +1 -1
  25. package/dist/internal/events/coreEventManager.js.map +1 -1
  26. package/dist/internal/events/coreEventManager.test.js +18 -24
  27. package/dist/internal/events/coreEventManager.test.js.map +1 -1
  28. package/dist/internal/events/index.d.ts +3 -4
  29. package/dist/internal/events/index.js +4 -4
  30. package/dist/internal/events/index.js.map +1 -1
  31. package/dist/internal/events/interface.d.ts +3 -0
  32. package/dist/internal/nodes/apiService.d.ts +12 -3
  33. package/dist/internal/nodes/apiService.js +53 -13
  34. package/dist/internal/nodes/apiService.js.map +1 -1
  35. package/dist/internal/nodes/apiService.test.js +19 -2
  36. package/dist/internal/nodes/apiService.test.js.map +1 -1
  37. package/dist/internal/nodes/cache.js +3 -1
  38. package/dist/internal/nodes/cache.js.map +1 -1
  39. package/dist/internal/nodes/cryptoReporter.d.ts +20 -0
  40. package/dist/internal/nodes/cryptoReporter.js +96 -0
  41. package/dist/internal/nodes/cryptoReporter.js.map +1 -0
  42. package/dist/internal/nodes/cryptoService.d.ts +18 -13
  43. package/dist/internal/nodes/cryptoService.js +18 -98
  44. package/dist/internal/nodes/cryptoService.js.map +1 -1
  45. package/dist/internal/nodes/cryptoService.test.js +7 -5
  46. package/dist/internal/nodes/cryptoService.test.js.map +1 -1
  47. package/dist/internal/nodes/errors.d.ts +4 -0
  48. package/dist/internal/nodes/errors.js +9 -0
  49. package/dist/internal/nodes/errors.js.map +1 -0
  50. package/dist/internal/nodes/index.js +3 -1
  51. package/dist/internal/nodes/index.js.map +1 -1
  52. package/dist/internal/nodes/index.test.js +1 -1
  53. package/dist/internal/nodes/index.test.js.map +1 -1
  54. package/dist/internal/nodes/interface.d.ts +5 -2
  55. package/dist/internal/nodes/nodesAccess.d.ts +4 -4
  56. package/dist/internal/nodes/nodesAccess.js +77 -69
  57. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  58. package/dist/internal/nodes/nodesAccess.test.js +48 -8
  59. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  60. package/dist/internal/nodes/nodesManagement.d.ts +2 -0
  61. package/dist/internal/nodes/nodesManagement.js +86 -9
  62. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  63. package/dist/internal/nodes/nodesManagement.test.js +81 -5
  64. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  65. package/dist/internal/photos/albums.d.ts +9 -7
  66. package/dist/internal/photos/albums.js +26 -13
  67. package/dist/internal/photos/albums.js.map +1 -1
  68. package/dist/internal/photos/apiService.d.ts +34 -3
  69. package/dist/internal/photos/apiService.js +96 -3
  70. package/dist/internal/photos/apiService.js.map +1 -1
  71. package/dist/internal/photos/index.d.ts +20 -4
  72. package/dist/internal/photos/index.js +30 -7
  73. package/dist/internal/photos/index.js.map +1 -1
  74. package/dist/internal/photos/interface.d.ts +25 -1
  75. package/dist/internal/photos/shares.d.ts +43 -0
  76. package/dist/internal/photos/shares.js +112 -0
  77. package/dist/internal/photos/shares.js.map +1 -0
  78. package/dist/internal/photos/timeline.d.ts +15 -0
  79. package/dist/internal/photos/timeline.js +22 -0
  80. package/dist/internal/photos/timeline.js.map +1 -0
  81. package/dist/internal/shares/manager.d.ts +1 -1
  82. package/dist/internal/shares/manager.js +4 -4
  83. package/dist/internal/shares/manager.js.map +1 -1
  84. package/dist/internal/shares/manager.test.js +7 -7
  85. package/dist/internal/shares/manager.test.js.map +1 -1
  86. package/dist/internal/sharing/cache.d.ts +3 -0
  87. package/dist/internal/sharing/cache.js +17 -2
  88. package/dist/internal/sharing/cache.js.map +1 -1
  89. package/dist/internal/sharing/interface.d.ts +2 -2
  90. package/dist/internal/sharing/interface.js +1 -1
  91. package/dist/internal/sharing/sharingAccess.js +7 -1
  92. package/dist/internal/sharing/sharingAccess.js.map +1 -1
  93. package/dist/internal/sharing/sharingAccess.test.js +243 -34
  94. package/dist/internal/sharing/sharingAccess.test.js.map +1 -1
  95. package/dist/internal/sharingPublic/apiService.d.ts +1 -1
  96. package/dist/internal/sharingPublic/apiService.js +9 -2
  97. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  98. package/dist/internal/sharingPublic/cryptoService.d.ts +6 -20
  99. package/dist/internal/sharingPublic/cryptoService.js +40 -103
  100. package/dist/internal/sharingPublic/cryptoService.js.map +1 -1
  101. package/dist/internal/sharingPublic/index.d.ts +2 -2
  102. package/dist/internal/sharingPublic/index.js +2 -2
  103. package/dist/internal/sharingPublic/index.js.map +1 -1
  104. package/dist/internal/sharingPublic/interface.d.ts +1 -43
  105. package/dist/internal/sharingPublic/manager.d.ts +1 -1
  106. package/dist/internal/sharingPublic/manager.js +9 -7
  107. package/dist/internal/sharingPublic/manager.js.map +1 -1
  108. package/dist/internal/upload/streamUploader.js +1 -1
  109. package/dist/internal/upload/streamUploader.js.map +1 -1
  110. package/dist/internal/upload/streamUploader.test.js +3 -1
  111. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  112. package/dist/protonDriveClient.d.ts +20 -3
  113. package/dist/protonDriveClient.js +24 -4
  114. package/dist/protonDriveClient.js.map +1 -1
  115. package/dist/protonDrivePhotosClient.d.ts +86 -12
  116. package/dist/protonDrivePhotosClient.js +132 -29
  117. package/dist/protonDrivePhotosClient.js.map +1 -1
  118. package/dist/protonDrivePublicLinkClient.d.ts +13 -4
  119. package/dist/protonDrivePublicLinkClient.js +13 -11
  120. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  121. package/package.json +1 -1
  122. package/src/crypto/driveCrypto.ts +1 -1
  123. package/src/crypto/interface.ts +1 -1
  124. package/src/crypto/openPGPCrypto.ts +5 -2
  125. package/src/internal/apiService/errorCodes.ts +1 -0
  126. package/src/internal/apiService/errors.ts +6 -0
  127. package/src/internal/devices/interface.ts +1 -1
  128. package/src/internal/devices/manager.test.ts +3 -3
  129. package/src/internal/devices/manager.ts +1 -1
  130. package/src/internal/download/cryptoService.ts +2 -2
  131. package/src/internal/download/fileDownloader.test.ts +3 -1
  132. package/src/internal/download/fileDownloader.ts +2 -2
  133. package/src/internal/events/apiService.ts +1 -1
  134. package/src/internal/events/coreEventManager.test.ts +21 -27
  135. package/src/internal/events/coreEventManager.ts +1 -1
  136. package/src/internal/events/index.ts +3 -4
  137. package/src/internal/events/interface.ts +4 -0
  138. package/src/internal/nodes/apiService.test.ts +35 -1
  139. package/src/internal/nodes/apiService.ts +103 -17
  140. package/src/internal/nodes/cache.ts +3 -1
  141. package/src/internal/nodes/cryptoReporter.ts +145 -0
  142. package/src/internal/nodes/cryptoService.test.ts +11 -9
  143. package/src/internal/nodes/cryptoService.ts +45 -138
  144. package/src/internal/nodes/errors.ts +5 -0
  145. package/src/internal/nodes/index.test.ts +1 -1
  146. package/src/internal/nodes/index.ts +3 -1
  147. package/src/internal/nodes/interface.ts +6 -2
  148. package/src/internal/nodes/nodesAccess.test.ts +68 -8
  149. package/src/internal/nodes/nodesAccess.ts +101 -76
  150. package/src/internal/nodes/nodesManagement.test.ts +100 -5
  151. package/src/internal/nodes/nodesManagement.ts +100 -13
  152. package/src/internal/photos/albums.ts +31 -12
  153. package/src/internal/photos/apiService.ts +159 -4
  154. package/src/internal/photos/index.ts +54 -9
  155. package/src/internal/photos/interface.ts +23 -1
  156. package/src/internal/photos/shares.ts +134 -0
  157. package/src/internal/photos/timeline.ts +24 -0
  158. package/src/internal/shares/manager.test.ts +7 -7
  159. package/src/internal/shares/manager.ts +4 -4
  160. package/src/internal/sharing/cache.ts +19 -2
  161. package/src/internal/sharing/interface.ts +2 -2
  162. package/src/internal/sharing/sharingAccess.test.ts +283 -35
  163. package/src/internal/sharing/sharingAccess.ts +7 -1
  164. package/src/internal/sharingPublic/apiService.ts +11 -2
  165. package/src/internal/sharingPublic/cryptoService.ts +71 -135
  166. package/src/internal/sharingPublic/index.ts +3 -2
  167. package/src/internal/sharingPublic/interface.ts +8 -53
  168. package/src/internal/sharingPublic/manager.ts +9 -8
  169. package/src/internal/upload/streamUploader.test.ts +3 -1
  170. package/src/internal/upload/streamUploader.ts +1 -1
  171. package/src/protonDriveClient.ts +34 -4
  172. package/src/protonDrivePhotosClient.ts +211 -32
  173. package/src/protonDrivePublicLinkClient.ts +26 -12
  174. package/dist/internal/photos/cache.d.ts +0 -6
  175. package/dist/internal/photos/cache.js +0 -15
  176. package/dist/internal/photos/cache.js.map +0 -1
  177. package/dist/internal/photos/photosTimeline.d.ts +0 -10
  178. package/dist/internal/photos/photosTimeline.js +0 -19
  179. package/dist/internal/photos/photosTimeline.js.map +0 -1
  180. package/src/internal/photos/cache.ts +0 -11
  181. package/src/internal/photos/photosTimeline.ts +0 -17
@@ -1,26 +1,34 @@
1
- import { DriveCrypto, PrivateKey } from '../../crypto';
2
- import { resultOk, resultError, Result } from '../../interface';
3
- import { getErrorMessage } from '../errors';
4
- import { EncryptedShareCrypto, EncryptedNode, DecryptedNode, DecryptedNodeKeys } from './interface';
5
-
6
- /**
7
- * Provides crypto operations for public link data.
8
- *
9
- * The public link crypto service is responsible for decrypting and encrypting
10
- * public link data. It should export high-level actions only, such as "decrypt
11
- * share key" instead of low-level operations like "decrypt key". Low-level
12
- * operations should be kept private to the module.
13
- */
14
- export class SharingPublicCryptoService {
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
- private driveCrypto: DriveCrypto,
22
+ telemetry: ProtonDriveTelemetry,
23
+ driveCrypto: DriveCrypto,
24
+ account: ProtonDriveAccount,
17
25
  private password: string,
18
26
  ) {
19
- this.driveCrypto = driveCrypto;
27
+ super(telemetry, driveCrypto, account, new SharingPublicCryptoReporter(telemetry));
20
28
  this.password = password;
21
29
  }
22
30
 
23
- async decryptShareKey(encryptedShare: EncryptedShareCrypto): Promise<PrivateKey> {
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
- // TODO: verfiy it has all needed
34
- async decryptNode(
35
- node: EncryptedNode,
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
- let contentKeyPacketSessionKey;
74
- if ('file' in node.encryptedCrypto) {
75
- try {
76
- const keySessionKeyResult = await this.driveCrypto.decryptAndVerifySessionKey(
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
- contentKeyPacketSessionKey = keySessionKeyResult.sessionKey;
84
- } catch (error: unknown) {
85
- errors.push(error);
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
- node: {
91
- ...commonNodeMetadata,
92
- name,
93
- errors: errors.length ? errors : undefined,
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
- private async decryptKey(node: EncryptedNode, parentKey: PrivateKey): Promise<DecryptedNodeKeys> {
106
- const key = await this.driveCrypto.decryptKey(
107
- node.encryptedCrypto.armoredKey,
108
- node.encryptedCrypto.armoredNodePassphrase,
109
- '',
110
- [parentKey],
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
- private async decryptName(
122
- node: EncryptedNode,
123
- parentKey: PrivateKey,
124
- ): Promise<{
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
- return {
131
- name: resultOk(name),
132
- };
133
- } catch (error: unknown) {
134
- const errorMessage = getErrorMessage(error);
135
- return {
136
- name: resultError(new Error(errorMessage)),
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
- private async decryptHashKey(
142
- node: EncryptedNode,
143
- nodeKey: PrivateKey,
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
- import { PrivateKey, SessionKey } from "../../crypto";
2
- import { NodeType, Result, InvalidNameError } from "../../interface";
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 *iterateChildren(parentUid: string): AsyncGenerator<DecryptedNode> {
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.iterateChildren(parentUid)) {
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.decryptShareKey(encryptedShare);
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: decryptedNode, keys } = await this.cryptoService.decryptNode(encryptedNode, parentKey);
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(decryptedNode.uid, keys);
53
+ await this.cryptoCache.setNodeKeys(node.uid, keys);
53
54
  } catch (error: unknown) {
54
- this.logger.error(`Failed to cache node keys ${decryptedNode.uid}`, error);
55
+ this.logger.error(`Failed to cache node keys ${node.uid}`, error);
55
56
  }
56
57
  }
57
58
 
58
- return decryptedNode;
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
- expect(onProgress).toHaveBeenNthCalledWith(i + 1, uploadedBytes[i]);
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?.(uploadedBytes);
125
+ onProgress?.(fileProgress);
126
126
  });
127
127
 
128
128
  this.logger.debug(`All blocks uploaded, committing`);
@@ -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 sharing updates.
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.getMyFilesRootFolder());
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(parentNodeUid: NodeOrUid, signal?: AbortSignal): AsyncGenerator<MaybeNode> {
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
- yield* convertInternalNodeIterator(this.nodes.access.iterateFolderChildren(getUid(parentNodeUid), signal));
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
  *