@metamask-previews/profile-sync-controller 3.0.0-preview-e1a582c9 → 3.0.0-preview-e644b4f

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 (55) hide show
  1. package/dist/controllers/user-storage/__fixtures__/mockResponses.cjs +7 -6
  2. package/dist/controllers/user-storage/__fixtures__/mockResponses.cjs.map +1 -1
  3. package/dist/controllers/user-storage/__fixtures__/mockResponses.d.cts.map +1 -1
  4. package/dist/controllers/user-storage/__fixtures__/mockResponses.d.mts.map +1 -1
  5. package/dist/controllers/user-storage/__fixtures__/mockResponses.mjs +7 -6
  6. package/dist/controllers/user-storage/__fixtures__/mockResponses.mjs.map +1 -1
  7. package/dist/controllers/user-storage/services.cjs +47 -1
  8. package/dist/controllers/user-storage/services.cjs.map +1 -1
  9. package/dist/controllers/user-storage/services.d.cts +8 -0
  10. package/dist/controllers/user-storage/services.d.cts.map +1 -1
  11. package/dist/controllers/user-storage/services.d.mts +8 -0
  12. package/dist/controllers/user-storage/services.d.mts.map +1 -1
  13. package/dist/controllers/user-storage/services.mjs +45 -0
  14. package/dist/controllers/user-storage/services.mjs.map +1 -1
  15. package/dist/sdk/__fixtures__/mock-userstorage.cjs +29 -6
  16. package/dist/sdk/__fixtures__/mock-userstorage.cjs.map +1 -1
  17. package/dist/sdk/__fixtures__/mock-userstorage.d.cts +4 -6
  18. package/dist/sdk/__fixtures__/mock-userstorage.d.cts.map +1 -1
  19. package/dist/sdk/__fixtures__/mock-userstorage.d.mts +4 -6
  20. package/dist/sdk/__fixtures__/mock-userstorage.d.mts.map +1 -1
  21. package/dist/sdk/__fixtures__/mock-userstorage.mjs +6 -6
  22. package/dist/sdk/__fixtures__/mock-userstorage.mjs.map +1 -1
  23. package/dist/sdk/user-storage.cjs +62 -9
  24. package/dist/sdk/user-storage.cjs.map +1 -1
  25. package/dist/sdk/user-storage.d.cts +5 -1
  26. package/dist/sdk/user-storage.d.cts.map +1 -1
  27. package/dist/sdk/user-storage.d.mts +5 -1
  28. package/dist/sdk/user-storage.d.mts.map +1 -1
  29. package/dist/sdk/user-storage.mjs +62 -9
  30. package/dist/sdk/user-storage.mjs.map +1 -1
  31. package/dist/shared/encryption/cache.cjs +12 -14
  32. package/dist/shared/encryption/cache.cjs.map +1 -1
  33. package/dist/shared/encryption/cache.d.cts +4 -3
  34. package/dist/shared/encryption/cache.d.cts.map +1 -1
  35. package/dist/shared/encryption/cache.d.mts +4 -3
  36. package/dist/shared/encryption/cache.d.mts.map +1 -1
  37. package/dist/shared/encryption/cache.mjs +11 -13
  38. package/dist/shared/encryption/cache.mjs.map +1 -1
  39. package/dist/shared/encryption/constants.cjs +18 -0
  40. package/dist/shared/encryption/constants.cjs.map +1 -0
  41. package/dist/shared/encryption/constants.d.cts +8 -0
  42. package/dist/shared/encryption/constants.d.cts.map +1 -0
  43. package/dist/shared/encryption/constants.d.mts +8 -0
  44. package/dist/shared/encryption/constants.d.mts.map +1 -0
  45. package/dist/shared/encryption/constants.mjs +15 -0
  46. package/dist/shared/encryption/constants.mjs.map +1 -0
  47. package/dist/shared/encryption/encryption.cjs +49 -25
  48. package/dist/shared/encryption/encryption.cjs.map +1 -1
  49. package/dist/shared/encryption/encryption.d.cts +2 -0
  50. package/dist/shared/encryption/encryption.d.cts.map +1 -1
  51. package/dist/shared/encryption/encryption.d.mts +2 -0
  52. package/dist/shared/encryption/encryption.d.mts.map +1 -1
  53. package/dist/shared/encryption/encryption.mjs +38 -14
  54. package/dist/shared/encryption/encryption.mjs.map +1 -1
  55. package/package.json +1 -1
@@ -29,13 +29,14 @@ exports.createMockGetStorageResponse = createMockGetStorageResponse;
29
29
  * @returns a realistic GET ALL Response Body
30
30
  */
31
31
  async function createMockAllFeatureEntriesResponse(dataArr = [mockStorage_1.MOCK_STORAGE_DATA]) {
32
- return await Promise.all(dataArr.map(async function (d) {
33
- const encryptedData = await (0, mockStorage_1.MOCK_ENCRYPTED_STORAGE_DATA)(d);
34
- return {
32
+ const decryptedData = [];
33
+ for (const data of dataArr) {
34
+ decryptedData.push({
35
35
  HashedKey: 'HASHED_KEY',
36
- Data: encryptedData,
37
- };
38
- }));
36
+ Data: await (0, mockStorage_1.MOCK_ENCRYPTED_STORAGE_DATA)(data),
37
+ });
38
+ }
39
+ return decryptedData;
39
40
  }
40
41
  exports.createMockAllFeatureEntriesResponse = createMockAllFeatureEntriesResponse;
41
42
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../../shared/storage-schema';\nimport {\n createEntryPath,\n USER_STORAGE_FEATURE_NAMES,\n} from '../../../shared/storage-schema';\nimport type {\n GetUserStorageAllFeatureEntriesResponse,\n GetUserStorageResponse,\n} from '../services';\nimport { USER_STORAGE_ENDPOINT } from '../services';\nimport {\n MOCK_ENCRYPTED_STORAGE_DATA,\n MOCK_STORAGE_DATA,\n MOCK_STORAGE_KEY,\n} from './mockStorage';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';\n response: unknown;\n};\n\nexport const getMockUserStorageEndpoint = (\n path: UserStoragePathWithFeatureAndKey | UserStoragePathWithFeatureOnly,\n) => {\n if (path.split('.').length === 1) {\n return `${USER_STORAGE_ENDPOINT}/${path}`;\n }\n\n return `${USER_STORAGE_ENDPOINT}/${createEntryPath(\n path as UserStoragePathWithFeatureAndKey,\n MOCK_STORAGE_KEY,\n )}`;\n};\n\n/**\n * Creates a mock GET user-storage response\n * @param data - data to encrypt\n * @returns a realistic GET Response Body\n */\nexport async function createMockGetStorageResponse(\n data?: string,\n): Promise<GetUserStorageResponse> {\n return {\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n };\n}\n\n/**\n * Creates a mock GET ALL user-storage response\n * @param dataArr - array of data to encrypt\n * @returns a realistic GET ALL Response Body\n */\nexport async function createMockAllFeatureEntriesResponse(\n dataArr: string[] = [MOCK_STORAGE_DATA],\n): Promise<GetUserStorageAllFeatureEntriesResponse> {\n return await Promise.all(\n dataArr.map(async function (d) {\n const encryptedData = await MOCK_ENCRYPTED_STORAGE_DATA(d);\n return {\n HashedKey: 'HASHED_KEY',\n Data: encryptedData,\n };\n }),\n );\n}\n\n/**\n * Creates a mock user-storage api GET request\n * @param path - path of the GET Url\n * @returns mock GET API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageGetResponse(\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockGetStorageResponse(),\n } satisfies MockResponse;\n}\n\n/**\n * Creates a mock user-storage api GET ALL request\n * @param path - path of the GET url\n * @param dataArr - data to encrypt\n * @returns mock GET ALL API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageAllFeatureEntriesResponse(\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n dataArr?: string[],\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockAllFeatureEntriesResponse(dataArr),\n } satisfies MockResponse;\n}\n\nexport const getMockUserStoragePutResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchPutResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchDeleteResponse = (\n path: UserStoragePathWithFeatureOnly = 'notifications',\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageAllFeatureEntriesResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n"]}
1
+ {"version":3,"file":"mockResponses.cjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../../shared/storage-schema';\nimport {\n createEntryPath,\n USER_STORAGE_FEATURE_NAMES,\n} from '../../../shared/storage-schema';\nimport type {\n GetUserStorageAllFeatureEntriesResponse,\n GetUserStorageResponse,\n} from '../services';\nimport { USER_STORAGE_ENDPOINT } from '../services';\nimport {\n MOCK_ENCRYPTED_STORAGE_DATA,\n MOCK_STORAGE_DATA,\n MOCK_STORAGE_KEY,\n} from './mockStorage';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';\n response: unknown;\n};\n\nexport const getMockUserStorageEndpoint = (\n path: UserStoragePathWithFeatureAndKey | UserStoragePathWithFeatureOnly,\n) => {\n if (path.split('.').length === 1) {\n return `${USER_STORAGE_ENDPOINT}/${path}`;\n }\n\n return `${USER_STORAGE_ENDPOINT}/${createEntryPath(\n path as UserStoragePathWithFeatureAndKey,\n MOCK_STORAGE_KEY,\n )}`;\n};\n\n/**\n * Creates a mock GET user-storage response\n * @param data - data to encrypt\n * @returns a realistic GET Response Body\n */\nexport async function createMockGetStorageResponse(\n data?: string,\n): Promise<GetUserStorageResponse> {\n return {\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n };\n}\n\n/**\n * Creates a mock GET ALL user-storage response\n * @param dataArr - array of data to encrypt\n * @returns a realistic GET ALL Response Body\n */\nexport async function createMockAllFeatureEntriesResponse(\n dataArr: string[] = [MOCK_STORAGE_DATA],\n): Promise<GetUserStorageAllFeatureEntriesResponse> {\n const decryptedData = [];\n\n for (const data of dataArr) {\n decryptedData.push({\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n });\n }\n\n return decryptedData;\n}\n\n/**\n * Creates a mock user-storage api GET request\n * @param path - path of the GET Url\n * @returns mock GET API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageGetResponse(\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockGetStorageResponse(),\n } satisfies MockResponse;\n}\n\n/**\n * Creates a mock user-storage api GET ALL request\n * @param path - path of the GET url\n * @param dataArr - data to encrypt\n * @returns mock GET ALL API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageAllFeatureEntriesResponse(\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n dataArr?: string[],\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockAllFeatureEntriesResponse(dataArr),\n } satisfies MockResponse;\n}\n\nexport const getMockUserStoragePutResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchPutResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchDeleteResponse = (\n path: UserStoragePathWithFeatureOnly = 'notifications',\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageAllFeatureEntriesResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,2CAAuC;AAKxC,OAAO,KAAK,EACV,uCAAuC,EACvC,sBAAsB,EACvB,wBAAoB;AAcrB,eAAO,MAAM,0BAA0B,SAC/B,gCAAgC,GAAG,8BAA8B,WAUxE,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,CAKjC;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,OAAO,GAAE,MAAM,EAAwB,GACtC,OAAO,CAAC,uCAAuC,CAAC,CAUlD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,GAAE,gCAAsG;;;;GAO7G;AAED;;;;;GAKG;AACH,wBAAsB,2CAA2C,CAC/D,IAAI,GAAE,8BAAyE,EAC/E,OAAO,CAAC,EAAE,MAAM,EAAE;;;;GAOnB;AAED,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,kCAAkC,UACvC,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,qCAAqC,UAC1C,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,8CAA8C,UACnD,8BAA8B;;;;CAOrC,CAAC"}
1
+ {"version":3,"file":"mockResponses.d.cts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,2CAAuC;AAKxC,OAAO,KAAK,EACV,uCAAuC,EACvC,sBAAsB,EACvB,wBAAoB;AAcrB,eAAO,MAAM,0BAA0B,SAC/B,gCAAgC,GAAG,8BAA8B,WAUxE,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,CAKjC;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,OAAO,GAAE,MAAM,EAAwB,GACtC,OAAO,CAAC,uCAAuC,CAAC,CAWlD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,GAAE,gCAAsG;;;;GAO7G;AAED;;;;;GAKG;AACH,wBAAsB,2CAA2C,CAC/D,IAAI,GAAE,8BAAyE,EAC/E,OAAO,CAAC,EAAE,MAAM,EAAE;;;;GAOnB;AAED,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,kCAAkC,UACvC,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,qCAAqC,UAC1C,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,8CAA8C,UACnD,8BAA8B;;;;CAOrC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,2CAAuC;AAKxC,OAAO,KAAK,EACV,uCAAuC,EACvC,sBAAsB,EACvB,wBAAoB;AAcrB,eAAO,MAAM,0BAA0B,SAC/B,gCAAgC,GAAG,8BAA8B,WAUxE,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,CAKjC;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,OAAO,GAAE,MAAM,EAAwB,GACtC,OAAO,CAAC,uCAAuC,CAAC,CAUlD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,GAAE,gCAAsG;;;;GAO7G;AAED;;;;;GAKG;AACH,wBAAsB,2CAA2C,CAC/D,IAAI,GAAE,8BAAyE,EAC/E,OAAO,CAAC,EAAE,MAAM,EAAE;;;;GAOnB;AAED,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,kCAAkC,UACvC,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,qCAAqC,UAC1C,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,8CAA8C,UACnD,8BAA8B;;;;CAOrC,CAAC"}
1
+ {"version":3,"file":"mockResponses.d.mts","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,2CAAuC;AAKxC,OAAO,KAAK,EACV,uCAAuC,EACvC,sBAAsB,EACvB,wBAAoB;AAcrB,eAAO,MAAM,0BAA0B,SAC/B,gCAAgC,GAAG,8BAA8B,WAUxE,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,sBAAsB,CAAC,CAKjC;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,OAAO,GAAE,MAAM,EAAwB,GACtC,OAAO,CAAC,uCAAuC,CAAC,CAWlD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,GAAE,gCAAsG;;;;GAO7G;AAED;;;;;GAKG;AACH,wBAAsB,2CAA2C,CAC/D,IAAI,GAAE,8BAAyE,EAC/E,OAAO,CAAC,EAAE,MAAM,EAAE;;;;GAOnB;AAED,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,kCAAkC,UACvC,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,qCAAqC,UAC1C,8BAA8B;;;;CAOrC,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAClC,gCAAgC;;;;CAOvC,CAAC;AAEF,eAAO,MAAM,8CAA8C,UACnD,8BAA8B;;;;CAOrC,CAAC"}
@@ -24,13 +24,14 @@ export async function createMockGetStorageResponse(data) {
24
24
  * @returns a realistic GET ALL Response Body
25
25
  */
26
26
  export async function createMockAllFeatureEntriesResponse(dataArr = [MOCK_STORAGE_DATA]) {
27
- return await Promise.all(dataArr.map(async function (d) {
28
- const encryptedData = await MOCK_ENCRYPTED_STORAGE_DATA(d);
29
- return {
27
+ const decryptedData = [];
28
+ for (const data of dataArr) {
29
+ decryptedData.push({
30
30
  HashedKey: 'HASHED_KEY',
31
- Data: encryptedData,
32
- };
33
- }));
31
+ Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),
32
+ });
33
+ }
34
+ return decryptedData;
34
35
  }
35
36
  /**
36
37
  * Creates a mock user-storage api GET request
@@ -1 +1 @@
1
- {"version":3,"file":"mockResponses.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../../shared/storage-schema';\nimport {\n createEntryPath,\n USER_STORAGE_FEATURE_NAMES,\n} from '../../../shared/storage-schema';\nimport type {\n GetUserStorageAllFeatureEntriesResponse,\n GetUserStorageResponse,\n} from '../services';\nimport { USER_STORAGE_ENDPOINT } from '../services';\nimport {\n MOCK_ENCRYPTED_STORAGE_DATA,\n MOCK_STORAGE_DATA,\n MOCK_STORAGE_KEY,\n} from './mockStorage';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';\n response: unknown;\n};\n\nexport const getMockUserStorageEndpoint = (\n path: UserStoragePathWithFeatureAndKey | UserStoragePathWithFeatureOnly,\n) => {\n if (path.split('.').length === 1) {\n return `${USER_STORAGE_ENDPOINT}/${path}`;\n }\n\n return `${USER_STORAGE_ENDPOINT}/${createEntryPath(\n path as UserStoragePathWithFeatureAndKey,\n MOCK_STORAGE_KEY,\n )}`;\n};\n\n/**\n * Creates a mock GET user-storage response\n * @param data - data to encrypt\n * @returns a realistic GET Response Body\n */\nexport async function createMockGetStorageResponse(\n data?: string,\n): Promise<GetUserStorageResponse> {\n return {\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n };\n}\n\n/**\n * Creates a mock GET ALL user-storage response\n * @param dataArr - array of data to encrypt\n * @returns a realistic GET ALL Response Body\n */\nexport async function createMockAllFeatureEntriesResponse(\n dataArr: string[] = [MOCK_STORAGE_DATA],\n): Promise<GetUserStorageAllFeatureEntriesResponse> {\n return await Promise.all(\n dataArr.map(async function (d) {\n const encryptedData = await MOCK_ENCRYPTED_STORAGE_DATA(d);\n return {\n HashedKey: 'HASHED_KEY',\n Data: encryptedData,\n };\n }),\n );\n}\n\n/**\n * Creates a mock user-storage api GET request\n * @param path - path of the GET Url\n * @returns mock GET API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageGetResponse(\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockGetStorageResponse(),\n } satisfies MockResponse;\n}\n\n/**\n * Creates a mock user-storage api GET ALL request\n * @param path - path of the GET url\n * @param dataArr - data to encrypt\n * @returns mock GET ALL API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageAllFeatureEntriesResponse(\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n dataArr?: string[],\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockAllFeatureEntriesResponse(dataArr),\n } satisfies MockResponse;\n}\n\nexport const getMockUserStoragePutResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchPutResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchDeleteResponse = (\n path: UserStoragePathWithFeatureOnly = 'notifications',\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageAllFeatureEntriesResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n"]}
1
+ {"version":3,"file":"mockResponses.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/__fixtures__/mockResponses.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../../shared/storage-schema';\nimport {\n createEntryPath,\n USER_STORAGE_FEATURE_NAMES,\n} from '../../../shared/storage-schema';\nimport type {\n GetUserStorageAllFeatureEntriesResponse,\n GetUserStorageResponse,\n} from '../services';\nimport { USER_STORAGE_ENDPOINT } from '../services';\nimport {\n MOCK_ENCRYPTED_STORAGE_DATA,\n MOCK_STORAGE_DATA,\n MOCK_STORAGE_KEY,\n} from './mockStorage';\n\ntype MockResponse = {\n url: string;\n requestMethod: 'GET' | 'POST' | 'PUT' | 'DELETE';\n response: unknown;\n};\n\nexport const getMockUserStorageEndpoint = (\n path: UserStoragePathWithFeatureAndKey | UserStoragePathWithFeatureOnly,\n) => {\n if (path.split('.').length === 1) {\n return `${USER_STORAGE_ENDPOINT}/${path}`;\n }\n\n return `${USER_STORAGE_ENDPOINT}/${createEntryPath(\n path as UserStoragePathWithFeatureAndKey,\n MOCK_STORAGE_KEY,\n )}`;\n};\n\n/**\n * Creates a mock GET user-storage response\n * @param data - data to encrypt\n * @returns a realistic GET Response Body\n */\nexport async function createMockGetStorageResponse(\n data?: string,\n): Promise<GetUserStorageResponse> {\n return {\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n };\n}\n\n/**\n * Creates a mock GET ALL user-storage response\n * @param dataArr - array of data to encrypt\n * @returns a realistic GET ALL Response Body\n */\nexport async function createMockAllFeatureEntriesResponse(\n dataArr: string[] = [MOCK_STORAGE_DATA],\n): Promise<GetUserStorageAllFeatureEntriesResponse> {\n const decryptedData = [];\n\n for (const data of dataArr) {\n decryptedData.push({\n HashedKey: 'HASHED_KEY',\n Data: await MOCK_ENCRYPTED_STORAGE_DATA(data),\n });\n }\n\n return decryptedData;\n}\n\n/**\n * Creates a mock user-storage api GET request\n * @param path - path of the GET Url\n * @returns mock GET API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageGetResponse(\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockGetStorageResponse(),\n } satisfies MockResponse;\n}\n\n/**\n * Creates a mock user-storage api GET ALL request\n * @param path - path of the GET url\n * @param dataArr - data to encrypt\n * @returns mock GET ALL API request. Can be used by e2e or unit mock servers\n */\nexport async function getMockUserStorageAllFeatureEntriesResponse(\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n dataArr?: string[],\n) {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'GET',\n response: await createMockAllFeatureEntriesResponse(dataArr),\n } satisfies MockResponse;\n}\n\nexport const getMockUserStoragePutResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchPutResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const getMockUserStorageBatchDeleteResponse = (\n path: UserStoragePathWithFeatureOnly = 'notifications',\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'PUT',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageResponse = (\n path: UserStoragePathWithFeatureAndKey = `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n\nexport const deleteMockUserStorageAllFeatureEntriesResponse = (\n path: UserStoragePathWithFeatureOnly = USER_STORAGE_FEATURE_NAMES.notifications,\n) => {\n return {\n url: getMockUserStorageEndpoint(path),\n requestMethod: 'DELETE',\n response: null,\n } satisfies MockResponse;\n};\n"]}
@@ -26,9 +26,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.deleteUserStorageAllFeatureEntries = exports.batchDeleteUserStorage = exports.deleteUserStorage = exports.batchUpsertUserStorage = exports.upsertUserStorage = exports.getUserStorageAllFeatureEntries = exports.getUserStorage = exports.USER_STORAGE_ENDPOINT = exports.USER_STORAGE_API = void 0;
29
+ exports.deleteUserStorageAllFeatureEntries = exports.batchDeleteUserStorage = exports.deleteUserStorage = exports.batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries = exports.batchUpsertUserStorage = exports.upsertUserStorage = exports.getUserStorageAllFeatureEntries = exports.getUserStorage = exports.USER_STORAGE_ENDPOINT = exports.USER_STORAGE_API = void 0;
30
30
  const loglevel_1 = __importDefault(require("loglevel"));
31
31
  const encryption_1 = __importStar(require("../../shared/encryption/index.cjs"));
32
+ const constants_1 = require("../../shared/encryption/constants.cjs");
32
33
  const env_1 = require("../../shared/env.cjs");
33
34
  const storage_schema_1 = require("../../shared/storage-schema.cjs");
34
35
  const ENV_URLS = (0, env_1.getEnvUrls)(env_1.Env.PRD);
@@ -65,6 +66,11 @@ async function getUserStorage(opts) {
65
66
  return null;
66
67
  }
67
68
  const decryptedData = await encryption_1.default.decryptString(encryptedData, opts.storageKey, nativeScryptCrypto);
69
+ // Re-encrypt and re-upload the entry if the salt is random
70
+ const salt = encryption_1.default.getSalt(encryptedData);
71
+ if (salt.toString() !== constants_1.SHARED_SALT.toString()) {
72
+ await upsertUserStorage(decryptedData, opts);
73
+ }
68
74
  return decryptedData;
69
75
  }
70
76
  catch (e) {
@@ -101,6 +107,7 @@ async function getUserStorageAllFeatureEntries(opts) {
101
107
  return null;
102
108
  }
103
109
  const decryptedData = [];
110
+ const reEncryptedEntries = [];
104
111
  for (const entry of userStorage) {
105
112
  /* istanbul ignore if - unreachable if statement, but kept as edge case */
106
113
  if (!entry.Data) {
@@ -109,11 +116,23 @@ async function getUserStorageAllFeatureEntries(opts) {
109
116
  try {
110
117
  const data = await encryption_1.default.decryptString(entry.Data, opts.storageKey, nativeScryptCrypto);
111
118
  decryptedData.push(data);
119
+ // Re-encrypt the entry if the salt is different from the shared one
120
+ const salt = encryption_1.default.getSalt(entry.Data);
121
+ if (salt.toString() !== constants_1.SHARED_SALT.toString()) {
122
+ reEncryptedEntries.push([
123
+ entry.HashedKey,
124
+ await encryption_1.default.encryptString(data, opts.storageKey, nativeScryptCrypto),
125
+ ]);
126
+ }
112
127
  }
113
128
  catch {
114
129
  // do nothing
115
130
  }
116
131
  }
132
+ // Re-upload the re-encrypted entries
133
+ if (reEncryptedEntries.length) {
134
+ await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(reEncryptedEntries, opts);
135
+ }
117
136
  return decryptedData;
118
137
  }
119
138
  catch (e) {
@@ -180,6 +199,33 @@ async function batchUpsertUserStorage(data, opts) {
180
199
  }
181
200
  }
182
201
  exports.batchUpsertUserStorage = batchUpsertUserStorage;
202
+ /**
203
+ * User Storage Service - Set multiple storage entries for one specific feature.
204
+ * You cannot use this method to set multiple features at once.
205
+ *
206
+ * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs
207
+ * @param opts - storage options
208
+ */
209
+ async function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(encryptedData, opts) {
210
+ if (!encryptedData.length) {
211
+ return;
212
+ }
213
+ const { bearerToken, path } = opts;
214
+ const url = new URL(`${exports.USER_STORAGE_ENDPOINT}/${path}`);
215
+ const formattedData = Object.fromEntries(encryptedData);
216
+ const res = await fetch(url.toString(), {
217
+ method: 'PUT',
218
+ headers: {
219
+ 'Content-Type': 'application/json',
220
+ Authorization: `Bearer ${bearerToken}`,
221
+ },
222
+ body: JSON.stringify({ data: formattedData }),
223
+ });
224
+ if (!res.ok) {
225
+ throw new Error('user-storage - unable to batch upsert data');
226
+ }
227
+ }
228
+ exports.batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries = batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries;
183
229
  /**
184
230
  * User Storage Service - Delete Storage Entry.
185
231
  *
@@ -1 +1 @@
1
- {"version":3,"file":"services.cjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"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","sourcesContent":["import log from 'loglevel';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { Env, getEnvUrls } from '../../shared/env';\nimport type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../shared/storage-schema';\nimport { createEntryPath } from '../../shared/storage-schema';\nimport type { NativeScrypt } from '../../shared/types/encryption';\n\nconst ENV_URLS = getEnvUrls(Env.PRD);\n\nexport const USER_STORAGE_API: string = ENV_URLS.userStorageApiUrl;\nexport const USER_STORAGE_ENDPOINT = `${USER_STORAGE_API}/api/v1/userstorage`;\n\n/**\n * This is the Server Response shape for a feature entry.\n */\nexport type GetUserStorageResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n};\n\nexport type GetUserStorageAllFeatureEntriesResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n}[];\n\nexport type UserStorageBaseOptions = {\n bearerToken: string;\n storageKey: string;\n nativeScryptCrypto?: NativeScrypt;\n};\n\nexport type UserStorageOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureAndKey;\n};\n\nexport type UserStorageAllFeatureEntriesOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureOnly;\n};\n\nexport type UserStorageBatchUpsertOptions = UserStorageAllFeatureEntriesOptions;\n\n/**\n * User Storage Service - Get Storage Entry.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorage(\n opts: UserStorageOptions,\n): Promise<string | null> {\n try {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates entry does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageResponse | null =\n await userStorageResponse.json();\n const encryptedData = userStorage?.Data ?? null;\n\n /* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */\n if (!encryptedData) {\n return null;\n }\n\n const decryptedData = await encryption.decryptString(\n encryptedData,\n opts.storageKey,\n nativeScryptCrypto,\n );\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Get all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<string[] | null> {\n try {\n const { bearerToken, path, nativeScryptCrypto } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates feature does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageAllFeatureEntriesResponse | null =\n await userStorageResponse.json();\n\n if (!Array.isArray(userStorage)) {\n return null;\n }\n\n const decryptedData: string[] = [];\n\n for (const entry of userStorage) {\n /* istanbul ignore if - unreachable if statement, but kept as edge case */\n if (!entry.Data) {\n continue;\n }\n\n try {\n const data = await encryption.decryptString(\n entry.Data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n decryptedData.push(data);\n } catch {\n // do nothing\n }\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Set Storage Entry.\n *\n * @param data - data to store\n * @param opts - storage options\n */\nexport async function upsertUserStorage(\n data: string,\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData = await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param data - data to store, in the form of an array of [entryKey, entryValue] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorage(\n data: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData: string[][] = [];\n\n for (const d of data) {\n encryptedData.push([\n createSHA256Hash(d[0] + storageKey),\n await encryption.encryptString(d[1], opts.storageKey, nativeScryptCrypto),\n ]);\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Delete Storage Entry.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorage(\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey } = opts;\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature/entry not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n\n/**\n * User Storage Service - Delete multiple storage entries for one specific feature.\n * You cannot use this method to delete multiple features at once.\n *\n * @param data - data to delete, in the form of an array entryKey[]\n * @param opts - storage options\n */\nexport async function batchDeleteUserStorage(\n data: string[],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey } = opts;\n\n const encryptedData: string[] = [];\n\n for (const d of data) {\n encryptedData.push(createSHA256Hash(d + storageKey));\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n // eslint-disable-next-line @typescript-eslint/naming-convention\n body: JSON.stringify({ batch_delete: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch delete data');\n }\n}\n\n/**\n * User Storage Service - Delete all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<void> {\n const { bearerToken, path } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n"]}
1
+ {"version":3,"file":"services.cjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"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","sourcesContent":["import log from 'loglevel';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { SHARED_SALT } from '../../shared/encryption/constants';\nimport { Env, getEnvUrls } from '../../shared/env';\nimport type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../shared/storage-schema';\nimport { createEntryPath } from '../../shared/storage-schema';\nimport type { NativeScrypt } from '../../shared/types/encryption';\n\nconst ENV_URLS = getEnvUrls(Env.PRD);\n\nexport const USER_STORAGE_API: string = ENV_URLS.userStorageApiUrl;\nexport const USER_STORAGE_ENDPOINT = `${USER_STORAGE_API}/api/v1/userstorage`;\n\n/**\n * This is the Server Response shape for a feature entry.\n */\nexport type GetUserStorageResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n};\n\nexport type GetUserStorageAllFeatureEntriesResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n}[];\n\nexport type UserStorageBaseOptions = {\n bearerToken: string;\n storageKey: string;\n nativeScryptCrypto?: NativeScrypt;\n};\n\nexport type UserStorageOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureAndKey;\n};\n\nexport type UserStorageAllFeatureEntriesOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureOnly;\n};\n\nexport type UserStorageBatchUpsertOptions = UserStorageAllFeatureEntriesOptions;\n\n/**\n * User Storage Service - Get Storage Entry.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorage(\n opts: UserStorageOptions,\n): Promise<string | null> {\n try {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates entry does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageResponse | null =\n await userStorageResponse.json();\n const encryptedData = userStorage?.Data ?? null;\n\n /* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */\n if (!encryptedData) {\n return null;\n }\n\n const decryptedData = await encryption.decryptString(\n encryptedData,\n opts.storageKey,\n nativeScryptCrypto,\n );\n\n // Re-encrypt and re-upload the entry if the salt is random\n const salt = encryption.getSalt(encryptedData);\n if (salt.toString() !== SHARED_SALT.toString()) {\n await upsertUserStorage(decryptedData, opts);\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Get all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<string[] | null> {\n try {\n const { bearerToken, path, nativeScryptCrypto } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates feature does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageAllFeatureEntriesResponse | null =\n await userStorageResponse.json();\n\n if (!Array.isArray(userStorage)) {\n return null;\n }\n\n const decryptedData: string[] = [];\n const reEncryptedEntries: [string, string][] = [];\n\n for (const entry of userStorage) {\n /* istanbul ignore if - unreachable if statement, but kept as edge case */\n if (!entry.Data) {\n continue;\n }\n\n try {\n const data = await encryption.decryptString(\n entry.Data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n decryptedData.push(data);\n\n // Re-encrypt the entry if the salt is different from the shared one\n const salt = encryption.getSalt(entry.Data);\n if (salt.toString() !== SHARED_SALT.toString()) {\n reEncryptedEntries.push([\n entry.HashedKey,\n await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n ),\n ]);\n }\n } catch {\n // do nothing\n }\n }\n\n // Re-upload the re-encrypted entries\n if (reEncryptedEntries.length) {\n await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(\n reEncryptedEntries,\n opts,\n );\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Set Storage Entry.\n *\n * @param data - data to store\n * @param opts - storage options\n */\nexport async function upsertUserStorage(\n data: string,\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData = await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param data - data to store, in the form of an array of [entryKey, entryValue] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorage(\n data: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData: string[][] = [];\n\n for (const d of data) {\n encryptedData.push([\n createSHA256Hash(d[0] + storageKey),\n await encryption.encryptString(d[1], opts.storageKey, nativeScryptCrypto),\n ]);\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(\n encryptedData: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!encryptedData.length) {\n return;\n }\n\n const { bearerToken, path } = opts;\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Delete Storage Entry.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorage(\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey } = opts;\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature/entry not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n\n/**\n * User Storage Service - Delete multiple storage entries for one specific feature.\n * You cannot use this method to delete multiple features at once.\n *\n * @param data - data to delete, in the form of an array entryKey[]\n * @param opts - storage options\n */\nexport async function batchDeleteUserStorage(\n data: string[],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey } = opts;\n\n const encryptedData: string[] = [];\n\n for (const d of data) {\n encryptedData.push(createSHA256Hash(d + storageKey));\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n // eslint-disable-next-line @typescript-eslint/naming-convention\n body: JSON.stringify({ batch_delete: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch delete data');\n }\n}\n\n/**\n * User Storage Service - Delete all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<void> {\n const { bearerToken, path } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n"]}
@@ -54,6 +54,14 @@ export declare function upsertUserStorage(data: string, opts: UserStorageOptions
54
54
  * @param opts - storage options
55
55
  */
56
56
  export declare function batchUpsertUserStorage(data: [string, string][], opts: UserStorageBatchUpsertOptions): Promise<void>;
57
+ /**
58
+ * User Storage Service - Set multiple storage entries for one specific feature.
59
+ * You cannot use this method to set multiple features at once.
60
+ *
61
+ * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs
62
+ * @param opts - storage options
63
+ */
64
+ export declare function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(encryptedData: [string, string][], opts: UserStorageBatchUpsertOptions): Promise<void>;
57
65
  /**
58
66
  * User Storage Service - Delete Storage Entry.
59
67
  *
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.cts","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,wCAAoC;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,0CAAsC;AAIlE,eAAO,MAAM,gBAAgB,EAAE,MAAmC,CAAC;AACnE,eAAO,MAAM,qBAAqB,QAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IAEnC,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IAEpD,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,EAAE,CAAC;AAEJ,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,YAAY,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,IAAI,EAAE,gCAAgC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,sBAAsB,GAAG;IACzE,IAAI,EAAE,8BAA8B,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,mCAAmC,CAAC;AAEhF;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2CxB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAqD1B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACxB,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAmBf"}
1
+ {"version":3,"file":"services.d.cts","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,wCAAoC;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,0CAAsC;AAIlE,eAAO,MAAM,gBAAgB,EAAE,MAAmC,CAAC;AACnE,eAAO,MAAM,qBAAqB,QAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IAEnC,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IAEpD,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,EAAE,CAAC;AAEJ,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,YAAY,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,IAAI,EAAE,gCAAgC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,sBAAsB,GAAG;IACzE,IAAI,EAAE,8BAA8B,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,mCAAmC,CAAC;AAEhF;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiDxB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CA2E1B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACxB,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;;;GAMG;AACH,wBAAsB,0DAA0D,CAC9E,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACjC,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -54,6 +54,14 @@ export declare function upsertUserStorage(data: string, opts: UserStorageOptions
54
54
  * @param opts - storage options
55
55
  */
56
56
  export declare function batchUpsertUserStorage(data: [string, string][], opts: UserStorageBatchUpsertOptions): Promise<void>;
57
+ /**
58
+ * User Storage Service - Set multiple storage entries for one specific feature.
59
+ * You cannot use this method to set multiple features at once.
60
+ *
61
+ * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs
62
+ * @param opts - storage options
63
+ */
64
+ export declare function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(encryptedData: [string, string][], opts: UserStorageBatchUpsertOptions): Promise<void>;
57
65
  /**
58
66
  * User Storage Service - Delete Storage Entry.
59
67
  *
@@ -1 +1 @@
1
- {"version":3,"file":"services.d.mts","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,wCAAoC;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,0CAAsC;AAIlE,eAAO,MAAM,gBAAgB,EAAE,MAAmC,CAAC;AACnE,eAAO,MAAM,qBAAqB,QAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IAEnC,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IAEpD,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,EAAE,CAAC;AAEJ,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,YAAY,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,IAAI,EAAE,gCAAgC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,sBAAsB,GAAG;IACzE,IAAI,EAAE,8BAA8B,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,mCAAmC,CAAC;AAEhF;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2CxB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAqD1B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACxB,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAmBf"}
1
+ {"version":3,"file":"services.d.mts","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC/B,wCAAoC;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,0CAAsC;AAIlE,eAAO,MAAM,gBAAgB,EAAE,MAAmC,CAAC;AACnE,eAAO,MAAM,qBAAqB,QAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IAEnC,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,uCAAuC,GAAG;IAEpD,SAAS,EAAE,MAAM,CAAC;IAElB,IAAI,EAAE,MAAM,CAAC;CACd,EAAE,CAAC;AAEJ,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,YAAY,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,IAAI,EAAE,gCAAgC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,sBAAsB,GAAG;IACzE,IAAI,EAAE,8BAA8B,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,mCAAmC,CAAC;AAEhF;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiDxB;AAED;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CA2E1B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACxB,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;;;GAMG;AACH,wBAAsB,0DAA0D,CAC9E,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EACjC,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;;;GAIG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,mCAAmC,GACxC,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -7,6 +7,7 @@ function $importDefault(module) {
7
7
  import $log from "loglevel";
8
8
  const log = $importDefault($log);
9
9
  import encryption, { createSHA256Hash } from "../../shared/encryption/index.mjs";
10
+ import { SHARED_SALT } from "../../shared/encryption/constants.mjs";
10
11
  import { Env, getEnvUrls } from "../../shared/env.mjs";
11
12
  import { createEntryPath } from "../../shared/storage-schema.mjs";
12
13
  const ENV_URLS = getEnvUrls(Env.PRD);
@@ -43,6 +44,11 @@ export async function getUserStorage(opts) {
43
44
  return null;
44
45
  }
45
46
  const decryptedData = await encryption.decryptString(encryptedData, opts.storageKey, nativeScryptCrypto);
47
+ // Re-encrypt and re-upload the entry if the salt is random
48
+ const salt = encryption.getSalt(encryptedData);
49
+ if (salt.toString() !== SHARED_SALT.toString()) {
50
+ await upsertUserStorage(decryptedData, opts);
51
+ }
46
52
  return decryptedData;
47
53
  }
48
54
  catch (e) {
@@ -78,6 +84,7 @@ export async function getUserStorageAllFeatureEntries(opts) {
78
84
  return null;
79
85
  }
80
86
  const decryptedData = [];
87
+ const reEncryptedEntries = [];
81
88
  for (const entry of userStorage) {
82
89
  /* istanbul ignore if - unreachable if statement, but kept as edge case */
83
90
  if (!entry.Data) {
@@ -86,11 +93,23 @@ export async function getUserStorageAllFeatureEntries(opts) {
86
93
  try {
87
94
  const data = await encryption.decryptString(entry.Data, opts.storageKey, nativeScryptCrypto);
88
95
  decryptedData.push(data);
96
+ // Re-encrypt the entry if the salt is different from the shared one
97
+ const salt = encryption.getSalt(entry.Data);
98
+ if (salt.toString() !== SHARED_SALT.toString()) {
99
+ reEncryptedEntries.push([
100
+ entry.HashedKey,
101
+ await encryption.encryptString(data, opts.storageKey, nativeScryptCrypto),
102
+ ]);
103
+ }
89
104
  }
90
105
  catch {
91
106
  // do nothing
92
107
  }
93
108
  }
109
+ // Re-upload the re-encrypted entries
110
+ if (reEncryptedEntries.length) {
111
+ await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(reEncryptedEntries, opts);
112
+ }
94
113
  return decryptedData;
95
114
  }
96
115
  catch (e) {
@@ -154,6 +173,32 @@ export async function batchUpsertUserStorage(data, opts) {
154
173
  throw new Error('user-storage - unable to batch upsert data');
155
174
  }
156
175
  }
176
+ /**
177
+ * User Storage Service - Set multiple storage entries for one specific feature.
178
+ * You cannot use this method to set multiple features at once.
179
+ *
180
+ * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs
181
+ * @param opts - storage options
182
+ */
183
+ export async function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(encryptedData, opts) {
184
+ if (!encryptedData.length) {
185
+ return;
186
+ }
187
+ const { bearerToken, path } = opts;
188
+ const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);
189
+ const formattedData = Object.fromEntries(encryptedData);
190
+ const res = await fetch(url.toString(), {
191
+ method: 'PUT',
192
+ headers: {
193
+ 'Content-Type': 'application/json',
194
+ Authorization: `Bearer ${bearerToken}`,
195
+ },
196
+ body: JSON.stringify({ data: formattedData }),
197
+ });
198
+ if (!res.ok) {
199
+ throw new Error('user-storage - unable to batch upsert data');
200
+ }
201
+ }
157
202
  /**
158
203
  * User Storage Service - Delete Storage Entry.
159
204
  *
@@ -1 +1 @@
1
- {"version":3,"file":"services.mjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"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","sourcesContent":["import log from 'loglevel';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { Env, getEnvUrls } from '../../shared/env';\nimport type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../shared/storage-schema';\nimport { createEntryPath } from '../../shared/storage-schema';\nimport type { NativeScrypt } from '../../shared/types/encryption';\n\nconst ENV_URLS = getEnvUrls(Env.PRD);\n\nexport const USER_STORAGE_API: string = ENV_URLS.userStorageApiUrl;\nexport const USER_STORAGE_ENDPOINT = `${USER_STORAGE_API}/api/v1/userstorage`;\n\n/**\n * This is the Server Response shape for a feature entry.\n */\nexport type GetUserStorageResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n};\n\nexport type GetUserStorageAllFeatureEntriesResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n}[];\n\nexport type UserStorageBaseOptions = {\n bearerToken: string;\n storageKey: string;\n nativeScryptCrypto?: NativeScrypt;\n};\n\nexport type UserStorageOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureAndKey;\n};\n\nexport type UserStorageAllFeatureEntriesOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureOnly;\n};\n\nexport type UserStorageBatchUpsertOptions = UserStorageAllFeatureEntriesOptions;\n\n/**\n * User Storage Service - Get Storage Entry.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorage(\n opts: UserStorageOptions,\n): Promise<string | null> {\n try {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates entry does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageResponse | null =\n await userStorageResponse.json();\n const encryptedData = userStorage?.Data ?? null;\n\n /* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */\n if (!encryptedData) {\n return null;\n }\n\n const decryptedData = await encryption.decryptString(\n encryptedData,\n opts.storageKey,\n nativeScryptCrypto,\n );\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Get all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<string[] | null> {\n try {\n const { bearerToken, path, nativeScryptCrypto } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates feature does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageAllFeatureEntriesResponse | null =\n await userStorageResponse.json();\n\n if (!Array.isArray(userStorage)) {\n return null;\n }\n\n const decryptedData: string[] = [];\n\n for (const entry of userStorage) {\n /* istanbul ignore if - unreachable if statement, but kept as edge case */\n if (!entry.Data) {\n continue;\n }\n\n try {\n const data = await encryption.decryptString(\n entry.Data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n decryptedData.push(data);\n } catch {\n // do nothing\n }\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Set Storage Entry.\n *\n * @param data - data to store\n * @param opts - storage options\n */\nexport async function upsertUserStorage(\n data: string,\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData = await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param data - data to store, in the form of an array of [entryKey, entryValue] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorage(\n data: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData: string[][] = [];\n\n for (const d of data) {\n encryptedData.push([\n createSHA256Hash(d[0] + storageKey),\n await encryption.encryptString(d[1], opts.storageKey, nativeScryptCrypto),\n ]);\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Delete Storage Entry.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorage(\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey } = opts;\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature/entry not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n\n/**\n * User Storage Service - Delete multiple storage entries for one specific feature.\n * You cannot use this method to delete multiple features at once.\n *\n * @param data - data to delete, in the form of an array entryKey[]\n * @param opts - storage options\n */\nexport async function batchDeleteUserStorage(\n data: string[],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey } = opts;\n\n const encryptedData: string[] = [];\n\n for (const d of data) {\n encryptedData.push(createSHA256Hash(d + storageKey));\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n // eslint-disable-next-line @typescript-eslint/naming-convention\n body: JSON.stringify({ batch_delete: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch delete data');\n }\n}\n\n/**\n * User Storage Service - Delete all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<void> {\n const { bearerToken, path } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n"]}
1
+ {"version":3,"file":"services.mjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"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","sourcesContent":["import log from 'loglevel';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { SHARED_SALT } from '../../shared/encryption/constants';\nimport { Env, getEnvUrls } from '../../shared/env';\nimport type {\n UserStoragePathWithFeatureAndKey,\n UserStoragePathWithFeatureOnly,\n} from '../../shared/storage-schema';\nimport { createEntryPath } from '../../shared/storage-schema';\nimport type { NativeScrypt } from '../../shared/types/encryption';\n\nconst ENV_URLS = getEnvUrls(Env.PRD);\n\nexport const USER_STORAGE_API: string = ENV_URLS.userStorageApiUrl;\nexport const USER_STORAGE_ENDPOINT = `${USER_STORAGE_API}/api/v1/userstorage`;\n\n/**\n * This is the Server Response shape for a feature entry.\n */\nexport type GetUserStorageResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n};\n\nexport type GetUserStorageAllFeatureEntriesResponse = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n HashedKey: string;\n // eslint-disable-next-line @typescript-eslint/naming-convention\n Data: string;\n}[];\n\nexport type UserStorageBaseOptions = {\n bearerToken: string;\n storageKey: string;\n nativeScryptCrypto?: NativeScrypt;\n};\n\nexport type UserStorageOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureAndKey;\n};\n\nexport type UserStorageAllFeatureEntriesOptions = UserStorageBaseOptions & {\n path: UserStoragePathWithFeatureOnly;\n};\n\nexport type UserStorageBatchUpsertOptions = UserStorageAllFeatureEntriesOptions;\n\n/**\n * User Storage Service - Get Storage Entry.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorage(\n opts: UserStorageOptions,\n): Promise<string | null> {\n try {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates entry does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageResponse | null =\n await userStorageResponse.json();\n const encryptedData = userStorage?.Data ?? null;\n\n /* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */\n if (!encryptedData) {\n return null;\n }\n\n const decryptedData = await encryption.decryptString(\n encryptedData,\n opts.storageKey,\n nativeScryptCrypto,\n );\n\n // Re-encrypt and re-upload the entry if the salt is random\n const salt = encryption.getSalt(encryptedData);\n if (salt.toString() !== SHARED_SALT.toString()) {\n await upsertUserStorage(decryptedData, opts);\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Get all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n * @returns The storage entry, or null if fails to find entry\n */\nexport async function getUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<string[] | null> {\n try {\n const { bearerToken, path, nativeScryptCrypto } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n // Acceptable error - since indicates feature does not exist.\n if (userStorageResponse.status === 404) {\n return null;\n }\n\n if (userStorageResponse.status !== 200) {\n throw new Error('Unable to get User Storage');\n }\n\n const userStorage: GetUserStorageAllFeatureEntriesResponse | null =\n await userStorageResponse.json();\n\n if (!Array.isArray(userStorage)) {\n return null;\n }\n\n const decryptedData: string[] = [];\n const reEncryptedEntries: [string, string][] = [];\n\n for (const entry of userStorage) {\n /* istanbul ignore if - unreachable if statement, but kept as edge case */\n if (!entry.Data) {\n continue;\n }\n\n try {\n const data = await encryption.decryptString(\n entry.Data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n decryptedData.push(data);\n\n // Re-encrypt the entry if the salt is different from the shared one\n const salt = encryption.getSalt(entry.Data);\n if (salt.toString() !== SHARED_SALT.toString()) {\n reEncryptedEntries.push([\n entry.HashedKey,\n await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n ),\n ]);\n }\n } catch {\n // do nothing\n }\n }\n\n // Re-upload the re-encrypted entries\n if (reEncryptedEntries.length) {\n await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(\n reEncryptedEntries,\n opts,\n );\n }\n\n return decryptedData;\n } catch (e) {\n log.error('Failed to get user storage', e);\n return null;\n }\n}\n\n/**\n * User Storage Service - Set Storage Entry.\n *\n * @param data - data to store\n * @param opts - storage options\n */\nexport async function upsertUserStorage(\n data: string,\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData = await encryption.encryptString(\n data,\n opts.storageKey,\n nativeScryptCrypto,\n );\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param data - data to store, in the form of an array of [entryKey, entryValue] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorage(\n data: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey, nativeScryptCrypto } = opts;\n\n const encryptedData: string[][] = [];\n\n for (const d of data) {\n encryptedData.push([\n createSHA256Hash(d[0] + storageKey),\n await encryption.encryptString(d[1], opts.storageKey, nativeScryptCrypto),\n ]);\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Set multiple storage entries for one specific feature.\n * You cannot use this method to set multiple features at once.\n *\n * @param encryptedData - data to store, in the form of an array of [hashedKey, encryptedData] pairs\n * @param opts - storage options\n */\nexport async function batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(\n encryptedData: [string, string][],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!encryptedData.length) {\n return;\n }\n\n const { bearerToken, path } = opts;\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const formattedData = Object.fromEntries(encryptedData);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n body: JSON.stringify({ data: formattedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch upsert data');\n }\n}\n\n/**\n * User Storage Service - Delete Storage Entry.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorage(\n opts: UserStorageOptions,\n): Promise<void> {\n const { bearerToken, path, storageKey } = opts;\n const encryptedPath = createEntryPath(path, storageKey);\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${encryptedPath}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature/entry not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n\n/**\n * User Storage Service - Delete multiple storage entries for one specific feature.\n * You cannot use this method to delete multiple features at once.\n *\n * @param data - data to delete, in the form of an array entryKey[]\n * @param opts - storage options\n */\nexport async function batchDeleteUserStorage(\n data: string[],\n opts: UserStorageBatchUpsertOptions,\n): Promise<void> {\n if (!data.length) {\n return;\n }\n\n const { bearerToken, path, storageKey } = opts;\n\n const encryptedData: string[] = [];\n\n for (const d of data) {\n encryptedData.push(createSHA256Hash(d + storageKey));\n }\n\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const res = await fetch(url.toString(), {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n // eslint-disable-next-line @typescript-eslint/naming-convention\n body: JSON.stringify({ batch_delete: encryptedData }),\n });\n\n if (!res.ok) {\n throw new Error('user-storage - unable to batch delete data');\n }\n}\n\n/**\n * User Storage Service - Delete all storage entries for a specific feature.\n *\n * @param opts - User Storage Options\n */\nexport async function deleteUserStorageAllFeatureEntries(\n opts: UserStorageAllFeatureEntriesOptions,\n): Promise<void> {\n const { bearerToken, path } = opts;\n const url = new URL(`${USER_STORAGE_ENDPOINT}/${path}`);\n\n const userStorageResponse = await fetch(url.toString(), {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (userStorageResponse.status === 404) {\n throw new Error('user-storage - feature not found');\n }\n\n if (!userStorageResponse.ok) {\n throw new Error('user-storage - unable to delete data');\n }\n}\n"]}
@@ -1,26 +1,49 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.handleMockUserStorageDeleteAllFeatureEntries = exports.handleMockUserStorageDelete = exports.handleMockUserStorageBatchDelete = exports.handleMockUserStoragePut = exports.handleMockUserStorageGetAllFeatureEntries = exports.handleMockUserStorageGet = exports.MOCK_STORAGE_RESPONSE = exports.MOCK_NOTIFICATIONS_DATA_ENCRYPTED = exports.MOCK_NOTIFICATIONS_DATA = exports.MOCK_STORAGE_KEY = void 0;
7
30
  const nock_1 = __importDefault(require("nock"));
8
- const encryption_1 = __importDefault(require("../../shared/encryption/index.cjs"));
31
+ const encryption_1 = __importStar(require("../../shared/encryption/index.cjs"));
9
32
  const env_1 = require("../../shared/env.cjs");
10
33
  const storage_schema_1 = require("../../shared/storage-schema.cjs");
11
34
  const user_storage_1 = require("../user-storage.cjs");
12
35
  // Example mock notifications storage entry (wildcard)
13
36
  const MOCK_STORAGE_URL = (0, user_storage_1.STORAGE_URL)(env_1.Env.DEV, `${storage_schema_1.USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`);
14
37
  const MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES = (0, user_storage_1.STORAGE_URL)(env_1.Env.DEV, storage_schema_1.USER_STORAGE_FEATURE_NAMES.notifications);
15
- exports.MOCK_STORAGE_KEY = 'MOCK_STORAGE_KEY';
38
+ exports.MOCK_STORAGE_KEY = (0, encryption_1.createSHA256Hash)('mockStorageKey');
16
39
  // TODO: Either fix this lint violation or explain why it's necessary to ignore.
17
40
  // eslint-disable-next-line @typescript-eslint/naming-convention
18
- exports.MOCK_NOTIFICATIONS_DATA = { is_compact: false };
19
- const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async () => await encryption_1.default.encryptString(JSON.stringify(exports.MOCK_NOTIFICATIONS_DATA), exports.MOCK_STORAGE_KEY);
41
+ exports.MOCK_NOTIFICATIONS_DATA = '{ is_compact: false }';
42
+ const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async (data) => await encryption_1.default.encryptString(data ?? exports.MOCK_NOTIFICATIONS_DATA, exports.MOCK_STORAGE_KEY);
20
43
  exports.MOCK_NOTIFICATIONS_DATA_ENCRYPTED = MOCK_NOTIFICATIONS_DATA_ENCRYPTED;
21
- const MOCK_STORAGE_RESPONSE = async () => ({
44
+ const MOCK_STORAGE_RESPONSE = async (data) => ({
22
45
  HashedKey: '8485d2c14c333ebca415140a276adaf546619b0efc204586b73a5d400a18a5e2',
23
- Data: await (0, exports.MOCK_NOTIFICATIONS_DATA_ENCRYPTED)(),
46
+ Data: await (0, exports.MOCK_NOTIFICATIONS_DATA_ENCRYPTED)(data),
24
47
  });
25
48
  exports.MOCK_STORAGE_RESPONSE = MOCK_STORAGE_RESPONSE;
26
49
  const handleMockUserStorageGet = async (mockReply) => {