@protontech/drive-sdk 0.12.0 → 0.13.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 (124) hide show
  1. package/dist/crypto/driveCrypto.d.ts +1 -0
  2. package/dist/crypto/driveCrypto.js +1 -0
  3. package/dist/crypto/driveCrypto.js.map +1 -1
  4. package/dist/interface/featureFlags.d.ts +2 -1
  5. package/dist/interface/featureFlags.js +1 -0
  6. package/dist/interface/featureFlags.js.map +1 -1
  7. package/dist/interface/httpClient.d.ts +1 -0
  8. package/dist/interface/nodes.d.ts +7 -0
  9. package/dist/interface/nodes.js.map +1 -1
  10. package/dist/internal/apiService/apiService.d.ts +1 -0
  11. package/dist/internal/apiService/apiService.js +16 -7
  12. package/dist/internal/apiService/apiService.js.map +1 -1
  13. package/dist/internal/apiService/apiService.test.js +24 -0
  14. package/dist/internal/apiService/apiService.test.js.map +1 -1
  15. package/dist/internal/apiService/driveTypes.d.ts +110 -101
  16. package/dist/internal/nodes/apiService.d.ts +4 -0
  17. package/dist/internal/nodes/apiService.js +4 -0
  18. package/dist/internal/nodes/apiService.js.map +1 -1
  19. package/dist/internal/nodes/apiService.test.js +8 -0
  20. package/dist/internal/nodes/apiService.test.js.map +1 -1
  21. package/dist/internal/nodes/cryptoService.js +3 -0
  22. package/dist/internal/nodes/cryptoService.js.map +1 -1
  23. package/dist/internal/nodes/extendedAttributes.d.ts +5 -5
  24. package/dist/internal/nodes/extendedAttributes.js +5 -14
  25. package/dist/internal/nodes/extendedAttributes.js.map +1 -1
  26. package/dist/internal/nodes/extendedAttributes.test.js +16 -22
  27. package/dist/internal/nodes/extendedAttributes.test.js.map +1 -1
  28. package/dist/internal/nodes/interface.d.ts +5 -0
  29. package/dist/internal/nodes/nodesManagement.d.ts +3 -3
  30. package/dist/internal/nodes/nodesManagement.js +7 -5
  31. package/dist/internal/nodes/nodesManagement.js.map +1 -1
  32. package/dist/internal/photos/albumsManager.js +1 -0
  33. package/dist/internal/photos/albumsManager.js.map +1 -1
  34. package/dist/internal/photos/nodes.d.ts +1 -1
  35. package/dist/internal/photos/nodes.js +2 -2
  36. package/dist/internal/photos/nodes.js.map +1 -1
  37. package/dist/internal/photos/upload.d.ts +5 -5
  38. package/dist/internal/photos/upload.js +8 -2
  39. package/dist/internal/photos/upload.js.map +1 -1
  40. package/dist/internal/sharingPublic/nodes.d.ts +1 -0
  41. package/dist/internal/upload/apiService.d.ts +45 -1
  42. package/dist/internal/upload/apiService.js +69 -1
  43. package/dist/internal/upload/apiService.js.map +1 -1
  44. package/dist/internal/upload/blockVerifier.d.ts +4 -1
  45. package/dist/internal/upload/blockVerifier.js +5 -0
  46. package/dist/internal/upload/blockVerifier.js.map +1 -1
  47. package/dist/internal/upload/cryptoService.d.ts +2 -2
  48. package/dist/internal/upload/cryptoService.js +1 -3
  49. package/dist/internal/upload/cryptoService.js.map +1 -1
  50. package/dist/internal/upload/fileUploader.d.ts +4 -3
  51. package/dist/internal/upload/fileUploader.js +17 -7
  52. package/dist/internal/upload/fileUploader.js.map +1 -1
  53. package/dist/internal/upload/index.d.ts +3 -3
  54. package/dist/internal/upload/index.js +17 -1
  55. package/dist/internal/upload/index.js.map +1 -1
  56. package/dist/internal/upload/index.test.d.ts +1 -0
  57. package/dist/internal/upload/index.test.js +71 -0
  58. package/dist/internal/upload/index.test.js.map +1 -0
  59. package/dist/internal/upload/interface.d.ts +2 -0
  60. package/dist/internal/upload/manager.d.ts +41 -2
  61. package/dist/internal/upload/manager.js +123 -42
  62. package/dist/internal/upload/manager.js.map +1 -1
  63. package/dist/internal/upload/manager.test.js +267 -0
  64. package/dist/internal/upload/manager.test.js.map +1 -1
  65. package/dist/internal/upload/smallFileUploader.d.ts +83 -0
  66. package/dist/internal/upload/smallFileUploader.js +197 -0
  67. package/dist/internal/upload/smallFileUploader.js.map +1 -0
  68. package/dist/internal/upload/smallFileUploader.test.d.ts +1 -0
  69. package/dist/internal/upload/smallFileUploader.test.js +358 -0
  70. package/dist/internal/upload/smallFileUploader.test.js.map +1 -0
  71. package/dist/internal/upload/streamReader.d.ts +4 -0
  72. package/dist/internal/upload/streamReader.js +37 -0
  73. package/dist/internal/upload/streamReader.js.map +1 -0
  74. package/dist/internal/upload/streamReader.test.d.ts +1 -0
  75. package/dist/internal/upload/streamReader.test.js +90 -0
  76. package/dist/internal/upload/streamReader.test.js.map +1 -0
  77. package/dist/internal/upload/streamUploader.d.ts +6 -0
  78. package/dist/internal/upload/streamUploader.js +3 -3
  79. package/dist/internal/upload/streamUploader.js.map +1 -1
  80. package/dist/internal/upload/telemetry.d.ts +3 -2
  81. package/dist/internal/upload/telemetry.js +3 -0
  82. package/dist/internal/upload/telemetry.js.map +1 -1
  83. package/dist/protonDrivePhotosClient.d.ts +1 -1
  84. package/dist/protonDrivePublicLinkClient.js +3 -1
  85. package/dist/protonDrivePublicLinkClient.js.map +1 -1
  86. package/dist/transformers.d.ts +1 -1
  87. package/dist/transformers.js +1 -0
  88. package/dist/transformers.js.map +1 -1
  89. package/package.json +1 -1
  90. package/src/crypto/driveCrypto.ts +2 -0
  91. package/src/interface/featureFlags.ts +1 -0
  92. package/src/interface/httpClient.ts +1 -0
  93. package/src/interface/nodes.ts +7 -0
  94. package/src/internal/apiService/apiService.test.ts +30 -0
  95. package/src/internal/apiService/apiService.ts +23 -7
  96. package/src/internal/apiService/driveTypes.ts +110 -101
  97. package/src/internal/nodes/apiService.test.ts +9 -0
  98. package/src/internal/nodes/apiService.ts +4 -0
  99. package/src/internal/nodes/cryptoService.ts +11 -1
  100. package/src/internal/nodes/extendedAttributes.test.ts +25 -25
  101. package/src/internal/nodes/extendedAttributes.ts +10 -19
  102. package/src/internal/nodes/interface.ts +5 -0
  103. package/src/internal/nodes/nodesManagement.ts +8 -6
  104. package/src/internal/photos/albumsManager.ts +1 -0
  105. package/src/internal/photos/nodes.ts +2 -2
  106. package/src/internal/photos/upload.ts +23 -10
  107. package/src/internal/upload/apiService.ts +167 -2
  108. package/src/internal/upload/blockVerifier.ts +12 -0
  109. package/src/internal/upload/cryptoService.ts +10 -10
  110. package/src/internal/upload/fileUploader.ts +20 -7
  111. package/src/internal/upload/index.test.ts +99 -0
  112. package/src/internal/upload/index.ts +45 -4
  113. package/src/internal/upload/interface.ts +2 -0
  114. package/src/internal/upload/manager.test.ts +367 -1
  115. package/src/internal/upload/manager.ts +226 -76
  116. package/src/internal/upload/smallFileUploader.test.ts +491 -0
  117. package/src/internal/upload/smallFileUploader.ts +353 -0
  118. package/src/internal/upload/streamReader.test.ts +109 -0
  119. package/src/internal/upload/streamReader.ts +38 -0
  120. package/src/internal/upload/streamUploader.ts +1 -1
  121. package/src/internal/upload/telemetry.ts +5 -1
  122. package/src/protonDrivePhotosClient.ts +1 -1
  123. package/src/protonDrivePublicLinkClient.ts +2 -0
  124. package/src/transformers.ts +2 -0
@@ -1392,7 +1392,7 @@ export interface paths {
1392
1392
  put?: never;
1393
1393
  /**
1394
1394
  * Request block upload
1395
- * @description Request upload information for a set of blocks.
1395
+ * @description Request upload URLs for a set of blocks/thumbnails of a given draft revision.
1396
1396
  */
1397
1397
  post: operations["post_drive-blocks"];
1398
1398
  delete?: never;
@@ -1410,10 +1410,7 @@ export interface paths {
1410
1410
  };
1411
1411
  get?: never;
1412
1412
  put?: never;
1413
- /**
1414
- * Upload small file
1415
- * @description This does not support anonymous uploads (yet)
1416
- */
1413
+ /** Upload small file */
1417
1414
  post: operations["post_drive-v2-volumes-{volumeID}-files-small"];
1418
1415
  delete?: never;
1419
1416
  options?: never;
@@ -1430,10 +1427,7 @@ export interface paths {
1430
1427
  };
1431
1428
  get?: never;
1432
1429
  put?: never;
1433
- /**
1434
- * Upload small revision
1435
- * @description This does not support anonymous uploads (yet)
1436
- */
1430
+ /** Upload small revision */
1437
1431
  post: operations["post_drive-v2-volumes-{volumeID}-files-{linkID}-revisions-small"];
1438
1432
  delete?: never;
1439
1433
  options?: never;
@@ -2148,7 +2142,10 @@ export interface paths {
2148
2142
  path?: never;
2149
2143
  cookie?: never;
2150
2144
  };
2151
- /** List URLs on share */
2145
+ /**
2146
+ * List URLs on share
2147
+ * @description There can only be one or zero share URLs on a given share.
2148
+ */
2152
2149
  get: operations["get_drive-shares-{shareID}-urls"];
2153
2150
  put?: never;
2154
2151
  /** Share by URL */
@@ -2465,6 +2462,46 @@ export interface paths {
2465
2462
  patch?: never;
2466
2463
  trace?: never;
2467
2464
  };
2465
+ "/drive/unauth/v2/volumes/{volumeID}/files/small": {
2466
+ parameters: {
2467
+ query?: never;
2468
+ header?: never;
2469
+ path?: never;
2470
+ cookie?: never;
2471
+ };
2472
+ get?: never;
2473
+ put?: never;
2474
+ /**
2475
+ * Upload small file
2476
+ * @description See /drive/v2/volumes/{volumeID}/files/small for full documentation
2477
+ */
2478
+ post: operations["post_drive-unauth-v2-volumes-{volumeID}-files-small"];
2479
+ delete?: never;
2480
+ options?: never;
2481
+ head?: never;
2482
+ patch?: never;
2483
+ trace?: never;
2484
+ };
2485
+ "/drive/unauth/v2/volumes/{volumeID}/files/{linkID}/revisions/small": {
2486
+ parameters: {
2487
+ query?: never;
2488
+ header?: never;
2489
+ path?: never;
2490
+ cookie?: never;
2491
+ };
2492
+ get?: never;
2493
+ put?: never;
2494
+ /**
2495
+ * Upload small revision
2496
+ * @description See /drive/v2/volumes/{volumeID}/files/{linkID}/revisions/small for full documentation
2497
+ */
2498
+ post: operations["post_drive-unauth-v2-volumes-{volumeID}-files-{linkID}-revisions-small"];
2499
+ delete?: never;
2500
+ options?: never;
2501
+ head?: never;
2502
+ patch?: never;
2503
+ trace?: never;
2504
+ };
2468
2505
  "/drive/unauth/v2/volumes/{volumeID}/links/{linkID}/revisions/{revisionID}/verification": {
2469
2506
  parameters: {
2470
2507
  query?: never;
@@ -4540,6 +4577,15 @@ export interface components {
4540
4577
  * @enum {integer}
4541
4578
  */
4542
4579
  LinkState2: 0 | 1 | 2;
4580
+ OwnedByDto: {
4581
+ /**
4582
+ * Format: email
4583
+ * @description OwnerUser email for regular and photo volumes, null otherwise
4584
+ */
4585
+ Email?: string | null;
4586
+ /** @description OwnerOrganization name for org. volumes, null otherwise */
4587
+ Organization?: string | null;
4588
+ };
4543
4589
  LinkDto: {
4544
4590
  LinkID: components["schemas"]["Id"];
4545
4591
  Type: components["schemas"]["NodeType2"];
@@ -4557,6 +4603,7 @@ export interface components {
4557
4603
  SignatureEmail?: string | null;
4558
4604
  /** Format: email */
4559
4605
  NameSignatureEmail?: string | null;
4606
+ OwnedBy: components["schemas"]["OwnedByDto"];
4560
4607
  /** @default null */
4561
4608
  DirectPermissions: number | null;
4562
4609
  };
@@ -5236,6 +5283,8 @@ export interface components {
5236
5283
  State: components["schemas"]["HealthCheckState"];
5237
5284
  };
5238
5285
  AbuseReportDto: {
5286
+ /** @description Passphrase for reported Link's Node key, unencrypted, as a string, escaped for JSON. */
5287
+ ResourcePassphrase: string;
5239
5288
  /**
5240
5289
  * @description Reported ShareURL, complete including fragment
5241
5290
  * @example https://drive.proton.me/urls/1F9BKXYDMA#yF7d7bn01GMM
@@ -5243,8 +5292,6 @@ export interface components {
5243
5292
  ShareURL: string;
5244
5293
  /** @enum {string} */
5245
5294
  AbuseCategory: "spam" | "copyright" | "child-abuse" | "stolen-data" | "malware" | "other";
5246
- /** @description Passphrase for reported Link's Node key, unencrypted, as a string, escaped for JSON. */
5247
- ResourcePassphrase: string;
5248
5295
  /**
5249
5296
  * @description Full password, including custom part, as string, escaped for JSON
5250
5297
  * @default
@@ -5984,11 +6031,10 @@ export interface components {
5984
6031
  ShareURLs: components["schemas"]["ShareURLResponseDto"][];
5985
6032
  /**
5986
6033
  * @deprecated
5987
- * @description If the Recursive query parameter is set, also returns the related links and ancestors up to the share as a dictionary by LinkID.
6034
+ * @description Unused and deprecated. Always empty.
6035
+ * @default []
5988
6036
  */
5989
- Links: {
5990
- [key: string]: components["schemas"]["ExtendedLinkTransformer"];
5991
- };
6037
+ Links: Record<string, never>;
5992
6038
  /**
5993
6039
  * ProtonResponseCode
5994
6040
  * @example 1000
@@ -7275,23 +7321,6 @@ export interface operations {
7275
7321
  };
7276
7322
  };
7277
7323
  };
7278
- /** @description Failed dependency */
7279
- 424: {
7280
- headers: {
7281
- [name: string]: unknown;
7282
- };
7283
- content: {
7284
- "application/json": {
7285
- /**
7286
- * @description Potential codes:
7287
- * - 2032
7288
- *
7289
- * @enum {integer}
7290
- */
7291
- Code: 2032;
7292
- };
7293
- };
7294
- };
7295
7324
  };
7296
7325
  };
7297
7326
  "post_drive-photos-volumes": {
@@ -7333,23 +7362,6 @@ export interface operations {
7333
7362
  };
7334
7363
  };
7335
7364
  };
7336
- /** @description Failed dependency */
7337
- 424: {
7338
- headers: {
7339
- [name: string]: unknown;
7340
- };
7341
- content: {
7342
- "application/json": {
7343
- /**
7344
- * @description Potential codes:
7345
- * - 2032
7346
- *
7347
- * @enum {integer}
7348
- */
7349
- Code: 2032;
7350
- };
7351
- };
7352
- };
7353
7365
  };
7354
7366
  };
7355
7367
  "put_drive-photos-volumes-{volumeID}-albums-{linkID}": {
@@ -7393,26 +7405,6 @@ export interface operations {
7393
7405
  };
7394
7406
  };
7395
7407
  };
7396
- /** @description Failed dependency */
7397
- 424: {
7398
- headers: {
7399
- [name: string]: unknown;
7400
- };
7401
- content: {
7402
- "application/json": {
7403
- /**
7404
- * @description Potential codes:
7405
- * - 2501: File or folder not found
7406
- * - 2001: Invalid PGP message
7407
- * - 200501: Operation failed: Please retry
7408
- * - 2032
7409
- *
7410
- * @enum {integer}
7411
- */
7412
- Code: 2032;
7413
- };
7414
- };
7415
- };
7416
7408
  };
7417
7409
  };
7418
7410
  "delete_drive-photos-volumes-{volumeID}-albums-{linkID}": {
@@ -10832,23 +10824,6 @@ export interface operations {
10832
10824
  };
10833
10825
  };
10834
10826
  };
10835
- /** @description Failed dependency */
10836
- 424: {
10837
- headers: {
10838
- [name: string]: unknown;
10839
- };
10840
- content: {
10841
- "application/json": {
10842
- /**
10843
- * @description Potential codes:
10844
- * - 2032
10845
- *
10846
- * @enum {integer}
10847
- */
10848
- Code: 2032;
10849
- };
10850
- };
10851
- };
10852
10827
  };
10853
10828
  };
10854
10829
  "post_drive-urls-{token}-auth": {
@@ -11570,20 +11545,7 @@ export interface operations {
11570
11545
  };
11571
11546
  "get_drive-shares-{shareID}-urls": {
11572
11547
  parameters: {
11573
- query?: {
11574
- /**
11575
- * @deprecated
11576
- * @description By default, only shareURL pointing to the share are returned. With Recursive=1, list all shareURLs in the subtree reachable from the Share. 1 (true) or 0 (false).
11577
- */
11578
- Recursive?: 0 | 1;
11579
- /**
11580
- * @deprecated
11581
- * @description Fetch Thumbnail URLs
11582
- */
11583
- Thumbnails?: 0 | 1;
11584
- PageSize?: components["schemas"]["OffsetPagination"]["PageSize"] & unknown;
11585
- Page?: components["schemas"]["OffsetPagination"]["Page"] & unknown;
11586
- };
11548
+ query?: never;
11587
11549
  header?: never;
11588
11550
  path: {
11589
11551
  shareID: string;
@@ -12120,6 +12082,53 @@ export interface operations {
12120
12082
  };
12121
12083
  };
12122
12084
  };
12085
+ "post_drive-unauth-v2-volumes-{volumeID}-files-small": {
12086
+ parameters: {
12087
+ query?: never;
12088
+ header?: never;
12089
+ path: {
12090
+ volumeID: string;
12091
+ };
12092
+ cookie?: never;
12093
+ };
12094
+ requestBody?: never;
12095
+ responses: {
12096
+ /** @description Success */
12097
+ 200: {
12098
+ headers: {
12099
+ "x-pm-code": 1000;
12100
+ [name: string]: unknown;
12101
+ };
12102
+ content: {
12103
+ "application/json": components["schemas"]["SmallUploadResponseDto"];
12104
+ };
12105
+ };
12106
+ };
12107
+ };
12108
+ "post_drive-unauth-v2-volumes-{volumeID}-files-{linkID}-revisions-small": {
12109
+ parameters: {
12110
+ query?: never;
12111
+ header?: never;
12112
+ path: {
12113
+ volumeID: string;
12114
+ linkID: components["schemas"]["Id"];
12115
+ };
12116
+ cookie?: never;
12117
+ };
12118
+ requestBody?: never;
12119
+ responses: {
12120
+ /** @description Success */
12121
+ 200: {
12122
+ headers: {
12123
+ "x-pm-code": 1000;
12124
+ [name: string]: unknown;
12125
+ };
12126
+ content: {
12127
+ "application/json": components["schemas"]["SmallUploadResponseDto"];
12128
+ };
12129
+ };
12130
+ };
12131
+ };
12123
12132
  "get_drive-unauth-v2-volumes-{volumeID}-links-{linkID}-revisions-{revisionID}-verification": {
12124
12133
  parameters: {
12125
12134
  query?: never;
@@ -74,6 +74,11 @@ function generateAPINode() {
74
74
  NodeKey: 'nodeKey',
75
75
  NodePassphrase: 'nodePass',
76
76
  NodePassphraseSignature: 'nodePassSig',
77
+
78
+ OwnedBy: {
79
+ Email: 'ownerByEmail',
80
+ Organization: null,
81
+ },
77
82
  },
78
83
  SharingSummary: null,
79
84
  };
@@ -149,6 +154,10 @@ function generateNode() {
149
154
  isSharedPublicly: false,
150
155
  directRole: MemberRole.Admin,
151
156
  membership: undefined,
157
+ ownedBy: {
158
+ email: 'ownerByEmail',
159
+ organization: undefined,
160
+ },
152
161
 
153
162
  encryptedCrypto: {
154
163
  armoredKey: 'nodeKey',
@@ -780,6 +780,10 @@ export function linkToEncryptedNodeBaseMetadata(
780
780
  inviteTime: new Date(link.Membership.InviteTime * 1000),
781
781
  }
782
782
  : undefined,
783
+ ownedBy: {
784
+ email: link.Link.OwnedBy?.Email || undefined,
785
+ organization: link.Link.OwnedBy?.Organization || undefined,
786
+ },
783
787
  };
784
788
 
785
789
  const baseCryptoNodeMetadata = {
@@ -1,6 +1,13 @@
1
1
  import { c } from 'ttag';
2
2
 
3
- import { DriveCrypto, PrivateKey, PublicKey, SessionKey, VERIFICATION_STATUS } from '../../crypto';
3
+ import {
4
+ base64StringToUint8Array,
5
+ DriveCrypto,
6
+ PrivateKey,
7
+ PublicKey,
8
+ SessionKey,
9
+ VERIFICATION_STATUS,
10
+ } from '../../crypto';
4
11
  import {
5
12
  resultOk,
6
13
  resultError,
@@ -201,7 +208,9 @@ export class NodesCryptoService {
201
208
  let activeRevision: Result<DecryptedUnparsedRevision, Error> | undefined;
202
209
  let contentKeyPacketSessionKey;
203
210
  let contentKeyPacketAuthor;
211
+ let contentKeyPacket: Uint8Array<ArrayBuffer> | undefined;
204
212
  if ('file' in node.encryptedCrypto) {
213
+ contentKeyPacket = base64StringToUint8Array(node.encryptedCrypto.file.base64ContentKeyPacket);
205
214
  const [activeRevisionPromise, contentKeyPacketSessionKeyPromise] = [
206
215
  this.decryptRevision(node.uid, node.encryptedCrypto.activeRevision, key),
207
216
  this.decryptContentKeyPacket(node, node.encryptedCrypto, key, keyVerificationKeys),
@@ -284,6 +293,7 @@ export class NodesCryptoService {
284
293
  passphrase,
285
294
  key,
286
295
  passphraseSessionKey,
296
+ contentKeyPacket,
287
297
  contentKeyPacketSessionKey,
288
298
  hashKey,
289
299
  },
@@ -51,19 +51,15 @@ describe('extended attrbiutes', () => {
51
51
  });
52
52
 
53
53
  describe('should generate file attributes without additional metadata', () => {
54
- const testCases: [object, string | undefined][] = [
55
- [{}, undefined],
54
+ const testCases: [any, string | undefined][] = [
56
55
  [
57
- { modificationTime: new Date(1234567890000) },
58
- '{"Common":{"ModificationTime":"2009-02-13T23:31:30.000Z"}}',
56
+ { size: 0, blockSizes: [], digests: { sha1: 'abcdef' } },
57
+ '{"Common":{"Size":0,"BlockSizes":[],"Digests":{"SHA1":"abcdef"}}}',
58
+ ],
59
+ [
60
+ { size: 1234, blockSizes: [1200, 34], digests: { sha1: 'gedcba' } },
61
+ '{"Common":{"Size":1234,"BlockSizes":[1200,34],"Digests":{"SHA1":"gedcba"}}}',
59
62
  ],
60
- [{ size: undefined }, undefined],
61
- [{ size: 0 }, '{"Common":{"Size":0}}'],
62
- [{ size: 1234 }, '{"Common":{"Size":1234}}'],
63
- [{ blockSizes: [] }, undefined],
64
- [{ blockSizes: [4, 4, 4, 2] }, '{"Common":{"BlockSizes":[4,4,4,2]}}'],
65
- [{ digests: {} }, undefined],
66
- [{ digests: { sha1: 'abcdef' } }, '{"Common":{"Digests":{"SHA1":"abcdef"}}}'],
67
63
  [
68
64
  {
69
65
  modificationTime: new Date(1234567890000),
@@ -75,7 +71,7 @@ describe('extended attrbiutes', () => {
75
71
  ],
76
72
  ];
77
73
  testCases.forEach(([input, expectedAttributes]) => {
78
- it(`should generate ${input}`, () => {
74
+ it(`should generate ${JSON.stringify(input)}`, () => {
79
75
  const output = generateFileExtendedAttributes(input);
80
76
  expect(output).toBe(expectedAttributes);
81
77
  });
@@ -83,24 +79,28 @@ describe('extended attrbiutes', () => {
83
79
  });
84
80
 
85
81
  describe('should generate file attributes with additional metadata', () => {
86
- const testCases: [object, string | undefined][] = [
87
- [{}, '{"Media":{"Width":100,"Height":100}}'],
88
- [{ size: undefined }, '{"Media":{"Width":100,"Height":100}}'],
89
- [{ size: 123 }, '{"Common":{"Size":123},"Media":{"Width":100,"Height":100}}'],
90
- ];
91
- testCases.forEach(([input, expectedAttributes]) => {
92
- it(`should generate ${input}`, () => {
93
- const output = generateFileExtendedAttributes(input, { Media: { Width: 100, Height: 100 } });
94
- expect(output).toBe(expectedAttributes);
95
- });
82
+ const input = {
83
+ size: 1234,
84
+ blockSizes: [1200, 34],
85
+ digests: { sha1: 'abcdef' },
86
+ };
87
+
88
+ it(`should generate ${JSON.stringify(input)}`, () => {
89
+ const output = generateFileExtendedAttributes(input, { Media: { Width: 100, Height: 100 } });
90
+ expect(output).toBe(
91
+ '{"Common":{"Size":1234,"BlockSizes":[1200,34],"Digests":{"SHA1":"abcdef"}},"Media":{"Width":100,"Height":100}}',
92
+ );
96
93
  });
97
94
  });
98
95
 
99
96
  describe('should throw an error if additional metadata contains common attributes', () => {
100
97
  it('should throw an error', () => {
101
- expect(() => generateFileExtendedAttributes({ size: 123 }, { Common: { Hello: 'World' } })).toThrow(
102
- 'Common attributes are not allowed in additional metadata',
103
- );
98
+ expect(() =>
99
+ generateFileExtendedAttributes(
100
+ { size: 123, blockSizes: [], digests: { sha1: 'abcdef' } },
101
+ { Common: { Hello: 'World' } },
102
+ ),
103
+ ).toThrow('Common attributes are not allowed in additional metadata');
104
104
  });
105
105
  });
106
106
 
@@ -86,14 +86,14 @@ export function parseFolderExtendedAttributes(logger: Logger, extendedAttributes
86
86
  export function generateFileExtendedAttributes(
87
87
  common: {
88
88
  modificationTime?: Date;
89
- size?: number;
90
- blockSizes?: number[];
91
- digests?: {
92
- sha1?: string;
89
+ size: number;
90
+ blockSizes: number[];
91
+ digests: {
92
+ sha1: string;
93
93
  };
94
94
  },
95
95
  additionalMetadata?: object,
96
- ): string | undefined {
96
+ ): string {
97
97
  if (additionalMetadata && 'Common' in additionalMetadata) {
98
98
  throw new Error('Common attributes are not allowed in additional metadata');
99
99
  }
@@ -102,20 +102,11 @@ export function generateFileExtendedAttributes(
102
102
  if (common.modificationTime) {
103
103
  commonAttributes.ModificationTime = dateToIsoString(common.modificationTime);
104
104
  }
105
- if (common.size !== undefined) {
106
- commonAttributes.Size = common.size;
107
- }
108
- if (common.blockSizes?.length) {
109
- commonAttributes.BlockSizes = common.blockSizes;
110
- }
111
- if (common.digests?.sha1) {
112
- commonAttributes.Digests = {
113
- SHA1: common.digests.sha1,
114
- };
115
- }
116
- if (!Object.keys(commonAttributes).length && !additionalMetadata) {
117
- return undefined;
118
- }
105
+ commonAttributes.Size = common.size;
106
+ commonAttributes.BlockSizes = common.blockSizes;
107
+ commonAttributes.Digests = {
108
+ SHA1: common.digests.sha1,
109
+ };
119
110
  return JSON.stringify({
120
111
  ...(Object.keys(commonAttributes).length ? { Common: commonAttributes } : {}),
121
112
  ...(additionalMetadata ? { ...additionalMetadata } : {}),
@@ -47,6 +47,10 @@ interface BaseNode {
47
47
  inviteTime: Date;
48
48
  // TODO: acceptedBy: Author;
49
49
  };
50
+ ownedBy: {
51
+ email?: string;
52
+ organization?: string;
53
+ };
50
54
  }
51
55
 
52
56
  /**
@@ -156,6 +160,7 @@ export interface DecryptedNodeKeys {
156
160
  passphrase: string;
157
161
  key: PrivateKey;
158
162
  passphraseSessionKey: SessionKey;
163
+ contentKeyPacket?: Uint8Array<ArrayBuffer>;
159
164
  contentKeyPacketSessionKey?: SessionKey;
160
165
  hashKey?: Uint8Array<ArrayBuffer>;
161
166
  }
@@ -312,6 +312,7 @@ export abstract class NodesManagementBase<
312
312
  async createFolder(parentNodeUid: string, folderName: string, modificationTime?: Date): Promise<TDecryptedNode> {
313
313
  validateNodeName(folderName);
314
314
 
315
+ const parentNode = await this.nodesAccess.getNode(parentNodeUid);
315
316
  const parentKeys = await this.nodesAccess.getNodeKeys(parentNodeUid);
316
317
  if (!parentKeys.hashKey) {
317
318
  throw new ValidationError(c('Error').t`Creating folders in non-folders is not allowed`);
@@ -338,14 +339,14 @@ export abstract class NodesManagementBase<
338
339
  });
339
340
 
340
341
  await this.nodesAccess.notifyChildCreated(parentNodeUid);
341
- const node = this.generateNodeFolder(nodeUid, parentNodeUid, folderName, encryptedCrypto);
342
+ const node = this.generateNodeFolder(parentNode, nodeUid, folderName, encryptedCrypto);
342
343
  await this.cryptoCache.setNodeKeys(nodeUid, keys);
343
344
  return node;
344
345
  }
345
346
 
346
347
  protected abstract generateNodeFolder(
348
+ parentNode: TDecryptedNode,
347
349
  nodeUid: string,
348
- parentUid: string,
349
350
  name: string,
350
351
  encryptedCrypto: {
351
352
  hash: string;
@@ -355,8 +356,8 @@ export abstract class NodesManagementBase<
355
356
  ): TDecryptedNode;
356
357
 
357
358
  protected generateNodeFolderBase(
359
+ parentNode: TDecryptedNode,
358
360
  nodeUid: string,
359
- parentNodeUid: string,
360
361
  name: string,
361
362
  encryptedCrypto: {
362
363
  hash: string;
@@ -371,7 +372,7 @@ export abstract class NodesManagementBase<
371
372
 
372
373
  // Basic node metadata
373
374
  uid: nodeUid,
374
- parentUid: parentNodeUid,
375
+ parentUid: parentNode.uid,
375
376
  type: NodeType.Folder,
376
377
  mediaType: FOLDER_MEDIA_TYPE,
377
378
  creationTime: new Date(),
@@ -381,6 +382,7 @@ export abstract class NodesManagementBase<
381
382
  isShared: false,
382
383
  isSharedPublicly: false,
383
384
  directRole: MemberRole.Inherited,
385
+ ownedBy: parentNode.ownedBy,
384
386
 
385
387
  // Decrypted metadata
386
388
  isStale: false,
@@ -432,8 +434,8 @@ export abstract class NodesManagementBase<
432
434
 
433
435
  export class NodesManagement extends NodesManagementBase {
434
436
  protected generateNodeFolder(
437
+ parentNode: DecryptedNode,
435
438
  nodeUid: string,
436
- parentNodeUid: string,
437
439
  name: string,
438
440
  encryptedCrypto: {
439
441
  hash: string;
@@ -441,6 +443,6 @@ export class NodesManagement extends NodesManagementBase {
441
443
  signatureEmail: string | null;
442
444
  },
443
445
  ): DecryptedNode {
444
- return this.generateNodeFolderBase(nodeUid, parentNodeUid, name, encryptedCrypto);
446
+ return this.generateNodeFolderBase(parentNode, nodeUid, name, encryptedCrypto);
445
447
  }
446
448
  }
@@ -95,6 +95,7 @@ export class AlbumsManager {
95
95
  isShared: false,
96
96
  isSharedPublicly: false,
97
97
  directRole: MemberRole.Inherited,
98
+ ownedBy: rootNode.ownedBy,
98
99
 
99
100
  // Decrypted metadata
100
101
  isStale: false,
@@ -252,8 +252,8 @@ export class PhotosNodesManagement extends NodesManagementBase<
252
252
  PhotosNodesCryptoService
253
253
  > {
254
254
  protected generateNodeFolder(
255
+ parentNode: DecryptedPhotoNode,
255
256
  nodeUid: string,
256
- parentNodeUid: string,
257
257
  name: string,
258
258
  encryptedCrypto: {
259
259
  hash: string;
@@ -261,6 +261,6 @@ export class PhotosNodesManagement extends NodesManagementBase<
261
261
  signatureEmail: string | null;
262
262
  },
263
263
  ): DecryptedPhotoNode {
264
- return this.generateNodeFolderBase(nodeUid, parentNodeUid, name, encryptedCrypto);
264
+ return this.generateNodeFolderBase(parentNode, nodeUid, name, encryptedCrypto);
265
265
  }
266
266
  }