@metamask/profile-sync-controller 3.0.0 → 3.1.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.
- package/CHANGELOG.md +14 -1
- package/dist/controllers/authentication/AuthenticationController.cjs +0 -1
- package/dist/controllers/authentication/AuthenticationController.cjs.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.cts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.d.mts.map +1 -1
- package/dist/controllers/authentication/AuthenticationController.mjs +0 -1
- package/dist/controllers/authentication/AuthenticationController.mjs.map +1 -1
- package/dist/controllers/user-storage/__fixtures__/mockResponses.cjs +7 -6
- package/dist/controllers/user-storage/__fixtures__/mockResponses.cjs.map +1 -1
- package/dist/controllers/user-storage/__fixtures__/mockResponses.d.cts.map +1 -1
- package/dist/controllers/user-storage/__fixtures__/mockResponses.d.mts.map +1 -1
- package/dist/controllers/user-storage/__fixtures__/mockResponses.mjs +7 -6
- package/dist/controllers/user-storage/__fixtures__/mockResponses.mjs.map +1 -1
- package/dist/controllers/user-storage/services.cjs +47 -1
- package/dist/controllers/user-storage/services.cjs.map +1 -1
- package/dist/controllers/user-storage/services.d.cts +8 -0
- package/dist/controllers/user-storage/services.d.cts.map +1 -1
- package/dist/controllers/user-storage/services.d.mts +8 -0
- package/dist/controllers/user-storage/services.d.mts.map +1 -1
- package/dist/controllers/user-storage/services.mjs +45 -0
- package/dist/controllers/user-storage/services.mjs.map +1 -1
- package/dist/sdk/__fixtures__/mock-userstorage.cjs +29 -6
- package/dist/sdk/__fixtures__/mock-userstorage.cjs.map +1 -1
- package/dist/sdk/__fixtures__/mock-userstorage.d.cts +4 -6
- package/dist/sdk/__fixtures__/mock-userstorage.d.cts.map +1 -1
- package/dist/sdk/__fixtures__/mock-userstorage.d.mts +4 -6
- package/dist/sdk/__fixtures__/mock-userstorage.d.mts.map +1 -1
- package/dist/sdk/__fixtures__/mock-userstorage.mjs +6 -6
- package/dist/sdk/__fixtures__/mock-userstorage.mjs.map +1 -1
- package/dist/sdk/user-storage.cjs +62 -9
- package/dist/sdk/user-storage.cjs.map +1 -1
- package/dist/sdk/user-storage.d.cts +5 -1
- package/dist/sdk/user-storage.d.cts.map +1 -1
- package/dist/sdk/user-storage.d.mts +5 -1
- package/dist/sdk/user-storage.d.mts.map +1 -1
- package/dist/sdk/user-storage.mjs +62 -9
- package/dist/sdk/user-storage.mjs.map +1 -1
- package/dist/shared/encryption/cache.cjs +12 -14
- package/dist/shared/encryption/cache.cjs.map +1 -1
- package/dist/shared/encryption/cache.d.cts +4 -3
- package/dist/shared/encryption/cache.d.cts.map +1 -1
- package/dist/shared/encryption/cache.d.mts +4 -3
- package/dist/shared/encryption/cache.d.mts.map +1 -1
- package/dist/shared/encryption/cache.mjs +11 -13
- package/dist/shared/encryption/cache.mjs.map +1 -1
- package/dist/shared/encryption/constants.cjs +18 -0
- package/dist/shared/encryption/constants.cjs.map +1 -0
- package/dist/shared/encryption/constants.d.cts +8 -0
- package/dist/shared/encryption/constants.d.cts.map +1 -0
- package/dist/shared/encryption/constants.d.mts +8 -0
- package/dist/shared/encryption/constants.d.mts.map +1 -0
- package/dist/shared/encryption/constants.mjs +15 -0
- package/dist/shared/encryption/constants.mjs.map +1 -0
- package/dist/shared/encryption/encryption.cjs +49 -25
- package/dist/shared/encryption/encryption.cjs.map +1 -1
- package/dist/shared/encryption/encryption.d.cts +2 -0
- package/dist/shared/encryption/encryption.d.cts.map +1 -1
- package/dist/shared/encryption/encryption.d.mts +2 -0
- package/dist/shared/encryption/encryption.d.mts.map +1 -1
- package/dist/shared/encryption/encryption.mjs +38 -14
- package/dist/shared/encryption/encryption.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.d.mts","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":"
|
|
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 =
|
|
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 = '
|
|
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(
|
|
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) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-userstorage.cjs","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mock-userstorage.cjs","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAwB;AAExB,gFAAuE;AACvE,8CAAuC;AACvC,oEAAyE;AACzE,sDAA8C;AAO9C,sDAAsD;AACtD,MAAM,gBAAgB,GAAG,IAAA,0BAAW,EAClC,SAAG,CAAC,GAAG,EACP,GAAG,2CAA0B,CAAC,aAAa,wBAAwB,CACpE,CAAC;AACF,MAAM,oCAAoC,GAAG,IAAA,0BAAW,EACtD,SAAG,CAAC,GAAG,EACP,2CAA0B,CAAC,aAAa,CACzC,CAAC;AAEW,QAAA,gBAAgB,GAAG,IAAA,6BAAgB,EAAC,gBAAgB,CAAC,CAAC;AACnE,gFAAgF;AAChF,gEAAgE;AACnD,QAAA,uBAAuB,GAAG,uBAAuB,CAAC;AACxD,MAAM,iCAAiC,GAAG,KAAK,EAAE,IAAa,EAAE,EAAE,CACvE,MAAM,oBAAU,CAAC,aAAa,CAC5B,IAAI,IAAI,+BAAuB,EAC/B,wBAAgB,CACjB,CAAC;AAJS,QAAA,iCAAiC,qCAI1C;AAEG,MAAM,qBAAqB,GAAG,KAAK,EAAE,IAAa,EAAE,EAAE,CAAC,CAAC;IAC7D,SAAS,EAAE,kEAAkE;IAC7E,IAAI,EAAE,MAAM,IAAA,yCAAiC,EAAC,IAAI,CAAC;CACpD,CAAC,CAAC;AAHU,QAAA,qBAAqB,yBAG/B;AAEI,MAAM,wBAAwB,GAAG,KAAK,EAAE,SAAqB,EAAE,EAAE;IACtE,MAAM,KAAK,GAAG,SAAS,IAAI;QACzB,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,MAAM,IAAA,6BAAqB,GAAE;KACpC,CAAC;IACF,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,gBAAgB,CAAC;SACxC,OAAO,EAAE;SACT,GAAG,CAAC,KAAK,CAAC;SACV,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAXW,QAAA,wBAAwB,4BAWnC;AAEK,MAAM,yCAAyC,GAAG,KAAK,EAC5D,SAAqB,EACrB,EAAE;IACF,MAAM,KAAK,GAAG,SAAS,IAAI;QACzB,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,CAAC,MAAM,IAAA,6BAAqB,GAAE,CAAC;KACtC,CAAC;IACF,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,oCAAoC,CAAC;SAC5D,OAAO,EAAE;SACT,GAAG,CAAC,EAAE,CAAC;SACP,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAbW,QAAA,yCAAyC,6CAapD;AAEK,MAAM,wBAAwB,GAAG,CACtC,SAAqB,EACrB,QAAiE,EACjE,EAAE;IACF,MAAM,KAAK,GAAG,SAAS,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,gBAAgB,CAAC;SACxC,OAAO,EAAE;SACT,GAAG,CAAC,KAAK,CAAC;SACV,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE;QAC9C,OAAO,MAAM,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEL,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAbW,QAAA,wBAAwB,4BAanC;AAEK,MAAM,gCAAgC,GAAG,CAC9C,SAAqB,EACrB,QAAiE,EACjE,EAAE;IACF,MAAM,KAAK,GAAG,SAAS,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,gBAAgB,CAAC;SACxC,OAAO,EAAE;SACT,GAAG,CAAC,KAAK,CAAC;SACV,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE;QAC9C,OAAO,MAAM,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEL,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAbW,QAAA,gCAAgC,oCAa3C;AAEK,MAAM,2BAA2B,GAAG,KAAK,EAAE,SAAqB,EAAE,EAAE;IACzE,MAAM,KAAK,GAAG,SAAS,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,gBAAgB,CAAC;SACxC,OAAO,EAAE;SACT,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEvB,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AARW,QAAA,2BAA2B,+BAQtC;AAEK,MAAM,4CAA4C,GAAG,KAAK,EAC/D,SAAqB,EACrB,EAAE;IACF,MAAM,KAAK,GAAG,SAAS,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAA,cAAI,EAAC,oCAAoC,CAAC;SAC5D,OAAO,EAAE;SACT,MAAM,CAAC,EAAE,CAAC;SACV,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAEvB,OAAO,YAAY,CAAC;AACtB,CAAC,CAAC;AAVW,QAAA,4CAA4C,gDAUvD","sourcesContent":["import nock from 'nock';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { Env } from '../../shared/env';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../shared/storage-schema';\nimport { STORAGE_URL } from '../user-storage';\n\ntype MockReply = {\n status: nock.StatusCode;\n body?: nock.Body;\n};\n\n// Example mock notifications storage entry (wildcard)\nconst MOCK_STORAGE_URL = STORAGE_URL(\n Env.DEV,\n `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n);\nconst MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES = STORAGE_URL(\n Env.DEV,\n USER_STORAGE_FEATURE_NAMES.notifications,\n);\n\nexport const MOCK_STORAGE_KEY = createSHA256Hash('mockStorageKey');\n// TODO: Either fix this lint violation or explain why it's necessary to ignore.\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const MOCK_NOTIFICATIONS_DATA = '{ is_compact: false }';\nexport const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async (data?: string) =>\n await encryption.encryptString(\n data ?? MOCK_NOTIFICATIONS_DATA,\n MOCK_STORAGE_KEY,\n );\n\nexport const MOCK_STORAGE_RESPONSE = async (data?: string) => ({\n HashedKey: '8485d2c14c333ebca415140a276adaf546619b0efc204586b73a5d400a18a5e2',\n Data: await MOCK_NOTIFICATIONS_DATA_ENCRYPTED(data),\n});\n\nexport const handleMockUserStorageGet = async (mockReply?: MockReply) => {\n const reply = mockReply ?? {\n status: 200,\n body: await MOCK_STORAGE_RESPONSE(),\n };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .get(/.*/u)\n .reply(reply.status, reply.body);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageGetAllFeatureEntries = async (\n mockReply?: MockReply,\n) => {\n const reply = mockReply ?? {\n status: 200,\n body: [await MOCK_STORAGE_RESPONSE()],\n };\n const mockEndpoint = nock(MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES)\n .persist()\n .get('')\n .reply(reply.status, reply.body);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStoragePut = (\n mockReply?: MockReply,\n callback?: (uri: string, requestBody: nock.Body) => Promise<void>,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .put(/.*/u)\n .reply(reply.status, async (uri, requestBody) => {\n return await callback?.(uri, requestBody);\n });\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageBatchDelete = (\n mockReply?: MockReply,\n callback?: (uri: string, requestBody: nock.Body) => Promise<void>,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .put(/.*/u)\n .reply(reply.status, async (uri, requestBody) => {\n return await callback?.(uri, requestBody);\n });\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageDelete = async (mockReply?: MockReply) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .delete(/.*/u)\n .reply(reply.status);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageDeleteAllFeatureEntries = async (\n mockReply?: MockReply,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES)\n .persist()\n .delete('')\n .reply(reply.status);\n\n return mockEndpoint;\n};\n"]}
|
|
@@ -3,12 +3,10 @@ type MockReply = {
|
|
|
3
3
|
status: nock.StatusCode;
|
|
4
4
|
body?: nock.Body;
|
|
5
5
|
};
|
|
6
|
-
export declare const MOCK_STORAGE_KEY
|
|
7
|
-
export declare const MOCK_NOTIFICATIONS_DATA:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare const MOCK_NOTIFICATIONS_DATA_ENCRYPTED: () => Promise<string>;
|
|
11
|
-
export declare const MOCK_STORAGE_RESPONSE: () => Promise<{
|
|
6
|
+
export declare const MOCK_STORAGE_KEY: string;
|
|
7
|
+
export declare const MOCK_NOTIFICATIONS_DATA = "{ is_compact: false }";
|
|
8
|
+
export declare const MOCK_NOTIFICATIONS_DATA_ENCRYPTED: (data?: string) => Promise<string>;
|
|
9
|
+
export declare const MOCK_STORAGE_RESPONSE: (data?: string) => Promise<{
|
|
12
10
|
HashedKey: string;
|
|
13
11
|
Data: string;
|
|
14
12
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-userstorage.d.cts","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,aAAa;AAOxB,KAAK,SAAS,GAAG;IACf,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;CAClB,CAAC;AAYF,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"mock-userstorage.d.cts","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,aAAa;AAOxB,KAAK,SAAS,GAAG;IACf,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;CAClB,CAAC;AAYF,eAAO,MAAM,gBAAgB,QAAqC,CAAC;AAGnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAC/D,eAAO,MAAM,iCAAiC,UAAiB,MAAM,oBAIlE,CAAC;AAEJ,eAAO,MAAM,qBAAqB,UAAiB,MAAM;;;EAGvD,CAAC;AAEH,eAAO,MAAM,wBAAwB,eAAsB,SAAS,wBAWnE,CAAC;AAEF,eAAO,MAAM,yCAAyC,eACxC,SAAS,wBAYtB,CAAC;AAEF,eAAO,MAAM,wBAAwB,eACvB,SAAS,oBACJ,MAAM,eAAe,SAAS,KAAK,QAAQ,IAAI,CAAC,4BAWlE,CAAC;AAEF,eAAO,MAAM,gCAAgC,eAC/B,SAAS,oBACJ,MAAM,eAAe,SAAS,KAAK,QAAQ,IAAI,CAAC,4BAWlE,CAAC;AAEF,eAAO,MAAM,2BAA2B,eAAsB,SAAS,wBAQtE,CAAC;AAEF,eAAO,MAAM,4CAA4C,eAC3C,SAAS,wBAStB,CAAC"}
|
|
@@ -3,12 +3,10 @@ type MockReply = {
|
|
|
3
3
|
status: nock.StatusCode;
|
|
4
4
|
body?: nock.Body;
|
|
5
5
|
};
|
|
6
|
-
export declare const MOCK_STORAGE_KEY
|
|
7
|
-
export declare const MOCK_NOTIFICATIONS_DATA:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export declare const MOCK_NOTIFICATIONS_DATA_ENCRYPTED: () => Promise<string>;
|
|
11
|
-
export declare const MOCK_STORAGE_RESPONSE: () => Promise<{
|
|
6
|
+
export declare const MOCK_STORAGE_KEY: string;
|
|
7
|
+
export declare const MOCK_NOTIFICATIONS_DATA = "{ is_compact: false }";
|
|
8
|
+
export declare const MOCK_NOTIFICATIONS_DATA_ENCRYPTED: (data?: string) => Promise<string>;
|
|
9
|
+
export declare const MOCK_STORAGE_RESPONSE: (data?: string) => Promise<{
|
|
12
10
|
HashedKey: string;
|
|
13
11
|
Data: string;
|
|
14
12
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-userstorage.d.mts","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,aAAa;AAOxB,KAAK,SAAS,GAAG;IACf,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;CAClB,CAAC;AAYF,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"mock-userstorage.d.mts","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,aAAa;AAOxB,KAAK,SAAS,GAAG;IACf,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;CAClB,CAAC;AAYF,eAAO,MAAM,gBAAgB,QAAqC,CAAC;AAGnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAC/D,eAAO,MAAM,iCAAiC,UAAiB,MAAM,oBAIlE,CAAC;AAEJ,eAAO,MAAM,qBAAqB,UAAiB,MAAM;;;EAGvD,CAAC;AAEH,eAAO,MAAM,wBAAwB,eAAsB,SAAS,wBAWnE,CAAC;AAEF,eAAO,MAAM,yCAAyC,eACxC,SAAS,wBAYtB,CAAC;AAEF,eAAO,MAAM,wBAAwB,eACvB,SAAS,oBACJ,MAAM,eAAe,SAAS,KAAK,QAAQ,IAAI,CAAC,4BAWlE,CAAC;AAEF,eAAO,MAAM,gCAAgC,eAC/B,SAAS,oBACJ,MAAM,eAAe,SAAS,KAAK,QAAQ,IAAI,CAAC,4BAWlE,CAAC;AAEF,eAAO,MAAM,2BAA2B,eAAsB,SAAS,wBAQtE,CAAC;AAEF,eAAO,MAAM,4CAA4C,eAC3C,SAAS,wBAStB,CAAC"}
|
|
@@ -6,21 +6,21 @@ function $importDefault(module) {
|
|
|
6
6
|
}
|
|
7
7
|
import $nock from "nock";
|
|
8
8
|
const nock = $importDefault($nock);
|
|
9
|
-
import encryption from "../../shared/encryption/index.mjs";
|
|
9
|
+
import encryption, { createSHA256Hash } from "../../shared/encryption/index.mjs";
|
|
10
10
|
import { Env } from "../../shared/env.mjs";
|
|
11
11
|
import { USER_STORAGE_FEATURE_NAMES } from "../../shared/storage-schema.mjs";
|
|
12
12
|
import { STORAGE_URL } from "../user-storage.mjs";
|
|
13
13
|
// Example mock notifications storage entry (wildcard)
|
|
14
14
|
const MOCK_STORAGE_URL = STORAGE_URL(Env.DEV, `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`);
|
|
15
15
|
const MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES = STORAGE_URL(Env.DEV, USER_STORAGE_FEATURE_NAMES.notifications);
|
|
16
|
-
export const MOCK_STORAGE_KEY = '
|
|
16
|
+
export const MOCK_STORAGE_KEY = createSHA256Hash('mockStorageKey');
|
|
17
17
|
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
19
|
-
export const MOCK_NOTIFICATIONS_DATA = { is_compact: false };
|
|
20
|
-
export const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async () => await encryption.encryptString(
|
|
21
|
-
export const MOCK_STORAGE_RESPONSE = async () => ({
|
|
19
|
+
export const MOCK_NOTIFICATIONS_DATA = '{ is_compact: false }';
|
|
20
|
+
export const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async (data) => await encryption.encryptString(data ?? MOCK_NOTIFICATIONS_DATA, MOCK_STORAGE_KEY);
|
|
21
|
+
export const MOCK_STORAGE_RESPONSE = async (data) => ({
|
|
22
22
|
HashedKey: '8485d2c14c333ebca415140a276adaf546619b0efc204586b73a5d400a18a5e2',
|
|
23
|
-
Data: await MOCK_NOTIFICATIONS_DATA_ENCRYPTED(),
|
|
23
|
+
Data: await MOCK_NOTIFICATIONS_DATA_ENCRYPTED(data),
|
|
24
24
|
});
|
|
25
25
|
export const handleMockUserStorageGet = async (mockReply) => {
|
|
26
26
|
const reply = mockReply ?? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-userstorage.mjs","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,KAAI,aAAa;;AAExB,OAAO,UAAU,0CAAgC;
|
|
1
|
+
{"version":3,"file":"mock-userstorage.mjs","sourceRoot":"","sources":["../../../src/sdk/__fixtures__/mock-userstorage.ts"],"names":[],"mappings":"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","sourcesContent":["import nock from 'nock';\n\nimport encryption, { createSHA256Hash } from '../../shared/encryption';\nimport { Env } from '../../shared/env';\nimport { USER_STORAGE_FEATURE_NAMES } from '../../shared/storage-schema';\nimport { STORAGE_URL } from '../user-storage';\n\ntype MockReply = {\n status: nock.StatusCode;\n body?: nock.Body;\n};\n\n// Example mock notifications storage entry (wildcard)\nconst MOCK_STORAGE_URL = STORAGE_URL(\n Env.DEV,\n `${USER_STORAGE_FEATURE_NAMES.notifications}.notification_settings`,\n);\nconst MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES = STORAGE_URL(\n Env.DEV,\n USER_STORAGE_FEATURE_NAMES.notifications,\n);\n\nexport const MOCK_STORAGE_KEY = createSHA256Hash('mockStorageKey');\n// TODO: Either fix this lint violation or explain why it's necessary to ignore.\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const MOCK_NOTIFICATIONS_DATA = '{ is_compact: false }';\nexport const MOCK_NOTIFICATIONS_DATA_ENCRYPTED = async (data?: string) =>\n await encryption.encryptString(\n data ?? MOCK_NOTIFICATIONS_DATA,\n MOCK_STORAGE_KEY,\n );\n\nexport const MOCK_STORAGE_RESPONSE = async (data?: string) => ({\n HashedKey: '8485d2c14c333ebca415140a276adaf546619b0efc204586b73a5d400a18a5e2',\n Data: await MOCK_NOTIFICATIONS_DATA_ENCRYPTED(data),\n});\n\nexport const handleMockUserStorageGet = async (mockReply?: MockReply) => {\n const reply = mockReply ?? {\n status: 200,\n body: await MOCK_STORAGE_RESPONSE(),\n };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .get(/.*/u)\n .reply(reply.status, reply.body);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageGetAllFeatureEntries = async (\n mockReply?: MockReply,\n) => {\n const reply = mockReply ?? {\n status: 200,\n body: [await MOCK_STORAGE_RESPONSE()],\n };\n const mockEndpoint = nock(MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES)\n .persist()\n .get('')\n .reply(reply.status, reply.body);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStoragePut = (\n mockReply?: MockReply,\n callback?: (uri: string, requestBody: nock.Body) => Promise<void>,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .put(/.*/u)\n .reply(reply.status, async (uri, requestBody) => {\n return await callback?.(uri, requestBody);\n });\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageBatchDelete = (\n mockReply?: MockReply,\n callback?: (uri: string, requestBody: nock.Body) => Promise<void>,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .put(/.*/u)\n .reply(reply.status, async (uri, requestBody) => {\n return await callback?.(uri, requestBody);\n });\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageDelete = async (mockReply?: MockReply) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL)\n .persist()\n .delete(/.*/u)\n .reply(reply.status);\n\n return mockEndpoint;\n};\n\nexport const handleMockUserStorageDeleteAllFeatureEntries = async (\n mockReply?: MockReply,\n) => {\n const reply = mockReply ?? { status: 204 };\n const mockEndpoint = nock(MOCK_STORAGE_URL_ALL_FEATURE_ENTRIES)\n .persist()\n .delete('')\n .reply(reply.status);\n\n return mockEndpoint;\n};\n"]}
|
|
@@ -27,10 +27,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
27
27
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
28
28
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
29
29
|
};
|
|
30
|
-
var _UserStorage_instances, _UserStorage_upsertUserStorage, _UserStorage_batchUpsertUserStorage, _UserStorage_getUserStorage, _UserStorage_getUserStorageAllFeatureEntries, _UserStorage_deleteUserStorage, _UserStorage_deleteUserStorageAllFeatureEntries, _UserStorage_batchDeleteUserStorage, _UserStorage_createEntryKey, _UserStorage_getAuthorizationHeader;
|
|
30
|
+
var _UserStorage_instances, _UserStorage_upsertUserStorage, _UserStorage_batchUpsertUserStorage, _UserStorage_batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries, _UserStorage_getUserStorage, _UserStorage_getUserStorageAllFeatureEntries, _UserStorage_deleteUserStorage, _UserStorage_deleteUserStorageAllFeatureEntries, _UserStorage_batchDeleteUserStorage, _UserStorage_createEntryKey, _UserStorage_getAuthorizationHeader;
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
32
|
exports.UserStorage = exports.STORAGE_URL = void 0;
|
|
33
33
|
const encryption_1 = __importStar(require("../shared/encryption/index.cjs"));
|
|
34
|
+
const constants_1 = require("../shared/encryption/constants.cjs");
|
|
34
35
|
const env_1 = require("../shared/env.cjs");
|
|
35
36
|
const storage_schema_1 = require("../shared/storage-schema.cjs");
|
|
36
37
|
const errors_1 = require("./errors.cjs");
|
|
@@ -140,6 +141,34 @@ _UserStorage_instances = new WeakSet(), _UserStorage_upsertUserStorage = async f
|
|
|
140
141
|
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e ?? '');
|
|
141
142
|
throw new errors_1.UserStorageError(`failed to batch upsert user storage for path '${path}'. ${errorMessage}`);
|
|
142
143
|
}
|
|
144
|
+
}, _UserStorage_batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries = async function _UserStorage_batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(path, encryptedData) {
|
|
145
|
+
try {
|
|
146
|
+
if (!encryptedData.length) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const headers = await __classPrivateFieldGet(this, _UserStorage_instances, "m", _UserStorage_getAuthorizationHeader).call(this);
|
|
150
|
+
const url = new URL((0, exports.STORAGE_URL)(this.env, path));
|
|
151
|
+
const response = await fetch(url.toString(), {
|
|
152
|
+
method: 'PUT',
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
...headers,
|
|
156
|
+
},
|
|
157
|
+
body: JSON.stringify({ data: Object.fromEntries(encryptedData) }),
|
|
158
|
+
});
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
const responseBody = await response.json().catch(() => ({
|
|
161
|
+
message: 'unknown',
|
|
162
|
+
error: 'unknown',
|
|
163
|
+
}));
|
|
164
|
+
throw new Error(`HTTP error message: ${responseBody.message}, error: ${responseBody.error}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
/* istanbul ignore next */
|
|
169
|
+
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e ?? '');
|
|
170
|
+
throw new errors_1.UserStorageError(`failed to batch upsert user storage for path '${path}'. ${errorMessage}`);
|
|
171
|
+
}
|
|
143
172
|
}, _UserStorage_getUserStorage = async function _UserStorage_getUserStorage(path) {
|
|
144
173
|
try {
|
|
145
174
|
const headers = await __classPrivateFieldGet(this, _UserStorage_instances, "m", _UserStorage_getAuthorizationHeader).call(this);
|
|
@@ -160,7 +189,13 @@ _UserStorage_instances = new WeakSet(), _UserStorage_upsertUserStorage = async f
|
|
|
160
189
|
throw new Error(`HTTP error message: ${responseBody.message}, error: ${responseBody.error}`);
|
|
161
190
|
}
|
|
162
191
|
const { Data: encryptedData } = await response.json();
|
|
163
|
-
|
|
192
|
+
const decryptedData = await encryption_1.default.decryptString(encryptedData, storageKey);
|
|
193
|
+
// Re-encrypt the entry if it was encrypted with a random salt
|
|
194
|
+
const salt = encryption_1.default.getSalt(encryptedData);
|
|
195
|
+
if (salt.toString() !== constants_1.SHARED_SALT.toString()) {
|
|
196
|
+
await __classPrivateFieldGet(this, _UserStorage_instances, "m", _UserStorage_upsertUserStorage).call(this, path, decryptedData);
|
|
197
|
+
}
|
|
198
|
+
return decryptedData;
|
|
164
199
|
}
|
|
165
200
|
catch (e) {
|
|
166
201
|
if (e instanceof errors_1.NotFoundError) {
|
|
@@ -192,15 +227,33 @@ _UserStorage_instances = new WeakSet(), _UserStorage_upsertUserStorage = async f
|
|
|
192
227
|
if (!Array.isArray(userStorage)) {
|
|
193
228
|
return null;
|
|
194
229
|
}
|
|
195
|
-
const decryptedData =
|
|
230
|
+
const decryptedData = [];
|
|
231
|
+
const reEncryptedEntries = [];
|
|
232
|
+
for (const entry of userStorage) {
|
|
196
233
|
if (!entry.Data) {
|
|
197
|
-
|
|
234
|
+
continue;
|
|
198
235
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
236
|
+
try {
|
|
237
|
+
const data = await encryption_1.default.decryptString(entry.Data, storageKey);
|
|
238
|
+
decryptedData.push(data);
|
|
239
|
+
// Re-encrypt the entry was encrypted with a random salt
|
|
240
|
+
const salt = encryption_1.default.getSalt(entry.Data);
|
|
241
|
+
if (salt.toString() !== constants_1.SHARED_SALT.toString()) {
|
|
242
|
+
reEncryptedEntries.push([
|
|
243
|
+
entry.HashedKey,
|
|
244
|
+
await encryption_1.default.encryptString(data, storageKey),
|
|
245
|
+
]);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// do nothing
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Re-upload the re-encrypted entries
|
|
253
|
+
if (reEncryptedEntries.length) {
|
|
254
|
+
await __classPrivateFieldGet(this, _UserStorage_instances, "m", _UserStorage_batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries).call(this, path, reEncryptedEntries);
|
|
255
|
+
}
|
|
256
|
+
return decryptedData;
|
|
204
257
|
}
|
|
205
258
|
catch (e) {
|
|
206
259
|
if (e instanceof errors_1.NotFoundError) {
|