@metamask/profile-sync-controller 2.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 +45 -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/UserStorageController.cjs +55 -22
- package/dist/controllers/user-storage/UserStorageController.cjs.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.cts +32 -15
- package/dist/controllers/user-storage/UserStorageController.d.cts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.d.mts +32 -15
- package/dist/controllers/user-storage/UserStorageController.d.mts.map +1 -1
- package/dist/controllers/user-storage/UserStorageController.mjs +56 -23
- package/dist/controllers/user-storage/UserStorageController.mjs.map +1 -1
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.cjs +171 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.cjs.map +1 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.d.cts +102 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.d.cts.map +1 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.d.mts +102 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.d.mts.map +1 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.mjs +166 -0
- package/dist/controllers/user-storage/__fixtures__/mockMessenger.mjs.map +1 -0
- 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/network-syncing/__fixtures__/mockNetwork.cjs +20 -2
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.d.cts +8 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.d.mts +8 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.mjs +17 -1
- package/dist/controllers/user-storage/network-syncing/__fixtures__/mockNetwork.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/add-network-utils.cjs +35 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.cjs.map +1 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.d.cts +21 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.d.cts.map +1 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.d.mts +21 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.d.mts.map +1 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.mjs +30 -0
- package/dist/controllers/user-storage/network-syncing/add-network-utils.mjs.map +1 -0
- package/dist/controllers/user-storage/network-syncing/controller-integration.cjs +157 -29
- package/dist/controllers/user-storage/network-syncing/controller-integration.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/controller-integration.d.cts +45 -3
- package/dist/controllers/user-storage/network-syncing/controller-integration.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/controller-integration.d.mts +45 -3
- package/dist/controllers/user-storage/network-syncing/controller-integration.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/controller-integration.mjs +154 -28
- package/dist/controllers/user-storage/network-syncing/controller-integration.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/services.cjs +9 -4
- package/dist/controllers/user-storage/network-syncing/services.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/services.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/services.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/services.mjs +10 -5
- package/dist/controllers/user-storage/network-syncing/services.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-all.cjs +10 -28
- package/dist/controllers/user-storage/network-syncing/sync-all.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-all.d.cts +5 -8
- package/dist/controllers/user-storage/network-syncing/sync-all.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-all.d.mts +5 -8
- package/dist/controllers/user-storage/network-syncing/sync-all.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-all.mjs +9 -26
- package/dist/controllers/user-storage/network-syncing/sync-all.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.cjs +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.d.cts +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.d.mts +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.mjs +1 -1
- package/dist/controllers/user-storage/network-syncing/sync-mutations.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/types.cjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/types.d.cts +1 -4
- package/dist/controllers/user-storage/network-syncing/types.d.cts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/types.d.mts +1 -4
- package/dist/controllers/user-storage/network-syncing/types.d.mts.map +1 -1
- package/dist/controllers/user-storage/network-syncing/types.mjs.map +1 -1
- package/dist/controllers/user-storage/network-syncing/update-network-utils.cjs +126 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.cjs.map +1 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.d.cts +56 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.d.cts.map +1 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.d.mts +56 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.d.mts.map +1 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.mjs +119 -0
- package/dist/controllers/user-storage/network-syncing/update-network-utils.mjs.map +1 -0
- package/dist/controllers/user-storage/services.cjs +49 -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 +47 -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/authentication-jwt-bearer/flow-siwe.mjs +1 -2
- package/dist/sdk/authentication-jwt-bearer/flow-siwe.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 +11 -6
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { RpcEndpointType } from "@metamask/network-controller";
|
|
2
|
+
import { setDifference } from "../utils.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Will map the network configuration we want to update into something valid that `updateNetwork` accepts
|
|
5
|
+
* Exported for testability
|
|
6
|
+
*
|
|
7
|
+
* @param props - properties
|
|
8
|
+
* @param props.originalNetworkConfiguration - original network configuration we will override
|
|
9
|
+
* @param props.newNetworkConfiguration - new network configuration
|
|
10
|
+
* @returns NetworkConfiguration to dispatch to `NetworkController:updateNetwork`
|
|
11
|
+
*/
|
|
12
|
+
export const getMappedNetworkConfiguration = (props) => {
|
|
13
|
+
const { originalNetworkConfiguration, newNetworkConfiguration } = props;
|
|
14
|
+
// Map of URL <> clientId (url is unique)
|
|
15
|
+
const originalRPCUrlMap = new Map(originalNetworkConfiguration.rpcEndpoints.map((r) => [r.url, r.networkClientId]));
|
|
16
|
+
const updateNetworkConfig = newNetworkConfiguration;
|
|
17
|
+
updateNetworkConfig.rpcEndpoints = updateNetworkConfig.rpcEndpoints.map((r) => {
|
|
18
|
+
const originalRPCClientId = originalRPCUrlMap.get(r.url);
|
|
19
|
+
// This is an existing RPC, so use the clients networkClientId
|
|
20
|
+
if (originalRPCClientId) {
|
|
21
|
+
r.networkClientId = originalRPCClientId;
|
|
22
|
+
return r;
|
|
23
|
+
}
|
|
24
|
+
// This is a new RPC, so remove the remote networkClientId
|
|
25
|
+
r.networkClientId = undefined;
|
|
26
|
+
return r;
|
|
27
|
+
});
|
|
28
|
+
return updateNetworkConfig;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Will insert any missing infura RPCs, as we cannot remove infura RPC
|
|
32
|
+
* Exported for testability
|
|
33
|
+
* @param props - properties
|
|
34
|
+
* @param props.originalNetworkConfiguration - original network configuration
|
|
35
|
+
* @param props.updateNetworkConfiguration - the updated network configuration to use when dispatching `NetworkController:updateNetwork`
|
|
36
|
+
* @returns mutates and returns the updateNetworkConfiguration
|
|
37
|
+
*/
|
|
38
|
+
export const appendMissingInfuraNetworks = (props) => {
|
|
39
|
+
const { originalNetworkConfiguration, updateNetworkConfiguration } = props;
|
|
40
|
+
// Ensure we have not removed any infura networks (and add them back if they were removed)
|
|
41
|
+
const origInfuraRPCMap = new Map(originalNetworkConfiguration.rpcEndpoints
|
|
42
|
+
.filter((r) => r.type === RpcEndpointType.Infura)
|
|
43
|
+
.map((r) => [r.networkClientId, r]));
|
|
44
|
+
const newInfuraRPCMap = new Map(updateNetworkConfiguration.rpcEndpoints
|
|
45
|
+
.filter((r) => r.type === RpcEndpointType.Infura && r.networkClientId)
|
|
46
|
+
.map((r) => [r.networkClientId, r]));
|
|
47
|
+
const missingOrigInfuraRPCs = setDifference(new Set(origInfuraRPCMap.keys()), new Set(newInfuraRPCMap.keys()));
|
|
48
|
+
if (missingOrigInfuraRPCs.size > 0) {
|
|
49
|
+
const missingRPCs = [];
|
|
50
|
+
missingOrigInfuraRPCs.forEach((clientId) => {
|
|
51
|
+
missingRPCs.push(origInfuraRPCMap.get(clientId));
|
|
52
|
+
});
|
|
53
|
+
updateNetworkConfiguration.rpcEndpoints.unshift(...missingRPCs);
|
|
54
|
+
}
|
|
55
|
+
return updateNetworkConfiguration;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The `NetworkController:updateNetwork` method will require us to pass in a `replacementSelectedRpcEndpointIndex` if the selected RPC is removed or modified
|
|
59
|
+
* @param props - properties
|
|
60
|
+
* @param props.originalNetworkConfiguration - the original network configuration
|
|
61
|
+
* @param props.updateNetworkConfiguration - the new network configuration we will use to update
|
|
62
|
+
* @param props.selectedNetworkClientId - the NetworkController's selected network id.
|
|
63
|
+
* @returns the new RPC index if it needs modification
|
|
64
|
+
*/
|
|
65
|
+
export const getNewRPCIndex = (props) => {
|
|
66
|
+
const { originalNetworkConfiguration, updateNetworkConfiguration, selectedNetworkClientId, } = props;
|
|
67
|
+
const isRPCInNewList = updateNetworkConfiguration.rpcEndpoints.some((r) => r.networkClientId === selectedNetworkClientId);
|
|
68
|
+
const isRPCInOldList = originalNetworkConfiguration.rpcEndpoints.some((r) => r.networkClientId === selectedNetworkClientId);
|
|
69
|
+
const getAnyRPCIndex = () => Math.max(updateNetworkConfiguration.rpcEndpoints.findIndex((r) => Boolean(r.networkClientId)), 0);
|
|
70
|
+
// We have removed the selected RPC, so we must point to a new RPC index
|
|
71
|
+
if (isRPCInOldList && !isRPCInNewList) {
|
|
72
|
+
// Try finding an existing index, or default to first RPC.
|
|
73
|
+
const newIndex = getAnyRPCIndex();
|
|
74
|
+
return newIndex;
|
|
75
|
+
}
|
|
76
|
+
// We have updated the selected RPC, so we must point to the same RPC index (or a new one)
|
|
77
|
+
if (isRPCInOldList && isRPCInNewList) {
|
|
78
|
+
const existingIndex = updateNetworkConfiguration.rpcEndpoints.findIndex((r) => r.networkClientId === selectedNetworkClientId);
|
|
79
|
+
/* istanbul ignore next - the `getAnyRPCIndex` should not be reachable since this is an existing network */
|
|
80
|
+
return existingIndex !== -1 ? existingIndex : getAnyRPCIndex();
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* create the correct `NetworkController:updateNetwork` parameters
|
|
86
|
+
* @param props - properties
|
|
87
|
+
* @param props.originalNetworkConfiguration - original config
|
|
88
|
+
* @param props.newNetworkConfiguration - new config (from remote)
|
|
89
|
+
* @param props.selectedNetworkClientId - the current selected network client id
|
|
90
|
+
* @returns parameters to be used for `NetworkController:updateNetwork` call
|
|
91
|
+
*/
|
|
92
|
+
export const createUpdateNetworkProps = (props) => {
|
|
93
|
+
const { originalNetworkConfiguration, newNetworkConfiguration, selectedNetworkClientId, } = props;
|
|
94
|
+
// The `NetworkController:updateNetwork` has a strict set of rules to follow
|
|
95
|
+
// New RPCs that we are adding must not have a networkClientId
|
|
96
|
+
// Existing RPCs must point to the correct networkClientId (so we must convert and use this client clientIds set)
|
|
97
|
+
// Removing RPCs are omitted from the list
|
|
98
|
+
// We cannot remove infura RPCs - so ensure that they stay populated
|
|
99
|
+
// If we are removing a selected RPC - then we need to provide `replacementSelectedRpcEndpointIndex` to an index in the new list
|
|
100
|
+
// If we are updating a selected RPC - then we need to provide `replacementSelectedRpcEndpointIndex` to the index in the new list
|
|
101
|
+
const mappedNetworkConfiguration = getMappedNetworkConfiguration({
|
|
102
|
+
originalNetworkConfiguration,
|
|
103
|
+
newNetworkConfiguration,
|
|
104
|
+
});
|
|
105
|
+
appendMissingInfuraNetworks({
|
|
106
|
+
originalNetworkConfiguration,
|
|
107
|
+
updateNetworkConfiguration: mappedNetworkConfiguration,
|
|
108
|
+
});
|
|
109
|
+
const updatedRPCIndex = getNewRPCIndex({
|
|
110
|
+
originalNetworkConfiguration,
|
|
111
|
+
updateNetworkConfiguration: mappedNetworkConfiguration,
|
|
112
|
+
selectedNetworkClientId,
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
updateNetworkFields: mappedNetworkConfiguration,
|
|
116
|
+
newSelectedRpcEndpointIndex: updatedRPCIndex,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=update-network-utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-network-utils.mjs","sourceRoot":"","sources":["../../../../src/controllers/user-storage/network-syncing/update-network-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n RpcEndpointType,\n type UpdateNetworkFields,\n type NetworkConfiguration,\n} from '@metamask/network-controller';\n\nimport { setDifference } from '../utils';\n\n/**\n * Will map the network configuration we want to update into something valid that `updateNetwork` accepts\n * Exported for testability\n *\n * @param props - properties\n * @param props.originalNetworkConfiguration - original network configuration we will override\n * @param props.newNetworkConfiguration - new network configuration\n * @returns NetworkConfiguration to dispatch to `NetworkController:updateNetwork`\n */\nexport const getMappedNetworkConfiguration = (props: {\n originalNetworkConfiguration: NetworkConfiguration;\n newNetworkConfiguration: NetworkConfiguration;\n}): UpdateNetworkFields => {\n const { originalNetworkConfiguration, newNetworkConfiguration } = props;\n\n // Map of URL <> clientId (url is unique)\n const originalRPCUrlMap = new Map(\n originalNetworkConfiguration.rpcEndpoints.map(\n (r) => [r.url, r.networkClientId] as const,\n ),\n );\n\n const updateNetworkConfig = newNetworkConfiguration as UpdateNetworkFields;\n\n updateNetworkConfig.rpcEndpoints = updateNetworkConfig.rpcEndpoints.map(\n (r) => {\n const originalRPCClientId = originalRPCUrlMap.get(r.url);\n\n // This is an existing RPC, so use the clients networkClientId\n if (originalRPCClientId) {\n r.networkClientId = originalRPCClientId;\n return r;\n }\n\n // This is a new RPC, so remove the remote networkClientId\n r.networkClientId = undefined;\n return r;\n },\n );\n\n return updateNetworkConfig;\n};\n\n/**\n * Will insert any missing infura RPCs, as we cannot remove infura RPC\n * Exported for testability\n * @param props - properties\n * @param props.originalNetworkConfiguration - original network configuration\n * @param props.updateNetworkConfiguration - the updated network configuration to use when dispatching `NetworkController:updateNetwork`\n * @returns mutates and returns the updateNetworkConfiguration\n */\nexport const appendMissingInfuraNetworks = (props: {\n originalNetworkConfiguration: NetworkConfiguration;\n updateNetworkConfiguration: UpdateNetworkFields;\n}) => {\n const { originalNetworkConfiguration, updateNetworkConfiguration } = props;\n\n // Ensure we have not removed any infura networks (and add them back if they were removed)\n const origInfuraRPCMap = new Map(\n originalNetworkConfiguration.rpcEndpoints\n .filter((r) => r.type === RpcEndpointType.Infura)\n .map((r) => [r.networkClientId, r] as const),\n );\n const newInfuraRPCMap = new Map(\n updateNetworkConfiguration.rpcEndpoints\n .filter((r) => r.type === RpcEndpointType.Infura && r.networkClientId)\n .map((r) => [r.networkClientId as string, r]),\n );\n const missingOrigInfuraRPCs = setDifference(\n new Set(origInfuraRPCMap.keys()),\n new Set(newInfuraRPCMap.keys()),\n );\n\n if (missingOrigInfuraRPCs.size > 0) {\n const missingRPCs: UpdateNetworkFields['rpcEndpoints'] = [];\n missingOrigInfuraRPCs.forEach((clientId) => {\n missingRPCs.push(\n origInfuraRPCMap.get(\n clientId,\n ) as UpdateNetworkFields['rpcEndpoints'][number],\n );\n });\n\n updateNetworkConfiguration.rpcEndpoints.unshift(...missingRPCs);\n }\n\n return updateNetworkConfiguration;\n};\n\n/**\n * The `NetworkController:updateNetwork` method will require us to pass in a `replacementSelectedRpcEndpointIndex` if the selected RPC is removed or modified\n * @param props - properties\n * @param props.originalNetworkConfiguration - the original network configuration\n * @param props.updateNetworkConfiguration - the new network configuration we will use to update\n * @param props.selectedNetworkClientId - the NetworkController's selected network id.\n * @returns the new RPC index if it needs modification\n */\nexport const getNewRPCIndex = (props: {\n originalNetworkConfiguration: NetworkConfiguration;\n updateNetworkConfiguration: UpdateNetworkFields;\n selectedNetworkClientId: string;\n}) => {\n const {\n originalNetworkConfiguration,\n updateNetworkConfiguration,\n selectedNetworkClientId,\n } = props;\n\n const isRPCInNewList = updateNetworkConfiguration.rpcEndpoints.some(\n (r) => r.networkClientId === selectedNetworkClientId,\n );\n const isRPCInOldList = originalNetworkConfiguration.rpcEndpoints.some(\n (r) => r.networkClientId === selectedNetworkClientId,\n );\n\n const getAnyRPCIndex = () =>\n Math.max(\n updateNetworkConfiguration.rpcEndpoints.findIndex((r) =>\n Boolean(r.networkClientId),\n ),\n 0,\n );\n\n // We have removed the selected RPC, so we must point to a new RPC index\n if (isRPCInOldList && !isRPCInNewList) {\n // Try finding an existing index, or default to first RPC.\n const newIndex = getAnyRPCIndex();\n return newIndex;\n }\n\n // We have updated the selected RPC, so we must point to the same RPC index (or a new one)\n if (isRPCInOldList && isRPCInNewList) {\n const existingIndex = updateNetworkConfiguration.rpcEndpoints.findIndex(\n (r) => r.networkClientId === selectedNetworkClientId,\n );\n /* istanbul ignore next - the `getAnyRPCIndex` should not be reachable since this is an existing network */\n return existingIndex !== -1 ? existingIndex : getAnyRPCIndex();\n }\n\n return undefined;\n};\n\n/**\n * create the correct `NetworkController:updateNetwork` parameters\n * @param props - properties\n * @param props.originalNetworkConfiguration - original config\n * @param props.newNetworkConfiguration - new config (from remote)\n * @param props.selectedNetworkClientId - the current selected network client id\n * @returns parameters to be used for `NetworkController:updateNetwork` call\n */\nexport const createUpdateNetworkProps = (props: {\n originalNetworkConfiguration: NetworkConfiguration;\n newNetworkConfiguration: NetworkConfiguration;\n selectedNetworkClientId: string;\n}) => {\n const {\n originalNetworkConfiguration,\n newNetworkConfiguration,\n selectedNetworkClientId,\n } = props;\n\n // The `NetworkController:updateNetwork` has a strict set of rules to follow\n // New RPCs that we are adding must not have a networkClientId\n // Existing RPCs must point to the correct networkClientId (so we must convert and use this client clientIds set)\n // Removing RPCs are omitted from the list\n // We cannot remove infura RPCs - so ensure that they stay populated\n // If we are removing a selected RPC - then we need to provide `replacementSelectedRpcEndpointIndex` to an index in the new list\n // If we are updating a selected RPC - then we need to provide `replacementSelectedRpcEndpointIndex` to the index in the new list\n\n const mappedNetworkConfiguration = getMappedNetworkConfiguration({\n originalNetworkConfiguration,\n newNetworkConfiguration,\n });\n\n appendMissingInfuraNetworks({\n originalNetworkConfiguration,\n updateNetworkConfiguration: mappedNetworkConfiguration,\n });\n\n const updatedRPCIndex = getNewRPCIndex({\n originalNetworkConfiguration,\n updateNetworkConfiguration: mappedNetworkConfiguration,\n selectedNetworkClientId,\n });\n\n return {\n updateNetworkFields: mappedNetworkConfiguration,\n newSelectedRpcEndpointIndex: updatedRPCIndex,\n };\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);
|
|
@@ -60,10 +61,16 @@ async function getUserStorage(opts) {
|
|
|
60
61
|
}
|
|
61
62
|
const userStorage = await userStorageResponse.json();
|
|
62
63
|
const encryptedData = userStorage?.Data ?? null;
|
|
64
|
+
/* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */
|
|
63
65
|
if (!encryptedData) {
|
|
64
66
|
return null;
|
|
65
67
|
}
|
|
66
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
|
+
}
|
|
67
74
|
return decryptedData;
|
|
68
75
|
}
|
|
69
76
|
catch (e) {
|
|
@@ -100,18 +107,32 @@ async function getUserStorageAllFeatureEntries(opts) {
|
|
|
100
107
|
return null;
|
|
101
108
|
}
|
|
102
109
|
const decryptedData = [];
|
|
110
|
+
const reEncryptedEntries = [];
|
|
103
111
|
for (const entry of userStorage) {
|
|
112
|
+
/* istanbul ignore if - unreachable if statement, but kept as edge case */
|
|
104
113
|
if (!entry.Data) {
|
|
105
114
|
continue;
|
|
106
115
|
}
|
|
107
116
|
try {
|
|
108
117
|
const data = await encryption_1.default.decryptString(entry.Data, opts.storageKey, nativeScryptCrypto);
|
|
109
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
|
+
}
|
|
110
127
|
}
|
|
111
128
|
catch {
|
|
112
129
|
// do nothing
|
|
113
130
|
}
|
|
114
131
|
}
|
|
132
|
+
// Re-upload the re-encrypted entries
|
|
133
|
+
if (reEncryptedEntries.length) {
|
|
134
|
+
await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(reEncryptedEntries, opts);
|
|
135
|
+
}
|
|
115
136
|
return decryptedData;
|
|
116
137
|
}
|
|
117
138
|
catch (e) {
|
|
@@ -178,6 +199,33 @@ async function batchUpsertUserStorage(data, opts) {
|
|
|
178
199
|
}
|
|
179
200
|
}
|
|
180
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;
|
|
181
229
|
/**
|
|
182
230
|
* User Storage Service - Delete Storage Entry.
|
|
183
231
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"services.cjs","sourceRoot":"","sources":["../../../src/controllers/user-storage/services.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wDAA2B;AAE3B,gFAAuE;AACvE,8CAAmD;AAKnD,oEAA8D;AAG9D,MAAM,QAAQ,GAAG,IAAA,gBAAU,EAAC,SAAG,CAAC,GAAG,CAAC,CAAC;AAExB,QAAA,gBAAgB,GAAW,QAAQ,CAAC,iBAAiB,CAAC;AACtD,QAAA,qBAAqB,GAAG,GAAG,wBAAgB,qBAAqB,CAAC;AAmC9E;;;;;GAKG;AACI,KAAK,UAAU,cAAc,CAClC,IAAwB;IAExB,IAAI;QACF,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;QAEnE,MAAM,aAAa,GAAG,IAAA,gCAAe,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;QAEjE,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACtD,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,2DAA2D;QAC3D,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,OAAO,IAAI,CAAC;SACb;QAED,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC/C;QAED,MAAM,WAAW,GACf,MAAM,mBAAmB,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC;QAEhD,IAAI,CAAC,aAAa,EAAE;YAClB,OAAO,IAAI,CAAC;SACb;QAED,MAAM,aAAa,GAAG,MAAM,oBAAU,CAAC,aAAa,CAClD,aAAa,EACb,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;QAEF,OAAO,aAAa,CAAC;KACtB;IAAC,OAAO,CAAC,EAAE;QACV,kBAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AA5CD,wCA4CC;AAED;;;;;GAKG;AACI,KAAK,UAAU,+BAA+B,CACnD,IAAyC;IAEzC,IAAI;QACF,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;QACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;QAExD,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACtD,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,6DAA6D;QAC7D,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,OAAO,IAAI,CAAC;SACb;QAED,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC/C;QAED,MAAM,WAAW,GACf,MAAM,mBAAmB,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YAC/B,OAAO,IAAI,CAAC;SACb;QAED,MAAM,aAAa,GAAa,EAAE,CAAC;QAEnC,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE;YAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;gBACf,SAAS;aACV;YAED,IAAI;gBACF,MAAM,IAAI,GAAG,MAAM,oBAAU,CAAC,aAAa,CACzC,KAAK,CAAC,IAAI,EACV,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;gBACF,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC1B;YAAC,MAAM;gBACN,aAAa;aACd;SACF;QAED,OAAO,aAAa,CAAC;KACtB;IAAC,OAAO,CAAC,EAAE;QACV,kBAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AAtDD,0EAsDC;AAED;;;;;GAKG;AACI,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,IAAwB;IAExB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAEnE,MAAM,aAAa,GAAG,MAAM,oBAAU,CAAC,aAAa,CAClD,IAAI,EACJ,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,gCAAe,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;IAEjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC;AA1BD,8CA0BC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,sBAAsB,CAC1C,IAAwB,EACxB,IAAmC;IAEnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChB,OAAO;KACR;IAED,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAEnE,MAAM,aAAa,GAAe,EAAE,CAAC;IAErC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE;QACpB,aAAa,CAAC,IAAI,CAAC;YACjB,IAAA,6BAAgB,EAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;YACnC,MAAM,oBAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC;SAC1E,CAAC,CAAC;KACJ;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;KAC/D;AACH,CAAC;AAnCD,wDAmCC;AAED;;;;GAIG;AACI,KAAK,UAAU,iBAAiB,CACrC,IAAwB;IAExB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,aAAa,GAAG,IAAA,gCAAe,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;IAEjE,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtD,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IAEH,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;KAC3D;IAED,IAAI,CAAC,mBAAmB,CAAC,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC;AAtBD,8CAsBC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,sBAAsB,CAC1C,IAAc,EACd,IAAmC;IAEnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChB,OAAO;KACR;IAED,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAE/C,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE;QACpB,aAAa,CAAC,IAAI,CAAC,IAAA,6BAAgB,EAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;KACtD;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,gEAAgE;QAChE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;KACtD,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;KAC/D;AACH,CAAC;AA/BD,wDA+BC;AAED;;;;GAIG;AACI,KAAK,UAAU,kCAAkC,CACtD,IAAyC;IAEzC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,6BAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtD,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IAEH,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;KACrD;IAED,IAAI,CAAC,mBAAmB,CAAC,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC;AArBD,gFAqBC","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 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 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":"
|
|
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":"
|
|
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);
|
|
@@ -38,10 +39,16 @@ export async function getUserStorage(opts) {
|
|
|
38
39
|
}
|
|
39
40
|
const userStorage = await userStorageResponse.json();
|
|
40
41
|
const encryptedData = userStorage?.Data ?? null;
|
|
42
|
+
/* istanbul ignore if - this is an edge case where our endpoint returns invalid JSON payload */
|
|
41
43
|
if (!encryptedData) {
|
|
42
44
|
return null;
|
|
43
45
|
}
|
|
44
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
|
+
}
|
|
45
52
|
return decryptedData;
|
|
46
53
|
}
|
|
47
54
|
catch (e) {
|
|
@@ -77,18 +84,32 @@ export async function getUserStorageAllFeatureEntries(opts) {
|
|
|
77
84
|
return null;
|
|
78
85
|
}
|
|
79
86
|
const decryptedData = [];
|
|
87
|
+
const reEncryptedEntries = [];
|
|
80
88
|
for (const entry of userStorage) {
|
|
89
|
+
/* istanbul ignore if - unreachable if statement, but kept as edge case */
|
|
81
90
|
if (!entry.Data) {
|
|
82
91
|
continue;
|
|
83
92
|
}
|
|
84
93
|
try {
|
|
85
94
|
const data = await encryption.decryptString(entry.Data, opts.storageKey, nativeScryptCrypto);
|
|
86
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
|
+
}
|
|
87
104
|
}
|
|
88
105
|
catch {
|
|
89
106
|
// do nothing
|
|
90
107
|
}
|
|
91
108
|
}
|
|
109
|
+
// Re-upload the re-encrypted entries
|
|
110
|
+
if (reEncryptedEntries.length) {
|
|
111
|
+
await batchUpsertUserStorageWithAlreadyHashedAndEncryptedEntries(reEncryptedEntries, opts);
|
|
112
|
+
}
|
|
92
113
|
return decryptedData;
|
|
93
114
|
}
|
|
94
115
|
catch (e) {
|
|
@@ -152,6 +173,32 @@ export async function batchUpsertUserStorage(data, opts) {
|
|
|
152
173
|
throw new Error('user-storage - unable to batch upsert data');
|
|
153
174
|
}
|
|
154
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
|
+
}
|
|
155
202
|
/**
|
|
156
203
|
* User Storage Service - Delete Storage Entry.
|
|
157
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 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 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":";;;;;;AAAA,OAAO,IAAG,iBAAiB;;AAE3B,OAAO,UAAU,EAAE,EAAE,gBAAgB,EAAE,0CAAgC;AACvE,OAAO,EAAE,WAAW,EAAE,8CAA0C;AAChE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,6BAAyB;AAKnD,OAAO,EAAE,eAAe,EAAE,wCAAoC;AAG9D,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAErC,MAAM,CAAC,MAAM,gBAAgB,GAAW,QAAQ,CAAC,iBAAiB,CAAC;AACnE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,gBAAgB,qBAAqB,CAAC;AAmC9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAwB;IAExB,IAAI;QACF,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;QAEnE,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;QAEjE,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACtD,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,2DAA2D;QAC3D,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,OAAO,IAAI,CAAC;SACb;QAED,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC/C;QAED,MAAM,WAAW,GACf,MAAM,mBAAmB,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,aAAa,GAAG,WAAW,EAAE,IAAI,IAAI,IAAI,CAAC;QAEhD,+FAA+F;QAC/F,IAAI,CAAC,aAAa,EAAE;YAClB,OAAO,IAAI,CAAC;SACb;QAED,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC,aAAa,CAClD,aAAa,EACb,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;QAEF,2DAA2D;QAC3D,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,WAAW,CAAC,QAAQ,EAAE,EAAE;YAC9C,MAAM,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;SAC9C;QAED,OAAO,aAAa,CAAC;KACtB;IAAC,OAAO,CAAC,EAAE;QACV,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,IAAyC;IAEzC,IAAI;QACF,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;QACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;QAExD,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACtD,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,WAAW,EAAE;aACvC;SACF,CAAC,CAAC;QAEH,6DAA6D;QAC7D,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,OAAO,IAAI,CAAC;SACb;QAED,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC/C;QAED,MAAM,WAAW,GACf,MAAM,mBAAmB,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;YAC/B,OAAO,IAAI,CAAC;SACb;QAED,MAAM,aAAa,GAAa,EAAE,CAAC;QACnC,MAAM,kBAAkB,GAAuB,EAAE,CAAC;QAElD,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE;YAC/B,0EAA0E;YAC1E,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;gBACf,SAAS;aACV;YAED,IAAI;gBACF,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,aAAa,CACzC,KAAK,CAAC,IAAI,EACV,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;gBACF,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAEzB,oEAAoE;gBACpE,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC5C,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,WAAW,CAAC,QAAQ,EAAE,EAAE;oBAC9C,kBAAkB,CAAC,IAAI,CAAC;wBACtB,KAAK,CAAC,SAAS;wBACf,MAAM,UAAU,CAAC,aAAa,CAC5B,IAAI,EACJ,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB;qBACF,CAAC,CAAC;iBACJ;aACF;YAAC,MAAM;gBACN,aAAa;aACd;SACF;QAED,qCAAqC;QACrC,IAAI,kBAAkB,CAAC,MAAM,EAAE;YAC7B,MAAM,0DAA0D,CAC9D,kBAAkB,EAClB,IAAI,CACL,CAAC;SACH;QAED,OAAO,aAAa,CAAC;KACtB;IAAC,OAAO,CAAC,EAAE;QACV,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAY,EACZ,IAAwB;IAExB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAEnE,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC,aAAa,CAClD,IAAI,EACJ,IAAI,CAAC,UAAU,EACf,kBAAkB,CACnB,CAAC;IACF,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;IAEjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAwB,EACxB,IAAmC;IAEnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChB,OAAO;KACR;IAED,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC;IAEnE,MAAM,aAAa,GAAe,EAAE,CAAC;IAErC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE;QACpB,aAAa,CAAC,IAAI,CAAC;YACjB,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;YACnC,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC;SAC1E,CAAC,CAAC;KACJ;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;KAC/D;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0DAA0D,CAC9E,aAAiC,EACjC,IAAmC;IAEnC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE;QACzB,OAAO;KACR;IAED,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAEnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;KAC/D;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAwB;IAExB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,aAAa,EAAE,CAAC,CAAC;IAEjE,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtD,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IAEH,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;KAC3D;IAED,IAAI,CAAC,mBAAmB,CAAC,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAc,EACd,IAAmC;IAEnC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAChB,OAAO;KACR;IAED,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAE/C,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE;QACpB,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;KACtD;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;QACD,gEAAgE;QAChE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;KACtD,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;KAC/D;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kCAAkC,CACtD,IAAyC;IAEzC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,qBAAqB,IAAI,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,mBAAmB,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtD,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IAEH,IAAI,mBAAmB,CAAC,MAAM,KAAK,GAAG,EAAE;QACtC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;KACrD;IAED,IAAI,CAAC,mBAAmB,CAAC,EAAE,EAAE;QAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;KACzD;AACH,CAAC","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) => {
|