@protontech/drive-sdk 0.5.0 → 0.6.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 (206) hide show
  1. package/dist/diagnostic/{sdkDiagnosticFull.d.ts → diagnostic.d.ts} +5 -4
  2. package/dist/diagnostic/{sdkDiagnosticFull.js → diagnostic.js} +13 -10
  3. package/dist/diagnostic/diagnostic.js.map +1 -0
  4. package/dist/diagnostic/index.js +2 -4
  5. package/dist/diagnostic/index.js.map +1 -1
  6. package/dist/diagnostic/interface.d.ts +22 -1
  7. package/dist/diagnostic/sdkDiagnostic.d.ts +3 -2
  8. package/dist/diagnostic/sdkDiagnostic.js +79 -7
  9. package/dist/diagnostic/sdkDiagnostic.js.map +1 -1
  10. package/dist/interface/index.d.ts +2 -2
  11. package/dist/interface/index.js.map +1 -1
  12. package/dist/interface/nodes.d.ts +9 -0
  13. package/dist/interface/telemetry.d.ts +4 -1
  14. package/dist/interface/telemetry.js.map +1 -1
  15. package/dist/interface/upload.d.ts +1 -12
  16. package/dist/internal/apiService/driveTypes.d.ts +2571 -2356
  17. package/dist/internal/download/controller.d.ts +2 -0
  18. package/dist/internal/download/controller.js +15 -1
  19. package/dist/internal/download/controller.js.map +1 -1
  20. package/dist/internal/download/fileDownloader.js +6 -1
  21. package/dist/internal/download/fileDownloader.js.map +1 -1
  22. package/dist/internal/nodes/apiService.d.ts +11 -1
  23. package/dist/internal/nodes/apiService.js +47 -13
  24. package/dist/internal/nodes/apiService.js.map +1 -1
  25. package/dist/internal/nodes/apiService.test.js +61 -3
  26. package/dist/internal/nodes/apiService.test.js.map +1 -1
  27. package/dist/internal/nodes/cryptoService.d.ts +4 -0
  28. package/dist/internal/nodes/cryptoService.js +6 -0
  29. package/dist/internal/nodes/cryptoService.js.map +1 -1
  30. package/dist/internal/nodes/debouncer.d.ts +3 -2
  31. package/dist/internal/nodes/debouncer.js +16 -4
  32. package/dist/internal/nodes/debouncer.js.map +1 -1
  33. package/dist/internal/nodes/debouncer.test.js +20 -12
  34. package/dist/internal/nodes/debouncer.test.js.map +1 -1
  35. package/dist/internal/nodes/extendedAttributes.js +2 -2
  36. package/dist/internal/nodes/extendedAttributes.js.map +1 -1
  37. package/dist/internal/nodes/index.d.ts +1 -1
  38. package/dist/internal/nodes/index.js +3 -3
  39. package/dist/internal/nodes/index.js.map +1 -1
  40. package/dist/internal/nodes/index.test.js +1 -1
  41. package/dist/internal/nodes/index.test.js.map +1 -1
  42. package/dist/internal/nodes/nodeName.d.ts +8 -0
  43. package/dist/internal/nodes/nodeName.js +30 -0
  44. package/dist/internal/nodes/nodeName.js.map +1 -0
  45. package/dist/internal/nodes/nodeName.test.d.ts +1 -0
  46. package/dist/internal/nodes/nodeName.test.js +50 -0
  47. package/dist/internal/nodes/nodeName.test.js.map +1 -0
  48. package/dist/internal/nodes/nodesAccess.d.ts +5 -4
  49. package/dist/internal/nodes/nodesAccess.js +6 -5
  50. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  51. package/dist/internal/nodes/nodesAccess.test.js +17 -5
  52. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  53. package/dist/internal/nodes/nodesManagement.d.ts +3 -2
  54. package/dist/internal/nodes/nodesManagement.js +35 -4
  55. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  56. package/dist/internal/nodes/nodesManagement.test.js +64 -1
  57. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  58. package/dist/internal/photos/apiService.js +9 -20
  59. package/dist/internal/photos/apiService.js.map +1 -1
  60. package/dist/internal/photos/index.d.ts +2 -0
  61. package/dist/internal/photos/index.js +4 -0
  62. package/dist/internal/photos/index.js.map +1 -1
  63. package/dist/internal/photos/upload.js +7 -1
  64. package/dist/internal/photos/upload.js.map +1 -1
  65. package/dist/internal/shares/index.d.ts +1 -0
  66. package/dist/internal/shares/index.js +3 -0
  67. package/dist/internal/shares/index.js.map +1 -1
  68. package/dist/internal/shares/interface.d.ts +8 -0
  69. package/dist/internal/shares/interface.js +10 -1
  70. package/dist/internal/shares/interface.js.map +1 -1
  71. package/dist/internal/sharing/apiService.d.ts +4 -2
  72. package/dist/internal/sharing/apiService.js +18 -14
  73. package/dist/internal/sharing/apiService.js.map +1 -1
  74. package/dist/internal/sharing/index.d.ts +2 -1
  75. package/dist/internal/sharing/index.js +6 -2
  76. package/dist/internal/sharing/index.js.map +1 -1
  77. package/dist/internal/sharing/sharingManagement.d.ts +4 -1
  78. package/dist/internal/sharing/sharingManagement.js +7 -4
  79. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  80. package/dist/internal/sharingPublic/apiService.d.ts +8 -10
  81. package/dist/internal/sharingPublic/apiService.js +9 -63
  82. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  83. package/dist/internal/sharingPublic/index.d.ts +3 -3
  84. package/dist/internal/sharingPublic/index.js +7 -12
  85. package/dist/internal/sharingPublic/index.js.map +1 -1
  86. package/dist/internal/sharingPublic/nodes.d.ts +13 -8
  87. package/dist/internal/sharingPublic/nodes.js +20 -2
  88. package/dist/internal/sharingPublic/nodes.js.map +1 -1
  89. package/dist/internal/sharingPublic/session/apiService.d.ts +7 -5
  90. package/dist/internal/sharingPublic/session/apiService.js +25 -4
  91. package/dist/internal/sharingPublic/session/apiService.js.map +1 -1
  92. package/dist/internal/sharingPublic/session/interface.d.ts +17 -0
  93. package/dist/internal/sharingPublic/session/manager.d.ts +12 -4
  94. package/dist/internal/sharingPublic/session/manager.js +14 -4
  95. package/dist/internal/sharingPublic/session/manager.js.map +1 -1
  96. package/dist/internal/sharingPublic/session/session.d.ts +5 -2
  97. package/dist/internal/sharingPublic/session/session.js +7 -3
  98. package/dist/internal/sharingPublic/session/session.js.map +1 -1
  99. package/dist/internal/sharingPublic/shares.d.ts +3 -10
  100. package/dist/internal/sharingPublic/shares.js +10 -33
  101. package/dist/internal/sharingPublic/shares.js.map +1 -1
  102. package/dist/internal/upload/apiService.d.ts +0 -9
  103. package/dist/internal/upload/apiService.js +0 -16
  104. package/dist/internal/upload/apiService.js.map +1 -1
  105. package/dist/internal/upload/controller.d.ts +3 -1
  106. package/dist/internal/upload/controller.js +16 -2
  107. package/dist/internal/upload/controller.js.map +1 -1
  108. package/dist/internal/upload/cryptoService.d.ts +0 -4
  109. package/dist/internal/upload/cryptoService.js +0 -6
  110. package/dist/internal/upload/cryptoService.js.map +1 -1
  111. package/dist/internal/upload/fileUploader.d.ts +0 -1
  112. package/dist/internal/upload/fileUploader.js +2 -6
  113. package/dist/internal/upload/fileUploader.js.map +1 -1
  114. package/dist/internal/upload/manager.d.ts +0 -1
  115. package/dist/internal/upload/manager.js +0 -51
  116. package/dist/internal/upload/manager.js.map +1 -1
  117. package/dist/internal/upload/manager.test.js +0 -61
  118. package/dist/internal/upload/manager.test.js.map +1 -1
  119. package/dist/internal/upload/streamUploader.d.ts +4 -3
  120. package/dist/internal/upload/streamUploader.js +61 -18
  121. package/dist/internal/upload/streamUploader.js.map +1 -1
  122. package/dist/internal/upload/streamUploader.test.js +38 -12
  123. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  124. package/dist/protonDriveClient.d.ts +24 -4
  125. package/dist/protonDriveClient.js +26 -7
  126. package/dist/protonDriveClient.js.map +1 -1
  127. package/dist/protonDrivePhotosClient.d.ts +100 -4
  128. package/dist/protonDrivePhotosClient.js +160 -9
  129. package/dist/protonDrivePhotosClient.js.map +1 -1
  130. package/dist/protonDrivePublicLinkClient.d.ts +4 -3
  131. package/dist/protonDrivePublicLinkClient.js +2 -2
  132. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  133. package/dist/tests/telemetry.d.ts +4 -2
  134. package/dist/tests/telemetry.js +3 -1
  135. package/dist/tests/telemetry.js.map +1 -1
  136. package/dist/transformers.d.ts +3 -2
  137. package/dist/transformers.js +6 -0
  138. package/dist/transformers.js.map +1 -1
  139. package/package.json +1 -1
  140. package/src/diagnostic/{sdkDiagnosticFull.ts → diagnostic.ts} +10 -6
  141. package/src/diagnostic/index.ts +3 -5
  142. package/src/diagnostic/interface.ts +39 -0
  143. package/src/diagnostic/sdkDiagnostic.ts +110 -9
  144. package/src/interface/index.ts +2 -1
  145. package/src/interface/nodes.ts +3 -0
  146. package/src/interface/telemetry.ts +5 -0
  147. package/src/interface/upload.ts +1 -13
  148. package/src/internal/apiService/driveTypes.ts +2698 -2529
  149. package/src/internal/download/controller.ts +13 -1
  150. package/src/internal/download/fileDownloader.ts +8 -1
  151. package/src/internal/nodes/apiService.test.ts +65 -1
  152. package/src/internal/nodes/apiService.ts +80 -17
  153. package/src/internal/nodes/cryptoService.ts +9 -0
  154. package/src/internal/nodes/debouncer.test.ts +25 -13
  155. package/src/internal/nodes/debouncer.ts +20 -4
  156. package/src/internal/nodes/extendedAttributes.ts +2 -2
  157. package/src/internal/nodes/index.test.ts +1 -0
  158. package/src/internal/nodes/index.ts +3 -9
  159. package/src/internal/nodes/nodeName.test.ts +57 -0
  160. package/src/internal/nodes/nodeName.ts +26 -0
  161. package/src/internal/nodes/nodesAccess.test.ts +17 -5
  162. package/src/internal/nodes/nodesAccess.ts +15 -5
  163. package/src/internal/nodes/nodesManagement.test.ts +68 -1
  164. package/src/internal/nodes/nodesManagement.ts +54 -6
  165. package/src/internal/photos/apiService.ts +12 -29
  166. package/src/internal/photos/index.ts +4 -0
  167. package/src/internal/photos/upload.ts +19 -1
  168. package/src/internal/shares/index.ts +1 -0
  169. package/src/internal/shares/interface.ts +9 -0
  170. package/src/internal/sharing/apiService.ts +17 -14
  171. package/src/internal/sharing/index.ts +7 -1
  172. package/src/internal/sharing/sharingManagement.ts +7 -4
  173. package/src/internal/sharingPublic/apiService.ts +23 -77
  174. package/src/internal/sharingPublic/index.ts +13 -11
  175. package/src/internal/sharingPublic/nodes.ts +33 -11
  176. package/src/internal/sharingPublic/session/apiService.ts +31 -9
  177. package/src/internal/sharingPublic/session/interface.ts +20 -0
  178. package/src/internal/sharingPublic/session/manager.ts +31 -8
  179. package/src/internal/sharingPublic/session/session.ts +10 -5
  180. package/src/internal/sharingPublic/shares.ts +7 -43
  181. package/src/internal/upload/apiService.ts +0 -39
  182. package/src/internal/upload/controller.ts +16 -4
  183. package/src/internal/upload/cryptoService.ts +0 -9
  184. package/src/internal/upload/fileUploader.ts +2 -7
  185. package/src/internal/upload/manager.test.ts +0 -65
  186. package/src/internal/upload/manager.ts +0 -64
  187. package/src/internal/upload/streamUploader.test.ts +46 -14
  188. package/src/internal/upload/streamUploader.ts +74 -21
  189. package/src/protonDriveClient.ts +46 -9
  190. package/src/protonDrivePhotosClient.ts +193 -8
  191. package/src/protonDrivePublicLinkClient.ts +7 -4
  192. package/src/tests/telemetry.ts +6 -3
  193. package/src/transformers.ts +8 -0
  194. package/dist/diagnostic/sdkDiagnosticFull.js.map +0 -1
  195. package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -15
  196. package/dist/internal/sharingPublic/cryptoCache.js +0 -44
  197. package/dist/internal/sharingPublic/cryptoCache.js.map +0 -1
  198. package/dist/internal/sharingPublic/cryptoService.d.ts +0 -8
  199. package/dist/internal/sharingPublic/cryptoService.js +0 -19
  200. package/dist/internal/sharingPublic/cryptoService.js.map +0 -1
  201. package/dist/internal/sharingPublic/interface.d.ts +0 -5
  202. package/dist/internal/sharingPublic/interface.js +0 -3
  203. package/dist/internal/sharingPublic/interface.js.map +0 -1
  204. package/src/internal/sharingPublic/cryptoCache.ts +0 -45
  205. package/src/internal/sharingPublic/cryptoService.ts +0 -22
  206. package/src/internal/sharingPublic/interface.ts +0 -5
@@ -1,4 +1,4 @@
1
- import { DriveCrypto } from '../../crypto';
1
+ import { DriveCrypto, PrivateKey } from '../../crypto';
2
2
  import {
3
3
  ProtonDriveCryptoCache,
4
4
  ProtonDriveTelemetry,
@@ -11,10 +11,7 @@ import { NodesCache } from '../nodes/cache';
11
11
  import { NodesCryptoCache } from '../nodes/cryptoCache';
12
12
  import { NodesCryptoService } from '../nodes/cryptoService';
13
13
  import { NodesRevisons } from '../nodes/nodesRevisions';
14
- import { SharingPublicAPIService } from './apiService';
15
- import { SharingPublicCryptoCache } from './cryptoCache';
16
14
  import { SharingPublicCryptoReporter } from './cryptoReporter';
17
- import { SharingPublicCryptoService } from './cryptoService';
18
15
  import { SharingPublicNodesAccess } from './nodes';
19
16
  import { SharingPublicSharesManager } from './shares';
20
17
 
@@ -38,12 +35,10 @@ export function initSharingPublicModule(
38
35
  account: ProtonDriveAccount,
39
36
  url: string,
40
37
  token: string,
41
- password: string,
38
+ publicShareKey: PrivateKey,
39
+ publicRootNodeUid: string,
42
40
  ) {
43
- const api = new SharingPublicAPIService(telemetry.getLogger('sharingPublic-api'), apiService);
44
- const cryptoCache = new SharingPublicCryptoCache(telemetry.getLogger('sharingPublic-crypto'), driveCryptoCache);
45
- const cryptoService = new SharingPublicCryptoService(driveCrypto, password);
46
- const shares = new SharingPublicSharesManager(api, cryptoCache, cryptoService, account, token);
41
+ const shares = new SharingPublicSharesManager(account, publicShareKey, publicRootNodeUid);
47
42
  const nodes = initSharingPublicNodesModule(
48
43
  telemetry,
49
44
  apiService,
@@ -54,6 +49,8 @@ export function initSharingPublicModule(
54
49
  shares,
55
50
  url,
56
51
  token,
52
+ publicShareKey,
53
+ publicRootNodeUid,
57
54
  );
58
55
 
59
56
  return {
@@ -78,14 +75,17 @@ export function initSharingPublicNodesModule(
78
75
  sharesService: SharingPublicSharesManager,
79
76
  url: string,
80
77
  token: string,
78
+ publicShareKey: PrivateKey,
79
+ publicRootNodeUid: string,
81
80
  ) {
82
- const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService);
81
+ const clientUid = undefined; // No client UID for public context yet.
82
+ const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService, clientUid);
83
83
  const cache = new NodesCache(telemetry.getLogger('nodes-cache'), driveEntitiesCache);
84
84
  const cryptoCache = new NodesCryptoCache(telemetry.getLogger('nodes-cache'), driveCryptoCache);
85
85
  const cryptoReporter = new SharingPublicCryptoReporter(telemetry);
86
86
  const cryptoService = new NodesCryptoService(telemetry, driveCrypto, account, cryptoReporter);
87
87
  const nodesAccess = new SharingPublicNodesAccess(
88
- telemetry.getLogger('nodes'),
88
+ telemetry,
89
89
  api,
90
90
  cache,
91
91
  cryptoCache,
@@ -93,6 +93,8 @@ export function initSharingPublicNodesModule(
93
93
  sharesService,
94
94
  url,
95
95
  token,
96
+ publicShareKey,
97
+ publicRootNodeUid,
96
98
  );
97
99
  const nodesRevisions = new NodesRevisons(telemetry.getLogger('nodes'), api, cryptoService, nodesAccess);
98
100
 
@@ -1,16 +1,18 @@
1
- import { Logger } from "../../interface";
2
- import { NodeAPIService } from "../nodes/apiService";
3
- import { NodesCache } from "../nodes/cache";
4
- import { NodesCryptoCache } from "../nodes/cryptoCache";
5
- import { NodesCryptoService } from "../nodes/cryptoService";
6
- import { NodesAccess } from "../nodes/nodesAccess";
7
- import { isProtonDocument, isProtonSheet } from "../nodes/mediaTypes";
8
- import { splitNodeUid } from "../uids";
9
- import { SharingPublicSharesManager } from "./shares";
1
+ import { ProtonDriveTelemetry } from '../../interface';
2
+ import { NodeAPIService } from '../nodes/apiService';
3
+ import { NodesCache } from '../nodes/cache';
4
+ import { NodesCryptoCache } from '../nodes/cryptoCache';
5
+ import { NodesCryptoService } from '../nodes/cryptoService';
6
+ import { NodesAccess } from '../nodes/nodesAccess';
7
+ import { isProtonDocument, isProtonSheet } from '../nodes/mediaTypes';
8
+ import { splitNodeUid } from '../uids';
9
+ import { SharingPublicSharesManager } from './shares';
10
+ import { DecryptedNode, DecryptedNodeKeys } from '../nodes/interface';
11
+ import { PrivateKey } from '../../crypto';
10
12
 
11
13
  export class SharingPublicNodesAccess extends NodesAccess {
12
14
  constructor(
13
- logger: Logger,
15
+ telemetry: ProtonDriveTelemetry,
14
16
  apiService: NodeAPIService,
15
17
  cache: NodesCache,
16
18
  cryptoCache: NodesCryptoCache,
@@ -18,9 +20,29 @@ export class SharingPublicNodesAccess extends NodesAccess {
18
20
  sharesService: SharingPublicSharesManager,
19
21
  private url: string,
20
22
  private token: string,
23
+ private publicShareKey: PrivateKey,
24
+ private publicRootNodeUid: string,
21
25
  ) {
22
- super(logger, apiService, cache, cryptoCache, cryptoService, sharesService);
26
+ super(telemetry, apiService, cache, cryptoCache, cryptoService, sharesService);
23
27
  this.token = token;
28
+ this.publicShareKey = publicShareKey;
29
+ this.publicRootNodeUid = publicRootNodeUid;
30
+ }
31
+
32
+ async getParentKeys(
33
+ node: Pick<DecryptedNode, 'uid' | 'parentUid' | 'shareId'>,
34
+ ): Promise<Pick<DecryptedNodeKeys, 'key' | 'hashKey'>> {
35
+ // If we reached the root node of the public link, return the public
36
+ // share key even if user has access to the parent node. We do not
37
+ // support access to nodes outside of the public link context.
38
+ // For other nodes, the client must use the main SDK.
39
+ if (node.uid === this.publicRootNodeUid) {
40
+ return {
41
+ key: this.publicShareKey,
42
+ };
43
+ }
44
+
45
+ return super.getParentKeys(node);
24
46
  }
25
47
 
26
48
  async getNodeUrl(nodeUid: string): Promise<string> {
@@ -1,5 +1,7 @@
1
- import { DriveAPIService, drivePaths } from '../../apiService';
2
- import { PublicLinkInfo, PublicLinkSrpAuth } from './interface';
1
+ import { Logger } from '../../../interface';
2
+ import { DriveAPIService, drivePaths, permissionsToMemberRole } from '../../apiService';
3
+ import { makeNodeUid } from '../../uids';
4
+ import { PublicLinkInfo, PublicLinkSrpAuth, PublicLinkSession, EncryptedShareCrypto } from './interface';
3
5
 
4
6
  type GetPublicLinkInfoResponse =
5
7
  drivePaths['/drive/urls/{token}/info']['get']['responses']['200']['content']['application/json'];
@@ -18,7 +20,11 @@ type PostPublicLinkAuthResponse =
18
20
  * and vice versa. It should not contain any business logic.
19
21
  */
20
22
  export class SharingPublicSessionAPIService {
21
- constructor(private apiService: DriveAPIService) {
23
+ constructor(
24
+ private logger: Logger,
25
+ private apiService: DriveAPIService,
26
+ ) {
27
+ this.logger = logger;
22
28
  this.apiService = apiService;
23
29
  }
24
30
 
@@ -38,6 +44,13 @@ export class SharingPublicSessionAPIService {
38
44
  isCustomPasswordProtected: (response.Flags & 1) === 1,
39
45
  isLegacy: response.Flags === 0 || response.Flags === 1,
40
46
  vendorType: response.VendorType,
47
+ directAccess: response.DirectAccess
48
+ ? {
49
+ nodeUid: makeNodeUid(response.DirectAccess.VolumeID, response.DirectAccess.LinkID),
50
+ directRole: permissionsToMemberRole(this.logger, response.DirectAccess.DirectPermissions),
51
+ publicRole: permissionsToMemberRole(this.logger, response.DirectAccess.PublicPermissions),
52
+ }
53
+ : undefined,
41
54
  };
42
55
  }
43
56
 
@@ -52,9 +65,9 @@ export class SharingPublicSessionAPIService {
52
65
  token: string,
53
66
  srp: PublicLinkSrpAuth,
54
67
  ): Promise<{
55
- serverProof: string;
56
- sessionUid: string;
57
- sessionAccessToken?: string;
68
+ session: PublicLinkSession;
69
+ encryptedShare: EncryptedShareCrypto;
70
+ rootUid: string;
58
71
  }> {
59
72
  const response = await this.apiService.post<PostPublicLinkAuthRequest, PostPublicLinkAuthResponse>(
60
73
  `drive/urls/${token}/auth`,
@@ -66,9 +79,18 @@ export class SharingPublicSessionAPIService {
66
79
  );
67
80
 
68
81
  return {
69
- serverProof: response.ServerProof,
70
- sessionUid: response.UID,
71
- sessionAccessToken: response.AccessToken,
82
+ session: {
83
+ serverProof: response.ServerProof,
84
+ sessionUid: response.UID,
85
+ sessionAccessToken: response.AccessToken,
86
+ },
87
+ encryptedShare: {
88
+ base64UrlPasswordSalt: response.Share.SharePasswordSalt,
89
+ armoredKey: response.Share.ShareKey,
90
+ armoredPassphrase: response.Share.SharePassphrase,
91
+ publicRole: permissionsToMemberRole(this.logger, response.Share.PublicPermissions),
92
+ },
93
+ rootUid: makeNodeUid(response.Share.VolumeID, response.Share.LinkID),
72
94
  };
73
95
  }
74
96
  }
@@ -1,8 +1,15 @@
1
+ import { MemberRole } from '../../../interface';
2
+
1
3
  export type PublicLinkInfo = {
2
4
  srp: PublicLinkSrpInfo;
3
5
  isCustomPasswordProtected: boolean;
4
6
  isLegacy: boolean;
5
7
  vendorType: number;
8
+ directAccess?: {
9
+ nodeUid: string;
10
+ directRole: MemberRole;
11
+ publicRole: MemberRole;
12
+ };
6
13
  };
7
14
 
8
15
  export type PublicLinkSrpInfo = {
@@ -18,3 +25,16 @@ export type PublicLinkSrpAuth = {
18
25
  clientEphemeral: string;
19
26
  srpSession: string;
20
27
  };
28
+
29
+ export type PublicLinkSession = {
30
+ serverProof: string;
31
+ sessionUid: string;
32
+ sessionAccessToken?: string;
33
+ };
34
+
35
+ export type EncryptedShareCrypto = {
36
+ base64UrlPasswordSalt: string;
37
+ armoredKey: string;
38
+ armoredPassphrase: string;
39
+ publicRole: MemberRole;
40
+ };
@@ -1,9 +1,9 @@
1
- import { ProtonDriveHTTPClient } from '../../../interface';
2
- import { SRPModule } from '../../../crypto';
1
+ import { MemberRole, ProtonDriveHTTPClient, ProtonDriveTelemetry } from '../../../interface';
2
+ import { DriveCrypto, PrivateKey, SRPModule } from '../../../crypto';
3
3
  import { DriveAPIService } from '../../apiService';
4
4
  import { SharingPublicSessionAPIService } from './apiService';
5
5
  import { SharingPublicSessionHttpClient } from './httpClient';
6
- import { PublicLinkInfo } from './interface';
6
+ import { EncryptedShareCrypto, PublicLinkInfo } from './interface';
7
7
  import { SharingPublicLinkSession } from './session';
8
8
  import { getTokenAndPasswordFromUrl } from './url';
9
9
 
@@ -18,14 +18,17 @@ export class SharingPublicSessionManager {
18
18
  private infosPerToken: Map<string, PublicLinkInfo> = new Map();
19
19
 
20
20
  constructor(
21
+ telemetry: ProtonDriveTelemetry,
21
22
  private httpClient: ProtonDriveHTTPClient,
22
- apiService: DriveAPIService,
23
+ private driveCrypto: DriveCrypto,
23
24
  private srpModule: SRPModule,
25
+ apiService: DriveAPIService,
24
26
  ) {
25
27
  this.httpClient = httpClient;
28
+ this.driveCrypto = driveCrypto;
26
29
  this.srpModule = srpModule;
27
30
 
28
- this.api = new SharingPublicSessionAPIService(apiService);
31
+ this.api = new SharingPublicSessionAPIService(telemetry.getLogger('sharingPublicSession'), apiService);
29
32
  }
30
33
 
31
34
  /**
@@ -42,6 +45,11 @@ export class SharingPublicSessionManager {
42
45
  isCustomPasswordProtected: boolean;
43
46
  isLegacy: boolean;
44
47
  vendorType: number;
48
+ directAccess?: {
49
+ nodeUid: string;
50
+ directRole: MemberRole;
51
+ publicRole: MemberRole;
52
+ };
45
53
  }> {
46
54
  const { token } = getTokenAndPasswordFromUrl(url);
47
55
 
@@ -52,6 +60,7 @@ export class SharingPublicSessionManager {
52
60
  isCustomPasswordProtected: info.isCustomPasswordProtected,
53
61
  isLegacy: info.isLegacy,
54
62
  vendorType: info.vendorType,
63
+ directAccess: info.directAccess,
55
64
  };
56
65
  }
57
66
 
@@ -73,8 +82,9 @@ export class SharingPublicSessionManager {
73
82
  customPassword?: string,
74
83
  ): Promise<{
75
84
  token: string;
76
- password: string;
77
85
  httpClient: SharingPublicSessionHttpClient;
86
+ shareKey: PrivateKey;
87
+ rootUid: string;
78
88
  }> {
79
89
  const { token, password: urlPassword } = getTokenAndPasswordFromUrl(url);
80
90
 
@@ -86,12 +96,25 @@ export class SharingPublicSessionManager {
86
96
  const password = `${urlPassword}${customPassword || ''}`;
87
97
 
88
98
  const session = new SharingPublicLinkSession(this.api, this.srpModule, token, password);
89
- await session.auth(info.srp);
99
+ const { encryptedShare, rootUid } = await session.auth(info.srp);
100
+
101
+ const shareKey = await this.decryptShareKey(encryptedShare, password);
90
102
 
91
103
  return {
92
104
  token,
93
- password,
94
105
  httpClient: new SharingPublicSessionHttpClient(this.httpClient, session),
106
+ shareKey,
107
+ rootUid,
95
108
  };
96
109
  }
110
+
111
+ private async decryptShareKey(encryptedShare: EncryptedShareCrypto, password: string): Promise<PrivateKey> {
112
+ const { key: shareKey } = await this.driveCrypto.decryptKeyWithSrpPassword(
113
+ password,
114
+ encryptedShare.base64UrlPasswordSalt,
115
+ encryptedShare.armoredKey,
116
+ encryptedShare.armoredPassphrase,
117
+ );
118
+ return shareKey;
119
+ }
97
120
  }
@@ -1,6 +1,6 @@
1
1
  import { SRPModule } from '../../../crypto';
2
2
  import { SharingPublicSessionAPIService } from './apiService';
3
- import { PublicLinkInfo, PublicLinkSrpInfo } from './interface';
3
+ import { EncryptedShareCrypto, PublicLinkInfo, PublicLinkSrpInfo } from './interface';
4
4
 
5
5
  /**
6
6
  * Session for a public link.
@@ -33,7 +33,7 @@ export class SharingPublicLinkSession {
33
33
  return this.apiService.initPublicLinkSession(this.token);
34
34
  }
35
35
 
36
- async auth(srp: PublicLinkSrpInfo): Promise<void> {
36
+ async auth(srp: PublicLinkSrpInfo): Promise<{ encryptedShare: EncryptedShareCrypto; rootUid: string }> {
37
37
  const { expectedServerProof, clientProof, clientEphemeral } = await this.srpModule.getSrp(
38
38
  srp.version,
39
39
  srp.modulus,
@@ -48,12 +48,17 @@ export class SharingPublicLinkSession {
48
48
  srpSession: srp.srpSession,
49
49
  });
50
50
 
51
- if (auth.serverProof !== expectedServerProof) {
51
+ if (auth.session.serverProof !== expectedServerProof) {
52
52
  throw new Error('Invalid server proof');
53
53
  }
54
54
 
55
- this.sessionUid = auth.sessionUid;
56
- this.sessionAccessToken = auth.sessionAccessToken;
55
+ this.sessionUid = auth.session.sessionUid;
56
+ this.sessionAccessToken = auth.session.sessionAccessToken;
57
+
58
+ return {
59
+ encryptedShare: auth.encryptedShare,
60
+ rootUid: auth.rootUid,
61
+ };
57
62
  }
58
63
 
59
64
  /**
@@ -1,9 +1,6 @@
1
1
  import { PrivateKey } from '../../crypto';
2
2
  import { MetricVolumeType, ProtonDriveAccount } from '../../interface';
3
3
  import { splitNodeUid } from '../uids';
4
- import { SharingPublicAPIService } from './apiService';
5
- import { SharingPublicCryptoCache } from './cryptoCache';
6
- import { SharingPublicCryptoService } from './cryptoService';
7
4
 
8
5
  /**
9
6
  * Provides high-level actions for managing public link share.
@@ -12,57 +9,24 @@ import { SharingPublicCryptoService } from './cryptoService';
12
9
  * service so it can be used in the same way in various modules that use shares.
13
10
  */
14
11
  export class SharingPublicSharesManager {
15
- private promisePublicLinkRoot?: Promise<{
16
- rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
17
- shareKey: PrivateKey;
18
- }>;
19
-
20
12
  constructor(
21
- private apiService: SharingPublicAPIService,
22
- private cryptoCache: SharingPublicCryptoCache,
23
- private cryptoService: SharingPublicCryptoService,
24
13
  private account: ProtonDriveAccount,
25
- private token: string,
14
+ private publicShareKey: PrivateKey,
15
+ private publicRootNodeUid: string,
26
16
  ) {
27
- this.apiService = apiService;
28
- this.cryptoCache = cryptoCache;
29
- this.cryptoService = cryptoService;
30
17
  this.account = account;
31
- this.token = token;
18
+ this.publicShareKey = publicShareKey;
19
+ this.publicRootNodeUid = publicRootNodeUid;
32
20
  }
33
21
 
34
22
  // TODO: Rename to getRootIDs everywhere.
35
23
  async getOwnVolumeIDs(): Promise<{ volumeId: string; rootNodeId: string; rootNodeUid: string }> {
36
- const { rootIds } = await this.getPublicLinkRoot();
37
- return rootIds;
24
+ const { volumeId, nodeId: rootNodeId } = splitNodeUid(this.publicRootNodeUid);
25
+ return { volumeId, rootNodeId, rootNodeUid: this.publicRootNodeUid };
38
26
  }
39
27
 
40
28
  async getSharePrivateKey(): Promise<PrivateKey> {
41
- const { shareKey } = await this.getPublicLinkRoot();
42
- return shareKey;
43
- }
44
-
45
- private async getPublicLinkRoot(): Promise<{
46
- rootIds: { volumeId: string; rootNodeId: string; rootNodeUid: string };
47
- shareKey: PrivateKey;
48
- }> {
49
- if (!this.promisePublicLinkRoot) {
50
- this.promisePublicLinkRoot = (async () => {
51
- const { encryptedNode, encryptedShare } = await this.apiService.getPublicLinkRoot(this.token);
52
-
53
- const { volumeId, nodeId: rootNodeId } = splitNodeUid(encryptedNode.uid);
54
-
55
- const shareKey = await this.cryptoService.decryptPublicLinkShareKey(encryptedShare);
56
- await this.cryptoCache.setShareKey(shareKey);
57
-
58
- return {
59
- rootIds: { volumeId, rootNodeId, rootNodeUid: encryptedNode.uid },
60
- shareKey,
61
- };
62
- })();
63
- }
64
-
65
- return this.promisePublicLinkRoot;
29
+ return this.publicShareKey;
66
30
  }
67
31
 
68
32
  async getContextShareMemberEmailKey(): Promise<{
@@ -6,13 +6,6 @@ import { splitNodeUid, makeNodeUid, splitNodeRevisionUid, makeNodeRevisionUid }
6
6
  import { UploadTokens } from './interface';
7
7
  import { ThumbnailType } from '../../interface';
8
8
 
9
- type PostCheckAvailableHashesRequest = Extract<
10
- drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['requestBody'],
11
- { content: object }
12
- >['content']['application/json'];
13
- type PostCheckAvailableHashesResponse =
14
- drivePaths['/drive/v2/volumes/{volumeID}/links/{linkID}/checkAvailableHashes']['post']['responses']['200']['content']['application/json'];
15
-
16
9
  type PostCreateDraftRequest = Extract<
17
10
  drivePaths['/drive/v2/volumes/{volumeID}/files']['post']['requestBody'],
18
11
  { content: object }
@@ -60,38 +53,6 @@ export class UploadAPIService {
60
53
  this.clientUid = clientUid;
61
54
  }
62
55
 
63
- async checkAvailableHashes(
64
- parentNodeUid: string,
65
- hashes: string[],
66
- ): Promise<{
67
- availalbleHashes: string[];
68
- pendingHashes: {
69
- hash: string;
70
- nodeUid: string;
71
- revisionUid: string;
72
- clientUid?: string;
73
- }[];
74
- }> {
75
- const { volumeId, nodeId: parentNodeId } = splitNodeUid(parentNodeUid);
76
- const result = await this.apiService.post<PostCheckAvailableHashesRequest, PostCheckAvailableHashesResponse>(
77
- `drive/v2/volumes/${volumeId}/links/${parentNodeId}/checkAvailableHashes`,
78
- {
79
- Hashes: hashes,
80
- ClientUID: this.clientUid ? [this.clientUid] : null,
81
- },
82
- );
83
-
84
- return {
85
- availalbleHashes: result.AvailableHashes,
86
- pendingHashes: result.PendingHashes.map((hash) => ({
87
- hash: hash.Hash,
88
- nodeUid: makeNodeUid(volumeId, hash.LinkID),
89
- revisionUid: makeNodeRevisionUid(volumeId, hash.LinkID, hash.RevisionID),
90
- clientUid: hash.ClientUID || undefined,
91
- })),
92
- };
93
- }
94
-
95
56
  async createDraft(
96
57
  parentNodeUid: string,
97
58
  node: {
@@ -1,11 +1,23 @@
1
+ import { AbortError } from '../../errors';
1
2
  import { waitForCondition } from '../wait';
2
3
 
3
4
  export class UploadController {
4
5
  private paused = false;
5
- public promise?: Promise<{ nodeRevisionUid: string, nodeUid: string }>;
6
+ public promise?: Promise<{ nodeRevisionUid: string; nodeUid: string }>;
6
7
 
7
- async waitIfPaused(): Promise<void> {
8
- await waitForCondition(() => !this.paused);
8
+ constructor(private signal?: AbortSignal) {
9
+ this.signal = signal;
10
+ }
11
+
12
+ async waitWhilePaused(): Promise<void> {
13
+ try {
14
+ await waitForCondition(() => !this.paused, this.signal);
15
+ } catch (error) {
16
+ if (error instanceof AbortError) {
17
+ return;
18
+ }
19
+ throw error;
20
+ }
9
21
  }
10
22
 
11
23
  pause(): void {
@@ -16,7 +28,7 @@ export class UploadController {
16
28
  this.paused = false;
17
29
  }
18
30
 
19
- async completion(): Promise<{ nodeRevisionUid: string, nodeUid: string }> {
31
+ async completion(): Promise<{ nodeRevisionUid: string; nodeUid: string }> {
20
32
  if (!this.promise) {
21
33
  throw new Error('UploadController.completion() called before upload started');
22
34
  }
@@ -40,15 +40,6 @@ export class UploadCryptoService {
40
40
  };
41
41
  }
42
42
 
43
- async generateNameHashes(parentHashKey: Uint8Array, names: string[]): Promise<{ name: string; hash: string }[]> {
44
- return Promise.all(
45
- names.map(async (name) => ({
46
- name,
47
- hash: await this.driveCrypto.generateLookupHash(name, parentHashKey),
48
- })),
49
- );
50
- }
51
-
52
43
  async encryptThumbnail(
53
44
  nodeRevisionDraftKeys: NodeRevisionDraftKeys,
54
45
  thumbnail: Thumbnail,
@@ -43,7 +43,7 @@ class Uploader {
43
43
  });
44
44
  }
45
45
 
46
- this.controller = new UploadController();
46
+ this.controller = new UploadController(this.abortController.signal);
47
47
  }
48
48
 
49
49
  async uploadFromFile(
@@ -120,7 +120,7 @@ class Uploader {
120
120
  this.metadata,
121
121
  onFinish,
122
122
  this.controller,
123
- this.signal,
123
+ this.abortController,
124
124
  );
125
125
  }
126
126
 
@@ -176,11 +176,6 @@ export class FileUploader extends Uploader {
176
176
  blockVerifier,
177
177
  };
178
178
  }
179
-
180
- async getAvailableName(): Promise<string> {
181
- const availableName = await this.manager.findAvailableName(this.parentFolderUid, this.name);
182
- return availableName;
183
- }
184
179
  }
185
180
 
186
181
  /**
@@ -26,10 +26,6 @@ describe('UploadManager', () => {
26
26
  nodeRevisionUid: 'newNode:nodeRevisionUid',
27
27
  }),
28
28
  deleteDraft: jest.fn(),
29
- checkAvailableHashes: jest.fn().mockResolvedValue({
30
- availalbleHashes: ['name1Hash'],
31
- pendingHashes: [],
32
- }),
33
29
  commitDraftRevision: jest.fn(),
34
30
  };
35
31
  // @ts-expect-error No need to implement all methods for mocking
@@ -58,20 +54,6 @@ describe('UploadManager', () => {
58
54
  email: 'signatureEmail',
59
55
  },
60
56
  }),
61
- generateNameHashes: jest.fn().mockResolvedValue([
62
- {
63
- name: 'name1',
64
- hash: 'name1Hash',
65
- },
66
- {
67
- name: 'name2',
68
- hash: 'name2Hash',
69
- },
70
- {
71
- name: 'name3',
72
- hash: 'name3Hash',
73
- },
74
- ]),
75
57
  commitFile: jest.fn().mockResolvedValue({
76
58
  armoredManifestSignature: 'newNode:armoredManifestSignature',
77
59
  signatureEmail: 'signatureEmail',
@@ -280,53 +262,6 @@ describe('UploadManager', () => {
280
262
  });
281
263
  });
282
264
 
283
- describe('findAvailableName', () => {
284
- it('should find available name', async () => {
285
- apiService.checkAvailableHashes = jest.fn().mockImplementation(() => {
286
- return {
287
- availalbleHashes: ['name3Hash'],
288
- pendingHashes: [],
289
- };
290
- });
291
-
292
- const result = await manager.findAvailableName('parentUid', 'name');
293
- expect(result).toBe('name3');
294
- expect(apiService.checkAvailableHashes).toHaveBeenCalledTimes(1);
295
- expect(apiService.checkAvailableHashes).toHaveBeenCalledWith('parentUid', [
296
- 'name1Hash',
297
- 'name2Hash',
298
- 'name3Hash',
299
- ]);
300
- });
301
-
302
- it('should find available name with multiple pages', async () => {
303
- let firstCall = false;
304
- apiService.checkAvailableHashes = jest.fn().mockImplementation(() => {
305
- if (!firstCall) {
306
- firstCall = true;
307
- return {
308
- // First page has no available hashes
309
- availalbleHashes: [],
310
- pendingHashes: [],
311
- };
312
- }
313
- return {
314
- availalbleHashes: ['name3Hash'],
315
- pendingHashes: [],
316
- };
317
- });
318
-
319
- const result = await manager.findAvailableName('parentUid', 'name');
320
- expect(result).toBe('name3');
321
- expect(apiService.checkAvailableHashes).toHaveBeenCalledTimes(2);
322
- expect(apiService.checkAvailableHashes).toHaveBeenCalledWith('parentUid', [
323
- 'name1Hash',
324
- 'name2Hash',
325
- 'name3Hash',
326
- ]);
327
- });
328
- });
329
-
330
265
  describe('commit draft', () => {
331
266
  const nodeRevisionDraft = {
332
267
  nodeUid: 'newNode:nodeUid',