@protontech/drive-sdk 0.5.0 → 0.5.1

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 (151) 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 +1 -1
  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/internal/apiService/driveTypes.d.ts +2571 -2356
  16. package/dist/internal/download/controller.d.ts +2 -0
  17. package/dist/internal/download/controller.js +15 -1
  18. package/dist/internal/download/controller.js.map +1 -1
  19. package/dist/internal/download/fileDownloader.js +6 -1
  20. package/dist/internal/download/fileDownloader.js.map +1 -1
  21. package/dist/internal/nodes/apiService.js +27 -12
  22. package/dist/internal/nodes/apiService.js.map +1 -1
  23. package/dist/internal/nodes/apiService.test.js +60 -2
  24. package/dist/internal/nodes/apiService.test.js.map +1 -1
  25. package/dist/internal/nodes/debouncer.d.ts +3 -2
  26. package/dist/internal/nodes/debouncer.js +16 -4
  27. package/dist/internal/nodes/debouncer.js.map +1 -1
  28. package/dist/internal/nodes/debouncer.test.js +20 -12
  29. package/dist/internal/nodes/debouncer.test.js.map +1 -1
  30. package/dist/internal/nodes/extendedAttributes.js +2 -2
  31. package/dist/internal/nodes/extendedAttributes.js.map +1 -1
  32. package/dist/internal/nodes/index.js +1 -1
  33. package/dist/internal/nodes/index.js.map +1 -1
  34. package/dist/internal/nodes/nodesAccess.d.ts +5 -4
  35. package/dist/internal/nodes/nodesAccess.js +6 -5
  36. package/dist/internal/nodes/nodesAccess.js.map +1 -1
  37. package/dist/internal/nodes/nodesAccess.test.js +17 -5
  38. package/dist/internal/nodes/nodesAccess.test.js.map +1 -1
  39. package/dist/internal/nodes/nodesManagement.d.ts +2 -2
  40. package/dist/internal/nodes/nodesManagement.js +5 -3
  41. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  42. package/dist/internal/nodes/nodesManagement.test.js +3 -1
  43. package/dist/internal/nodes/nodesManagement.test.js.map +1 -1
  44. package/dist/internal/photos/apiService.js +9 -20
  45. package/dist/internal/photos/apiService.js.map +1 -1
  46. package/dist/internal/photos/upload.js +7 -1
  47. package/dist/internal/photos/upload.js.map +1 -1
  48. package/dist/internal/sharing/apiService.d.ts +1 -1
  49. package/dist/internal/sharing/apiService.js +2 -2
  50. package/dist/internal/sharing/apiService.js.map +1 -1
  51. package/dist/internal/sharing/sharingManagement.d.ts +4 -1
  52. package/dist/internal/sharing/sharingManagement.js +7 -4
  53. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  54. package/dist/internal/sharingPublic/apiService.d.ts +8 -10
  55. package/dist/internal/sharingPublic/apiService.js +9 -63
  56. package/dist/internal/sharingPublic/apiService.js.map +1 -1
  57. package/dist/internal/sharingPublic/index.d.ts +3 -3
  58. package/dist/internal/sharingPublic/index.js +5 -11
  59. package/dist/internal/sharingPublic/index.js.map +1 -1
  60. package/dist/internal/sharingPublic/nodes.d.ts +13 -8
  61. package/dist/internal/sharingPublic/nodes.js +20 -2
  62. package/dist/internal/sharingPublic/nodes.js.map +1 -1
  63. package/dist/internal/sharingPublic/session/apiService.d.ts +7 -5
  64. package/dist/internal/sharingPublic/session/apiService.js +25 -4
  65. package/dist/internal/sharingPublic/session/apiService.js.map +1 -1
  66. package/dist/internal/sharingPublic/session/interface.d.ts +17 -0
  67. package/dist/internal/sharingPublic/session/manager.d.ts +12 -4
  68. package/dist/internal/sharingPublic/session/manager.js +14 -4
  69. package/dist/internal/sharingPublic/session/manager.js.map +1 -1
  70. package/dist/internal/sharingPublic/session/session.d.ts +5 -2
  71. package/dist/internal/sharingPublic/session/session.js +7 -3
  72. package/dist/internal/sharingPublic/session/session.js.map +1 -1
  73. package/dist/internal/sharingPublic/shares.d.ts +3 -10
  74. package/dist/internal/sharingPublic/shares.js +10 -33
  75. package/dist/internal/sharingPublic/shares.js.map +1 -1
  76. package/dist/internal/upload/controller.d.ts +3 -1
  77. package/dist/internal/upload/controller.js +16 -2
  78. package/dist/internal/upload/controller.js.map +1 -1
  79. package/dist/internal/upload/fileUploader.js +2 -2
  80. package/dist/internal/upload/fileUploader.js.map +1 -1
  81. package/dist/internal/upload/streamUploader.d.ts +4 -3
  82. package/dist/internal/upload/streamUploader.js +61 -18
  83. package/dist/internal/upload/streamUploader.js.map +1 -1
  84. package/dist/internal/upload/streamUploader.test.js +38 -12
  85. package/dist/internal/upload/streamUploader.test.js.map +1 -1
  86. package/dist/protonDriveClient.d.ts +8 -3
  87. package/dist/protonDriveClient.js +7 -6
  88. package/dist/protonDriveClient.js.map +1 -1
  89. package/dist/protonDrivePublicLinkClient.d.ts +4 -3
  90. package/dist/protonDrivePublicLinkClient.js +2 -2
  91. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  92. package/dist/tests/telemetry.d.ts +4 -2
  93. package/dist/tests/telemetry.js +3 -1
  94. package/dist/tests/telemetry.js.map +1 -1
  95. package/dist/transformers.d.ts +3 -2
  96. package/dist/transformers.js +6 -0
  97. package/dist/transformers.js.map +1 -1
  98. package/package.json +1 -1
  99. package/src/diagnostic/{sdkDiagnosticFull.ts → diagnostic.ts} +10 -6
  100. package/src/diagnostic/index.ts +3 -5
  101. package/src/diagnostic/interface.ts +39 -0
  102. package/src/diagnostic/sdkDiagnostic.ts +110 -9
  103. package/src/interface/index.ts +1 -0
  104. package/src/interface/nodes.ts +3 -0
  105. package/src/interface/telemetry.ts +5 -0
  106. package/src/internal/apiService/driveTypes.ts +2698 -2529
  107. package/src/internal/download/controller.ts +13 -1
  108. package/src/internal/download/fileDownloader.ts +8 -1
  109. package/src/internal/nodes/apiService.test.ts +64 -0
  110. package/src/internal/nodes/apiService.ts +38 -17
  111. package/src/internal/nodes/debouncer.test.ts +25 -13
  112. package/src/internal/nodes/debouncer.ts +20 -4
  113. package/src/internal/nodes/extendedAttributes.ts +2 -2
  114. package/src/internal/nodes/index.ts +1 -8
  115. package/src/internal/nodes/nodesAccess.test.ts +17 -5
  116. package/src/internal/nodes/nodesAccess.ts +15 -5
  117. package/src/internal/nodes/nodesManagement.test.ts +3 -1
  118. package/src/internal/nodes/nodesManagement.ts +11 -5
  119. package/src/internal/photos/apiService.ts +12 -29
  120. package/src/internal/photos/upload.ts +19 -1
  121. package/src/internal/sharing/apiService.ts +2 -2
  122. package/src/internal/sharing/sharingManagement.ts +7 -4
  123. package/src/internal/sharingPublic/apiService.ts +23 -77
  124. package/src/internal/sharingPublic/index.ts +11 -10
  125. package/src/internal/sharingPublic/nodes.ts +33 -11
  126. package/src/internal/sharingPublic/session/apiService.ts +31 -9
  127. package/src/internal/sharingPublic/session/interface.ts +20 -0
  128. package/src/internal/sharingPublic/session/manager.ts +31 -8
  129. package/src/internal/sharingPublic/session/session.ts +10 -5
  130. package/src/internal/sharingPublic/shares.ts +7 -43
  131. package/src/internal/upload/controller.ts +16 -4
  132. package/src/internal/upload/fileUploader.ts +2 -2
  133. package/src/internal/upload/streamUploader.test.ts +46 -14
  134. package/src/internal/upload/streamUploader.ts +74 -21
  135. package/src/protonDriveClient.ts +25 -7
  136. package/src/protonDrivePublicLinkClient.ts +7 -4
  137. package/src/tests/telemetry.ts +6 -3
  138. package/src/transformers.ts +8 -0
  139. package/dist/diagnostic/sdkDiagnosticFull.js.map +0 -1
  140. package/dist/internal/sharingPublic/cryptoCache.d.ts +0 -15
  141. package/dist/internal/sharingPublic/cryptoCache.js +0 -44
  142. package/dist/internal/sharingPublic/cryptoCache.js.map +0 -1
  143. package/dist/internal/sharingPublic/cryptoService.d.ts +0 -8
  144. package/dist/internal/sharingPublic/cryptoService.js +0 -19
  145. package/dist/internal/sharingPublic/cryptoService.js.map +0 -1
  146. package/dist/internal/sharingPublic/interface.d.ts +0 -5
  147. package/dist/internal/sharingPublic/interface.js +0 -3
  148. package/dist/internal/sharingPublic/interface.js.map +0 -1
  149. package/src/internal/sharingPublic/cryptoCache.ts +0 -45
  150. package/src/internal/sharingPublic/cryptoService.ts +0 -22
  151. package/src/internal/sharingPublic/interface.ts +0 -5
@@ -286,7 +286,7 @@ export class SharingManagement {
286
286
 
287
287
  if (!settings) {
288
288
  this.logger.info(`Unsharing node ${nodeUid}`);
289
- await this.deleteShare(currentSharing.share.shareId, nodeUid);
289
+ await this.deleteShareWithForce(currentSharing.share.shareId, nodeUid);
290
290
  return;
291
291
  }
292
292
 
@@ -348,7 +348,7 @@ export class SharingManagement {
348
348
  // update local state immediately.
349
349
  this.logger.info(`Deleting share ${currentSharing.share.shareId} for node ${nodeUid}`);
350
350
  try {
351
- await this.deleteShare(currentSharing.share.shareId, nodeUid);
351
+ await this.deleteShareWithForce(currentSharing.share.shareId, nodeUid);
352
352
  } catch (error: unknown) {
353
353
  // If deleting the share fails, we don't want to throw an error
354
354
  // as it might be a race condition that other client updated
@@ -433,8 +433,11 @@ export class SharingManagement {
433
433
  };
434
434
  }
435
435
 
436
- private async deleteShare(shareId: string, nodeUid: string): Promise<void> {
437
- await this.apiService.deleteShare(shareId);
436
+ /**
437
+ * Deletes the share even if it is not empty.
438
+ */
439
+ private async deleteShareWithForce(shareId: string, nodeUid: string): Promise<void> {
440
+ await this.apiService.deleteShare(shareId, true);
438
441
  await this.nodesService.notifyNodeChanged(nodeUid);
439
442
  if (await this.cache.hasSharedByMeNodeUidsLoaded()) {
440
443
  await this.cache.removeSharedByMeNodeUid(nodeUid);
@@ -1,92 +1,38 @@
1
- import { DriveAPIService, drivePaths, nodeTypeNumberToNodeType } from '../apiService';
2
- import { Logger, MemberRole } from '../../interface';
3
- import { makeNodeUid } from '../uids';
4
- import { EncryptedNode } from '../nodes/interface';
5
- import { EncryptedShareCrypto } from './interface';
1
+ import { DriveAPIService, drivePaths } from '../apiService';
6
2
 
7
- type GetTokenInfoResponse = drivePaths['/drive/urls/{token}']['get']['responses']['200']['content']['application/json'];
3
+ type PostTokenInfoRequest = Extract<
4
+ drivePaths['/drive/v2/urls/{token}/bookmark']['post']['requestBody'],
5
+ { content: object }
6
+ >['content']['application/json'];
7
+ type PostTokenInfoResponse =
8
+ drivePaths['/drive/v2/urls/{token}/bookmark']['post']['responses']['200']['content']['application/json'];
8
9
 
9
10
  /**
10
- * Provides API communication for accessing public link data.
11
+ * Provides API communication for actions on the public link.
11
12
  *
12
13
  * The service is responsible for transforming local objects to API payloads
13
14
  * and vice versa. It should not contain any business logic.
14
15
  */
15
16
  export class SharingPublicAPIService {
16
- constructor(
17
- private logger: Logger,
18
- private apiService: DriveAPIService,
19
- ) {
20
- this.logger = logger;
17
+ constructor(private apiService: DriveAPIService) {
21
18
  this.apiService = apiService;
22
19
  }
23
20
 
24
- async getPublicLinkRoot(token: string): Promise<{
25
- encryptedNode: EncryptedNode;
26
- encryptedShare: EncryptedShareCrypto;
27
- }> {
28
- const response = await this.apiService.get<GetTokenInfoResponse>(`drive/urls/${token}`);
29
- const encryptedNode = tokenToEncryptedNode(this.logger, response.Token);
30
-
31
- return {
32
- encryptedNode: encryptedNode,
33
- encryptedShare: {
34
- base64UrlPasswordSalt: response.Token.SharePasswordSalt,
35
- armoredKey: response.Token.ShareKey,
36
- armoredPassphrase: response.Token.SharePassphrase,
37
- },
38
- };
39
- }
40
- }
41
-
42
- function tokenToEncryptedNode(logger: Logger, token: GetTokenInfoResponse['Token']): EncryptedNode {
43
- const baseNodeMetadata = {
44
- // Internal metadata
45
- encryptedName: token.Name,
46
-
47
- // Basic node metadata
48
- uid: makeNodeUid(token.VolumeID, token.LinkID),
49
- parentUid: undefined,
50
- type: nodeTypeNumberToNodeType(logger, token.LinkType),
51
- creationTime: new Date(), // TODO
52
-
53
- isShared: false,
54
- isSharedPublicly: false,
55
- directRole: MemberRole.Viewer, // TODO
56
- };
57
-
58
- const baseCryptoNodeMetadata = {
59
- signatureEmail: token.SignatureEmail || undefined,
60
- armoredKey: token.NodeKey,
61
- armoredNodePassphrase: token.NodePassphrase,
62
- armoredNodePassphraseSignature: token.NodePassphraseSignature || undefined,
63
- };
64
-
65
- if (token.LinkType === 1 && token.NodeHashKey) {
66
- return {
67
- ...baseNodeMetadata,
68
- encryptedCrypto: {
69
- ...baseCryptoNodeMetadata,
70
- folder: {
71
- armoredHashKey: token.NodeHashKey as string,
21
+ async bookmarkPublicLink(bookmark: {
22
+ token: string;
23
+ encryptedUrlPassword: string;
24
+ addressId: string;
25
+ addressKeyId: string;
26
+ }): Promise<void> {
27
+ await this.apiService.post<PostTokenInfoRequest, PostTokenInfoResponse>(
28
+ `drive/v2/urls/${bookmark.token}/bookmark`,
29
+ {
30
+ BookmarkShareURL: {
31
+ EncryptedUrlPassword: bookmark.encryptedUrlPassword,
32
+ AddressID: bookmark.addressId,
33
+ AddressKeyID: bookmark.addressKeyId,
72
34
  },
73
35
  },
74
- };
36
+ );
75
37
  }
76
-
77
- if (token.LinkType === 2 && token.ContentKeyPacket) {
78
- return {
79
- ...baseNodeMetadata,
80
- totalStorageSize: token.Size || undefined,
81
- mediaType: token.MIMEType || undefined,
82
- encryptedCrypto: {
83
- ...baseCryptoNodeMetadata,
84
- file: {
85
- base64ContentKeyPacket: token.ContentKeyPacket,
86
- },
87
- },
88
- };
89
- }
90
-
91
- throw new Error(`Unknown node type: ${token.LinkType}`);
92
38
  }
@@ -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,6 +75,8 @@ export function initSharingPublicNodesModule(
78
75
  sharesService: SharingPublicSharesManager,
79
76
  url: string,
80
77
  token: string,
78
+ publicShareKey: PrivateKey,
79
+ publicRootNodeUid: string,
81
80
  ) {
82
81
  const api = new NodeAPIService(telemetry.getLogger('nodes-api'), apiService);
83
82
  const cache = new NodesCache(telemetry.getLogger('nodes-cache'), driveEntitiesCache);
@@ -85,7 +84,7 @@ export function initSharingPublicNodesModule(
85
84
  const cryptoReporter = new SharingPublicCryptoReporter(telemetry);
86
85
  const cryptoService = new NodesCryptoService(telemetry, driveCrypto, account, cryptoReporter);
87
86
  const nodesAccess = new SharingPublicNodesAccess(
88
- telemetry.getLogger('nodes'),
87
+ telemetry,
89
88
  api,
90
89
  cache,
91
90
  cryptoCache,
@@ -93,6 +92,8 @@ export function initSharingPublicNodesModule(
93
92
  sharesService,
94
93
  url,
95
94
  token,
95
+ publicShareKey,
96
+ publicRootNodeUid,
96
97
  );
97
98
  const nodesRevisions = new NodesRevisons(telemetry.getLogger('nodes'), api, cryptoService, nodesAccess);
98
99
 
@@ -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<{
@@ -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
  }
@@ -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