@protontech/drive-sdk 0.3.0 → 0.3.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 (121) hide show
  1. package/dist/crypto/interface.d.ts +5 -0
  2. package/dist/diagnostic/httpClient.d.ts +3 -3
  3. package/dist/interface/httpClient.d.ts +5 -5
  4. package/dist/interface/index.d.ts +15 -5
  5. package/dist/internal/apiService/apiService.js +1 -1
  6. package/dist/internal/apiService/apiService.js.map +1 -1
  7. package/dist/internal/apiService/errorCodes.d.ts +1 -0
  8. package/dist/internal/apiService/errorCodes.js.map +1 -1
  9. package/dist/internal/apiService/errors.d.ts +4 -3
  10. package/dist/internal/apiService/errors.js +7 -4
  11. package/dist/internal/apiService/errors.js.map +1 -1
  12. package/dist/internal/apiService/errors.test.js +2 -1
  13. package/dist/internal/apiService/errors.test.js.map +1 -1
  14. package/dist/internal/events/index.d.ts +1 -1
  15. package/dist/internal/nodes/cryptoCache.js +6 -7
  16. package/dist/internal/nodes/cryptoCache.js.map +1 -1
  17. package/dist/internal/nodes/cryptoCache.test.js +4 -7
  18. package/dist/internal/nodes/cryptoCache.test.js.map +1 -1
  19. package/dist/internal/shares/cryptoCache.d.ts +4 -3
  20. package/dist/internal/shares/cryptoCache.js +23 -6
  21. package/dist/internal/shares/cryptoCache.js.map +1 -1
  22. package/dist/internal/shares/cryptoCache.test.js +3 -2
  23. package/dist/internal/shares/cryptoCache.test.js.map +1 -1
  24. package/dist/internal/shares/index.js +1 -1
  25. package/dist/internal/shares/index.js.map +1 -1
  26. package/dist/internal/sharing/cryptoService.js +8 -6
  27. package/dist/internal/sharing/cryptoService.js.map +1 -1
  28. package/dist/internal/sharing/cryptoService.test.js +13 -0
  29. package/dist/internal/sharing/cryptoService.test.js.map +1 -1
  30. package/dist/internal/sharing/index.js +1 -1
  31. package/dist/internal/sharing/index.js.map +1 -1
  32. package/dist/internal/sharing/sharingManagement.d.ts +3 -1
  33. package/dist/internal/sharing/sharingManagement.js +10 -1
  34. package/dist/internal/sharing/sharingManagement.js.map +1 -1
  35. package/dist/internal/sharing/sharingManagement.test.js +32 -1
  36. package/dist/internal/sharing/sharingManagement.test.js.map +1 -1
  37. package/dist/internal/sharingPublic/apiService.d.ts +19 -0
  38. package/dist/internal/sharingPublic/apiService.js +134 -0
  39. package/dist/internal/sharingPublic/apiService.js.map +1 -0
  40. package/dist/internal/sharingPublic/cryptoCache.d.ts +19 -0
  41. package/dist/internal/sharingPublic/cryptoCache.js +72 -0
  42. package/dist/internal/sharingPublic/cryptoCache.js.map +1 -0
  43. package/dist/internal/sharingPublic/cryptoService.d.ts +23 -0
  44. package/dist/internal/sharingPublic/cryptoService.js +120 -0
  45. package/dist/internal/sharingPublic/cryptoService.js.map +1 -0
  46. package/dist/internal/sharingPublic/index.d.ts +15 -0
  47. package/dist/internal/sharingPublic/index.js +27 -0
  48. package/dist/internal/sharingPublic/index.js.map +1 -0
  49. package/dist/internal/sharingPublic/interface.d.ts +48 -0
  50. package/dist/internal/sharingPublic/interface.js +3 -0
  51. package/dist/internal/sharingPublic/interface.js.map +1 -0
  52. package/dist/internal/sharingPublic/manager.d.ts +19 -0
  53. package/dist/internal/sharingPublic/manager.js +79 -0
  54. package/dist/internal/sharingPublic/manager.js.map +1 -0
  55. package/dist/internal/sharingPublic/session/apiService.d.ts +28 -0
  56. package/dist/internal/sharingPublic/session/apiService.js +55 -0
  57. package/dist/internal/sharingPublic/session/apiService.js.map +1 -0
  58. package/dist/internal/sharingPublic/session/httpClient.d.ts +16 -0
  59. package/dist/internal/sharingPublic/session/httpClient.js +41 -0
  60. package/dist/internal/sharingPublic/session/httpClient.js.map +1 -0
  61. package/dist/internal/sharingPublic/session/index.d.ts +1 -0
  62. package/dist/internal/sharingPublic/session/index.js +6 -0
  63. package/dist/internal/sharingPublic/session/index.js.map +1 -0
  64. package/dist/internal/sharingPublic/session/interface.d.ts +18 -0
  65. package/dist/internal/sharingPublic/session/interface.js +3 -0
  66. package/dist/internal/sharingPublic/session/interface.js.map +1 -0
  67. package/dist/internal/sharingPublic/session/manager.d.ts +49 -0
  68. package/dist/internal/sharingPublic/session/manager.js +75 -0
  69. package/dist/internal/sharingPublic/session/manager.js.map +1 -0
  70. package/dist/internal/sharingPublic/session/session.d.ts +34 -0
  71. package/dist/internal/sharingPublic/session/session.js +67 -0
  72. package/dist/internal/sharingPublic/session/session.js.map +1 -0
  73. package/dist/internal/sharingPublic/session/url.d.ts +12 -0
  74. package/dist/internal/sharingPublic/session/url.js +23 -0
  75. package/dist/internal/sharingPublic/session/url.js.map +1 -0
  76. package/dist/internal/sharingPublic/session/url.test.d.ts +1 -0
  77. package/dist/internal/sharingPublic/session/url.test.js +59 -0
  78. package/dist/internal/sharingPublic/session/url.test.js.map +1 -0
  79. package/dist/protonDriveClient.d.ts +18 -3
  80. package/dist/protonDriveClient.js +30 -8
  81. package/dist/protonDriveClient.js.map +1 -1
  82. package/dist/protonDrivePublicLinkClient.d.ts +48 -0
  83. package/dist/protonDrivePublicLinkClient.js +71 -0
  84. package/dist/protonDrivePublicLinkClient.js.map +1 -0
  85. package/package.json +1 -1
  86. package/src/crypto/interface.ts +11 -0
  87. package/src/diagnostic/httpClient.ts +4 -4
  88. package/src/interface/httpClient.ts +5 -5
  89. package/src/interface/index.ts +18 -6
  90. package/src/internal/apiService/apiService.ts +1 -1
  91. package/src/internal/apiService/errorCodes.ts +1 -0
  92. package/src/internal/apiService/errors.test.ts +2 -1
  93. package/src/internal/apiService/errors.ts +15 -4
  94. package/src/internal/events/index.ts +1 -1
  95. package/src/internal/nodes/cryptoCache.test.ts +4 -7
  96. package/src/internal/nodes/cryptoCache.ts +6 -7
  97. package/src/internal/nodes/interface.ts +2 -0
  98. package/src/internal/shares/cryptoCache.test.ts +3 -2
  99. package/src/internal/shares/cryptoCache.ts +26 -7
  100. package/src/internal/shares/index.ts +1 -1
  101. package/src/internal/sharing/cryptoService.test.ts +22 -1
  102. package/src/internal/sharing/cryptoService.ts +8 -6
  103. package/src/internal/sharing/index.ts +1 -0
  104. package/src/internal/sharing/sharingManagement.test.ts +33 -0
  105. package/src/internal/sharing/sharingManagement.ts +9 -0
  106. package/src/internal/sharingPublic/apiService.ts +164 -0
  107. package/src/internal/sharingPublic/cryptoCache.ts +79 -0
  108. package/src/internal/sharingPublic/cryptoService.ts +162 -0
  109. package/src/internal/sharingPublic/index.ts +40 -0
  110. package/src/internal/sharingPublic/interface.ts +59 -0
  111. package/src/internal/sharingPublic/manager.ts +85 -0
  112. package/src/internal/sharingPublic/session/apiService.ts +74 -0
  113. package/src/internal/sharingPublic/session/httpClient.ts +48 -0
  114. package/src/internal/sharingPublic/session/index.ts +1 -0
  115. package/src/internal/sharingPublic/session/interface.ts +20 -0
  116. package/src/internal/sharingPublic/session/manager.ts +97 -0
  117. package/src/internal/sharingPublic/session/session.ts +78 -0
  118. package/src/internal/sharingPublic/session/url.test.ts +72 -0
  119. package/src/internal/sharingPublic/session/url.ts +23 -0
  120. package/src/protonDriveClient.ts +47 -11
  121. package/src/protonDrivePublicLinkClient.ts +121 -0
@@ -0,0 +1,72 @@
1
+ import { ValidationError } from '../../../errors';
2
+ import { getTokenAndPasswordFromUrl } from './url';
3
+
4
+ describe('getTokenAndPasswordFromUrl', () => {
5
+ describe('valid URLs', () => {
6
+ it('should extract token and password from a valid URL', () => {
7
+ const url = 'https://drive.proton.me/urls/abc123#def456';
8
+ const result = getTokenAndPasswordFromUrl(url);
9
+
10
+ expect(result).toEqual({
11
+ token: 'abc123',
12
+ password: 'def456'
13
+ });
14
+ });
15
+
16
+ it('should handle URLs with different domains', () => {
17
+ const url = 'https://example.com/urls/mytoken#mypassword';
18
+ const result = getTokenAndPasswordFromUrl(url);
19
+
20
+ expect(result).toEqual({
21
+ token: 'mytoken',
22
+ password: 'mypassword'
23
+ });
24
+ });
25
+
26
+ it('should handle URLs with query parameters', () => {
27
+ const url = 'https://drive.proton.me/urls/token123?param=value#password456';
28
+ const result = getTokenAndPasswordFromUrl(url);
29
+
30
+ expect(result).toEqual({
31
+ token: 'token123',
32
+ password: 'password456'
33
+ });
34
+ });
35
+ });
36
+
37
+ describe('should throw ValidationError', () => {
38
+ it('when token is missing (no path)', () => {
39
+ const url = 'https://drive.proton.me/#password123';
40
+
41
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
42
+ });
43
+
44
+ it('when token is missing (empty path segment)', () => {
45
+ const url = 'https://drive.proton.me/urls/#password123';
46
+
47
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
48
+ });
49
+
50
+ it('when password is missing (no hash)', () => {
51
+ const url = 'https://drive.proton.me/urls/token123';
52
+
53
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
54
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow('Invalid URL');
55
+ });
56
+
57
+ it('when password is empty (empty hash)', () => {
58
+ const url = 'https://drive.proton.me/urls/token123#';
59
+
60
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow(ValidationError);
61
+ expect(() => getTokenAndPasswordFromUrl(url)).toThrow('Invalid URL');
62
+ });
63
+
64
+ it('for empty string', () => {
65
+ expect(() => getTokenAndPasswordFromUrl('')).toThrow();
66
+ });
67
+
68
+ it('for invalid URL format', () => {
69
+ expect(() => getTokenAndPasswordFromUrl('not-a-url')).toThrow();
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,23 @@
1
+ import { c } from 'ttag';
2
+
3
+ import { ValidationError } from '../../../errors';
4
+
5
+ /**
6
+ * Parse the token and password from the URL.
7
+ *
8
+ * The URL format is: https://drive.proton.me/urls/token#password
9
+ *
10
+ * @param url - The URL of the public link.
11
+ * @returns The token and password.
12
+ */
13
+ export function getTokenAndPasswordFromUrl(url: string): { token: string; password: string } {
14
+ const urlObj = new URL(url);
15
+ const token = urlObj.pathname.split('/').pop();
16
+ const password = urlObj.hash.slice(1);
17
+
18
+ if (!token || !password) {
19
+ throw new ValidationError(c('Error').t`Invalid URL`);
20
+ }
21
+
22
+ return { token, password };
23
+ }
@@ -1,3 +1,5 @@
1
+ import { getConfig } from './config';
2
+ import { DriveCrypto, SessionKey } from './crypto';
1
3
  import {
2
4
  Logger,
3
5
  ProtonDriveClientContructorParameters,
@@ -26,16 +28,6 @@ import {
26
28
  ThumbnailResult,
27
29
  SDKEvent,
28
30
  } from './interface';
29
- import { DriveCrypto, SessionKey } from './crypto';
30
- import { DriveAPIService } from './internal/apiService';
31
- import { initSharesModule } from './internal/shares';
32
- import { initNodesModule } from './internal/nodes';
33
- import { initSharingModule } from './internal/sharing';
34
- import { initDownloadModule } from './internal/download';
35
- import { initUploadModule } from './internal/upload';
36
- import { DriveEventsService, DriveListener } from './internal/events';
37
- import { SDKEvents } from './internal/sdkEvents';
38
- import { getConfig } from './config';
39
31
  import {
40
32
  getUid,
41
33
  getUids,
@@ -45,9 +37,18 @@ import {
45
37
  convertInternalNode,
46
38
  } from './transformers';
47
39
  import { Telemetry } from './telemetry';
40
+ import { DriveAPIService } from './internal/apiService';
48
41
  import { initDevicesModule } from './internal/devices';
42
+ import { initDownloadModule } from './internal/download';
43
+ import { DriveEventsService, DriveListener, EventSubscription } from './internal/events';
44
+ import { initNodesModule } from './internal/nodes';
45
+ import { SDKEvents } from './internal/sdkEvents';
46
+ import { initSharesModule } from './internal/shares';
47
+ import { initSharingModule } from './internal/sharing';
48
+ import { SharingPublicSessionManager } from './internal/sharingPublic';
49
+ import { initUploadModule } from './internal/upload';
49
50
  import { makeNodeUid } from './internal/uids';
50
- import { EventSubscription } from './internal/events/interface';
51
+ import { ProtonDrivePublicLinkClient } from './protonDrivePublicLinkClient';
51
52
 
52
53
  /**
53
54
  * ProtonDriveClient is the main interface for the ProtonDrive SDK.
@@ -66,6 +67,7 @@ export class ProtonDriveClient {
66
67
  private download: ReturnType<typeof initDownloadModule>;
67
68
  private upload: ReturnType<typeof initUploadModule>;
68
69
  private devices: ReturnType<typeof initDevicesModule>;
70
+ private sessionManager: SharingPublicSessionManager;
69
71
 
70
72
  public experimental: {
71
73
  /**
@@ -82,6 +84,20 @@ export class ProtonDriveClient {
82
84
  * This is used by Docs app to encrypt and decrypt document updates.
83
85
  */
84
86
  getDocsKey: (nodeUid: NodeOrUid) => Promise<SessionKey>;
87
+ /**
88
+ * Experimental feature to get the info for a public link
89
+ * required to authenticate the public link.
90
+ */
91
+ getPublicLinkInfo: (url: string) => Promise<{
92
+ isCustomPasswordProtected: boolean;
93
+ isLegacy: boolean;
94
+ vendorType: number;
95
+ }>;
96
+ /**
97
+ * Experimental feature to authenticate a public link and
98
+ * return the client for the public link to access it.
99
+ */
100
+ authPublicLink: (url: string, customPassword?: string) => Promise<ProtonDrivePublicLinkClient>;
85
101
  };
86
102
 
87
103
  constructor({
@@ -167,6 +183,8 @@ export class ProtonDriveClient {
167
183
  latestEventIdProvider,
168
184
  );
169
185
 
186
+ this.sessionManager = new SharingPublicSessionManager(httpClient, apiService, srpModule);
187
+
170
188
  this.experimental = {
171
189
  getNodeUrl: async (nodeUid: NodeOrUid) => {
172
190
  this.logger.debug(`Getting node URL for ${getUid(nodeUid)}`);
@@ -180,6 +198,24 @@ export class ProtonDriveClient {
180
198
  }
181
199
  return keys.contentKeyPacketSessionKey;
182
200
  },
201
+ getPublicLinkInfo: async (url: string) => {
202
+ this.logger.info(`Getting info for public link ${url}`);
203
+ return this.sessionManager.getInfo(url);
204
+ },
205
+ authPublicLink: async (url: string, customPassword?: string) => {
206
+ this.logger.info(`Authenticating public link ${url}`);
207
+ const { httpClient, token, password } = await this.sessionManager.auth(url, customPassword);
208
+ return new ProtonDrivePublicLinkClient({
209
+ httpClient,
210
+ cryptoCache,
211
+ openPGPCryptoModule,
212
+ srpModule,
213
+ config,
214
+ telemetry,
215
+ token,
216
+ password,
217
+ });
218
+ },
183
219
  };
184
220
  }
185
221
 
@@ -0,0 +1,121 @@
1
+ import { getConfig } from './config';
2
+ import { DriveCrypto, OpenPGPCrypto, SRPModule, SessionKey } from './crypto';
3
+ import {
4
+ ProtonDriveHTTPClient,
5
+ ProtonDriveTelemetry,
6
+ ProtonDriveConfig,
7
+ Logger,
8
+ ProtonDriveCryptoCache,
9
+ NodeOrUid,
10
+ } from './interface';
11
+ import { Telemetry } from './telemetry';
12
+ import { getUid } from './transformers';
13
+ import { DriveAPIService } from './internal/apiService';
14
+ import { SDKEvents } from './internal/sdkEvents';
15
+ import { initSharingPublicModule } from './internal/sharingPublic';
16
+
17
+ /**
18
+ * ProtonDrivePublicLinkClient is the interface for the public link client.
19
+ *
20
+ * The client provides high-level operations for managing nodes, and
21
+ * downloading/uploading files.
22
+ *
23
+ * Do not use this client direclty, use ProtonDriveClient instead.
24
+ * The main client handles public link sessions and provides access to
25
+ * public links.
26
+ *
27
+ * See `experimental.getPublicLinkInfo` and `experimental.authPublicLink`
28
+ * for more information.
29
+ */
30
+ export class ProtonDrivePublicLinkClient {
31
+ private logger: Logger;
32
+ private sdkEvents: SDKEvents;
33
+ private sharingPublic: ReturnType<typeof initSharingPublicModule>;
34
+
35
+ public experimental: {
36
+ /**
37
+ * Experimental feature to return the URL of the node.
38
+ *
39
+ * Use it when you want to open the node in the ProtonDrive web app.
40
+ *
41
+ * It has hardcoded URLs to open in production client only.
42
+ */
43
+ getNodeUrl: (nodeUid: NodeOrUid) => Promise<string>;
44
+ /**
45
+ * Experimental feature to get the docs key for a node.
46
+ *
47
+ * This is used by Docs app to encrypt and decrypt document updates.
48
+ */
49
+ getDocsKey: (nodeUid: NodeOrUid) => Promise<SessionKey>;
50
+ };
51
+
52
+ constructor({
53
+ httpClient,
54
+ cryptoCache,
55
+ openPGPCryptoModule,
56
+ srpModule,
57
+ config,
58
+ telemetry,
59
+ token,
60
+ password,
61
+ }: {
62
+ httpClient: ProtonDriveHTTPClient;
63
+ cryptoCache: ProtonDriveCryptoCache;
64
+ openPGPCryptoModule: OpenPGPCrypto;
65
+ srpModule: SRPModule;
66
+ config?: ProtonDriveConfig;
67
+ telemetry?: ProtonDriveTelemetry;
68
+ token: string;
69
+ password: string;
70
+ }) {
71
+ if (!telemetry) {
72
+ telemetry = new Telemetry();
73
+ }
74
+ this.logger = telemetry.getLogger('interface');
75
+
76
+ const fullConfig = getConfig(config);
77
+ this.sdkEvents = new SDKEvents(telemetry);
78
+
79
+ const apiService = new DriveAPIService(
80
+ telemetry,
81
+ this.sdkEvents,
82
+ httpClient,
83
+ fullConfig.baseUrl,
84
+ fullConfig.language,
85
+ );
86
+ const driveCrypto = new DriveCrypto(openPGPCryptoModule, srpModule);
87
+ this.sharingPublic = initSharingPublicModule(telemetry, apiService, cryptoCache, driveCrypto, token, password);
88
+
89
+ this.experimental = {
90
+ getNodeUrl: async (nodeUid: NodeOrUid) => {
91
+ this.logger.debug(`Getting node URL for ${getUid(nodeUid)}`);
92
+ // TODO: public node has different URL
93
+ return '';
94
+ },
95
+ getDocsKey: async (nodeUid: NodeOrUid) => {
96
+ this.logger.debug(`Getting docs keys for ${getUid(nodeUid)}`);
97
+ const keys = await this.sharingPublic.getNodeKeys(getUid(nodeUid));
98
+ if (!keys.contentKeyPacketSessionKey) {
99
+ throw new Error('Node does not have a content key packet session key');
100
+ }
101
+ return keys.contentKeyPacketSessionKey;
102
+ },
103
+ };
104
+ }
105
+
106
+ // TODO: comment
107
+ // TODO: add public node interface
108
+ async getRootNode() {
109
+ this.logger.info(`Getting root node`);
110
+ // TODO: conversion to public node
111
+ return this.sharingPublic.getRootNode();
112
+ }
113
+
114
+ // TODO: comment
115
+ // TODO: add public node interface
116
+ async *iterateChildren(parentUid: NodeOrUid) {
117
+ this.logger.info(`Iterating children of ${getUid(parentUid)}`);
118
+ // TODO: conversion to public node
119
+ yield * this.sharingPublic.iterateChildren(getUid(parentUid));
120
+ }
121
+ }