passbolt-browser-extension 5.12.1 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/safe-chain-config.json +2 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +30 -22
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.jpmignore +0 -1
- package/CHANGELOG.md +82 -1
- package/CONTRIBUTING.md +1 -12
- package/README.md +5 -16
- package/RELEASE_NOTES.md +2 -3
- package/SECURITY.md +7 -0
- package/i18next.config.js +28 -0
- package/jest.config.json +1 -0
- package/package.json +20 -40
- package/src/all/background_page/controller/accountRecovery/reviewRequestController.test.js +1 -1
- package/src/all/background_page/{event → controller/actionLog}/findAllForActionLogController.js +1 -1
- package/src/all/background_page/{event → controller/actionLog}/findAllForActionLogController.test.js +4 -4
- package/src/all/background_page/controller/export/exportResourcesFileController.test.js +7 -2
- package/src/all/background_page/controller/folder/folderCreateController.js +3 -1
- package/src/all/background_page/controller/group/findMyGroupsController.test.js +2 -2
- package/src/all/background_page/controller/group/getOrFindGroupsController.js +61 -0
- package/src/all/background_page/controller/group/getOrFindGroupsController.test.js +69 -0
- package/src/all/background_page/controller/group/getOrFindGroupsUsersController.js +62 -0
- package/src/all/background_page/controller/group/getOrFindGroupsUsersController.test.js +69 -0
- package/src/all/background_page/controller/group/groupCreateController.js +1 -1
- package/src/all/background_page/controller/group/groupCreateController.test.js +1 -1
- package/src/all/background_page/controller/group/groupUpdateController.js +1 -1
- package/src/all/background_page/controller/group/updateAllGroupsLocalStorageController.test.js +1 -1
- package/src/all/background_page/controller/keyring/synchroniseKeyringController.js +51 -0
- package/src/all/background_page/controller/keyring/synchroniseKeyringController.test.js +49 -0
- package/src/all/background_page/controller/metadata/shareMetadataKeyPrivateController.test.js +1 -1
- package/src/all/background_page/controller/move/moveFolderController.js +0 -2
- package/src/all/background_page/controller/permission/FindAcoPermissionsForDisplayController.js +1 -1
- package/src/all/background_page/controller/permission/FindAcoPermissionsForDisplayController.test.js +2 -2
- package/src/all/background_page/controller/resource/findAllByIdsForDisplayPermissionsController.test.js +8 -3
- package/src/all/background_page/controller/resource/findAllIdsByIsSharedWithGroupController.test.js +9 -4
- package/src/all/background_page/controller/resource/resourceUpdateController.test.js +1 -2
- package/src/all/background_page/controller/resourceLocalStorage/resourceUpdateLocalStorageController.test.js +5 -2
- package/src/all/background_page/controller/share/findFoldersForShareController.js +66 -0
- package/src/all/background_page/controller/share/findFoldersForShareController.test.js +70 -0
- package/src/all/background_page/controller/share/searchUsersAndGroupsController.js +4 -4
- package/src/all/background_page/controller/share/searchUsersAndGroupsController.test.js +8 -23
- package/src/all/background_page/controller/share/shareResourcesController.test.js +2 -2
- package/src/all/background_page/controller/subscription/createSubscriptionKeyController.js +63 -0
- package/src/all/background_page/controller/subscription/createSubscriptionKeyController.test.js +56 -0
- package/src/all/background_page/controller/subscription/deleteSubscriptionKeyController.js +55 -0
- package/src/all/background_page/controller/subscription/deleteSubscriptionKeyController.test.js +50 -0
- package/src/all/background_page/controller/user/deleteUserController.test.js +1 -1
- package/src/all/background_page/controller/user/getOrFindUsersController.js +61 -0
- package/src/all/background_page/controller/user/getOrFindUsersController.test.js +69 -0
- package/src/all/background_page/error/deleteDryRunError.js +1 -1
- package/src/all/background_page/event/actionLogEvents.js +1 -1
- package/src/all/background_page/event/appEvents.js +25 -0
- package/src/all/background_page/event/groupEvents.js +26 -0
- package/src/all/background_page/event/keyringEvents.js +12 -0
- package/src/all/background_page/event/shareEvents.js +3 -9
- package/src/all/background_page/event/userEvents.js +13 -0
- package/src/all/background_page/model/config.js +12 -2
- package/src/all/background_page/model/entity/folder/folderEntity.js +2 -2
- package/src/all/background_page/model/entity/folder/folderEntity.test.js +2 -2
- package/src/all/background_page/model/entity/folder/foldersCollection.test.js +1 -1
- package/src/all/background_page/model/entity/group/update/groupUpdateEntity.js +1 -1
- package/src/all/background_page/model/entity/group/update/groupUpdateEntity.test.js +1 -1
- package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionEntity.js +2 -2
- package/src/all/background_page/model/entity/permission/actionLog/updatedPermissionEntity.test.data.js +1 -1
- package/src/all/background_page/model/entity/permission/change/permissionChangeEntity.js +1 -1
- package/src/all/background_page/model/entity/permission/change/permissionChangesCollection.js +2 -2
- package/src/all/background_page/model/entity/permission/change/permissionChangesCollection.test.js +2 -2
- package/src/all/background_page/model/entity/resource/resourceEntity.js +2 -2
- package/src/all/background_page/model/entity/resource/resourceEntity.test.js +1 -1
- package/src/all/background_page/model/entity/user/userEntity.js +16 -377
- package/src/all/background_page/model/entity/user/userEntity.test.js +22 -297
- package/src/all/background_page/model/entity/userAndGroupSearchResultEntity/userAndGroupSearchResultEntity.js +1 -1
- package/src/all/background_page/model/folder/folderModel.js +5 -154
- package/src/all/background_page/model/group/groupModel.js +1 -1
- package/src/all/background_page/model/keyring.js +52 -17
- package/src/all/background_page/model/keyring.test.js +110 -0
- package/src/all/background_page/model/resource/resourceModel.js +2 -55
- package/src/all/background_page/model/setup/setupModel.js +2 -2
- package/src/all/background_page/model/user/userModel.js +18 -15
- package/src/all/background_page/model/user/userModel.test.js +1 -3
- package/src/all/background_page/service/api/abstract/abstractService.js +3 -17
- package/src/all/background_page/service/api/edition/passboltEditionApiService.js +64 -0
- package/src/all/background_page/service/api/edition/passboltEditionApiService.test.js +99 -0
- package/src/all/background_page/service/api/group/groupApiService.js +22 -23
- package/src/all/background_page/service/api/group/groupApiService.test.js +70 -0
- package/src/all/background_page/service/api/resource/resourceService.js +18 -12
- package/src/all/background_page/service/api/share/{shareService.js → shareApiService.js} +10 -7
- package/src/all/background_page/service/api/share/{shareService.test.js → shareApiService.test.js} +5 -5
- package/src/all/background_page/service/group/createGroupService.js +1 -1
- package/src/all/background_page/service/group/createGroupService.test.js +1 -1
- package/src/all/background_page/service/group/findAndUpdateGroupsLocalStorageService.js +56 -1
- package/src/all/background_page/service/group/findAndUpdateGroupsLocalStorageService.test.js +84 -2
- package/src/all/background_page/service/group/findGroupsService.js +5 -9
- package/src/all/background_page/service/group/findGroupsService.test.data.js +1 -1
- package/src/all/background_page/service/group/findGroupsService.test.js +10 -15
- package/src/all/background_page/service/group/getOrFindGroupsService.js +65 -0
- package/src/all/background_page/service/group/getOrFindGroupsService.test.js +168 -0
- package/src/all/background_page/service/group/getOrFindGroupsUsersService.js +51 -0
- package/src/all/background_page/service/group/getOrFindGroupsUsersService.test.js +94 -0
- package/src/all/background_page/service/group/groupUpdateService.js +5 -3
- package/src/all/background_page/service/group/groupUpdateService.test.js +10 -2
- package/src/all/background_page/service/local_storage/groupLocalStorage.js +2 -2
- package/src/all/background_page/service/local_storage/groupLocalStorage.test.js +3 -3
- package/src/all/background_page/service/local_storage/userLocalStorage.js +57 -36
- package/src/all/background_page/service/local_storage/userLocalStorage.test.js +282 -0
- package/src/all/background_page/service/metadata/createMetadataKeyService.test.js +1 -1
- package/src/all/background_page/service/metadata/saveMetadataSettingsService.test.js +1 -1
- package/src/all/background_page/service/metadata/shareMetadataKeyPrivateService.test.js +1 -1
- package/src/all/background_page/service/migrateMetadata/migrateMetadataResourcesService.js +1 -1
- package/src/all/background_page/service/move/calculatePermissionsChangesForMoveService.js +113 -0
- package/src/all/background_page/service/move/calculatePermissionsChangesForMoveService.test.data.js +38 -0
- package/src/all/background_page/service/move/calculatePermissionsChangesForMoveService.test.js +158 -0
- package/src/all/background_page/service/move/moveOneFolderService.js +6 -7
- package/src/all/background_page/service/move/moveOneFolderService.test.js +90 -90
- package/src/all/background_page/service/move/moveResourcesService.js +2 -5
- package/src/all/background_page/service/permission/findPermissionsService.js +1 -1
- package/src/all/background_page/service/resource/create/resourceCreateService.js +13 -31
- package/src/all/background_page/service/resource/create/resourceCreateService.test.js +25 -18
- package/src/all/background_page/service/resource/export/exportResourcesService.test.js +13 -4
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.test.js +35 -28
- package/src/all/background_page/service/resource/findResourcesService.js +78 -2
- package/src/all/background_page/service/resource/findResourcesService.test.data.js +1 -1
- package/src/all/background_page/service/resource/findResourcesService.test.js +90 -31
- package/src/all/background_page/service/resource/getOrFindResourcesService.test.js +18 -8
- package/src/all/background_page/service/session_storage/keepSessionAliveService.js +3 -3
- package/src/all/background_page/service/session_storage/keepSessionAliveService.test.js +5 -3
- package/src/all/background_page/service/share/searchUsersAndGroupsService.js +41 -0
- package/src/all/background_page/service/share/searchUsersAndGroupsService.test.js +64 -0
- package/src/all/background_page/service/share/shareFoldersService.js +3 -3
- package/src/all/background_page/service/share/shareFoldersService.test.js +3 -3
- package/src/all/background_page/service/share/shareResourceService.js +4 -4
- package/src/all/background_page/service/share/shareResourceService.test.js +8 -8
- package/src/all/background_page/service/subscription/createSubscriptionKeyService.js +57 -0
- package/src/all/background_page/service/subscription/createSubscriptionKeyService.test.js +111 -0
- package/src/all/background_page/service/subscription/deleteSubscriptionKeyService.js +35 -0
- package/src/all/background_page/service/subscription/deleteSubscriptionKeyService.test.js +55 -0
- package/src/all/background_page/service/user/deleteUserService.js +4 -4
- package/src/all/background_page/service/user/deleteUserService.test.js +10 -10
- package/src/all/background_page/service/user/findAndUpdateUsersLocalStorageService.js +81 -0
- package/src/all/background_page/service/user/findAndUpdateUsersLocalStorageService.test.js +132 -0
- package/src/all/background_page/service/user/findUsersService.js +6 -6
- package/src/all/background_page/service/user/findUsersService.test.js +39 -38
- package/src/all/background_page/service/user/getOrFindUsersService.js +60 -0
- package/src/all/background_page/service/user/getOrFindUsersService.test.js +110 -0
- package/src/all/background_page/utils/assertions.js +1 -0
- package/src/all/locales/ko-KR/common.json +1 -1
- package/src/chrome/manifest.json +1 -1
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/firefox/manifest.json +3 -3
- package/src/safari/manifest.json +2 -2
- package/test/jest.env-setup.js +31 -0
- package/webpack/applyOutputClean.js +69 -0
- package/webpack/base.config.js +33 -0
- package/webpack/common-blocks.js +91 -0
- package/webpack/expectedBuildArtifacts.js +51 -0
- package/webpack/i18nextExtractionPlugin.js +43 -0
- package/webpack/passboltEnvPlugin.js +41 -0
- package/webpack/webExtPlugin/index.js +75 -0
- package/webpack.chromium-mv2.config.js +40 -0
- package/webpack.chromium-mv3.config.js +40 -0
- package/webpack.common.config.js +186 -0
- package/webpack.config.js +38 -0
- package/webpack.firefox.config.js +40 -0
- package/webpack.mv2.config.js +65 -0
- package/webpack.mv3.config.js +99 -0
- package/webpack.safari-background-page.config.js +66 -57
- package/webpack.safari.config.js +44 -0
- package/Gruntfile.js +0 -471
- package/am_i_compromised.py +0 -1036
- package/am_i_compromised.sh +0 -688
- package/i18next-parser.config.js +0 -22
- package/src/all/background_page/config/config.json +0 -7
- package/src/all/background_page/config/config.json.debug +0 -7
- package/src/all/background_page/config/config.json.default +0 -7
- package/src/all/background_page/model/entity/group/groupEntity.js +0 -241
- package/src/all/background_page/model/entity/group/groupEntity.test.js +0 -136
- package/src/all/background_page/model/entity/group/groupsCollection.js +0 -166
- package/src/all/background_page/model/entity/group/groupsCollection.test.data.js +0 -34
- package/src/all/background_page/model/entity/group/groupsCollection.test.js +0 -227
- package/src/all/background_page/model/entity/permission/permissionEntity.js +0 -485
- package/src/all/background_page/model/entity/permission/permissionEntity.test.js +0 -263
- package/src/all/background_page/model/entity/permission/permissionsCollection.js +0 -486
- package/src/all/background_page/model/entity/permission/permissionsCollection.test.js +0 -700
- package/src/all/background_page/model/entity/user/usersCollection.js +0 -147
- package/src/all/background_page/model/entity/user/usersCollection.test.js +0 -223
- package/src/all/background_page/model/share/shareModel.js +0 -183
- package/src/all/background_page/model/share/shareModel.test.js +0 -61
- package/src/all/background_page/service/api/user/userService.js +0 -260
- package/src/all/background_page/service/resource/create/resourceCreateService.test.data.js +0 -55
- package/webpack-content-scripts.browser-integration.config.js +0 -57
- package/webpack-content-scripts.config.js +0 -61
- package/webpack-content-scripts.public-website-sign-in.config.js +0 -57
- package/webpack-data.config.js +0 -102
- package/webpack-data.download.config.js +0 -59
- package/webpack-data.in-form-call-to-action.config.js +0 -97
- package/webpack-data.in-form-menu.config.js +0 -97
- package/webpack-offscreens.config.js +0 -55
- package/webpack.background-page.config.js +0 -62
- package/webpack.service-worker.config.js +0 -65
|
@@ -32,6 +32,8 @@ const PRIVATE = "PRIVATE";
|
|
|
32
32
|
const MY_KEY_ID = "MY_KEY_ID";
|
|
33
33
|
const STORAGE_KEY_PUBLIC = "passbolt-public-gpgkeys";
|
|
34
34
|
const STORAGE_KEY_PRIVATE = "passbolt-private-gpgkeys";
|
|
35
|
+
// Number of public keys parsed concurrently during a keyring sync, to bound peak memory.
|
|
36
|
+
const SYNC_CHUNK_SIZE = 500;
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* The class that deals with Passbolt Keyring.
|
|
@@ -88,6 +90,31 @@ class Keyring {
|
|
|
88
90
|
* if the user id is not valid
|
|
89
91
|
*/
|
|
90
92
|
async importPublic(armoredPublicKey, userId) {
|
|
93
|
+
const keyInfo = await this.buildPublicKeyInfo(armoredPublicKey, userId);
|
|
94
|
+
|
|
95
|
+
// Add the key in the keyring.
|
|
96
|
+
const publicKeys = this.getPublicKeysFromStorage();
|
|
97
|
+
publicKeys[userId] = keyInfo;
|
|
98
|
+
this.store(Keyring.PUBLIC, publicKeys);
|
|
99
|
+
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validate, parse and read the metadata of a public armored key, without persisting it.
|
|
105
|
+
* Used by importPublic (single key) and sync (batch), so the keyring is stored only once
|
|
106
|
+
* instead of once per key.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} armoredPublicKey The key to read
|
|
109
|
+
* @param {string} userId The owner of the key
|
|
110
|
+
* @returns {Promise<object>} The key info DTO, with its user_id set.
|
|
111
|
+
* @throw Error
|
|
112
|
+
* if the key cannot be read by openpgp
|
|
113
|
+
* if the key is not public
|
|
114
|
+
* if the user id is not valid
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
async buildPublicKeyInfo(armoredPublicKey, userId) {
|
|
91
118
|
// Check user id
|
|
92
119
|
if (typeof userId === "undefined") {
|
|
93
120
|
throw new Error("The user id is undefined");
|
|
@@ -107,14 +134,9 @@ class Keyring {
|
|
|
107
134
|
|
|
108
135
|
// Get the keyInfo.
|
|
109
136
|
const keyInfo = (await GetGpgKeyInfoService.getKeyInfo(primaryPublicKey)).toDto();
|
|
137
|
+
keyInfo.user_id = userId;
|
|
110
138
|
|
|
111
|
-
|
|
112
|
-
const publicKeys = this.getPublicKeysFromStorage();
|
|
113
|
-
publicKeys[userId] = keyInfo;
|
|
114
|
-
publicKeys[userId].user_id = userId;
|
|
115
|
-
this.store(Keyring.PUBLIC, publicKeys);
|
|
116
|
-
|
|
117
|
-
return true;
|
|
139
|
+
return keyInfo;
|
|
118
140
|
}
|
|
119
141
|
|
|
120
142
|
/**
|
|
@@ -259,20 +281,25 @@ class Keyring {
|
|
|
259
281
|
throw new Error("Could not synchronize the keyring. The server response body is missing.");
|
|
260
282
|
}
|
|
261
283
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
284
|
+
/*
|
|
285
|
+
* Build the keyring in memory and persist it once, rather than reading and re-serializing
|
|
286
|
+
* the whole keyring for every key (which is quadratic and exhausts memory on large orgs).
|
|
287
|
+
* Keys are read in bounded-size batches so we never parse the entire payload at once.
|
|
288
|
+
*/
|
|
289
|
+
const publicKeys = this.getPublicKeysFromStorage();
|
|
290
|
+
const metas = Object.values(json.body).filter((meta) => meta?.armored_key && meta?.user_id);
|
|
291
|
+
for (let i = 0; i < metas.length; i += Keyring.SYNC_CHUNK_SIZE) {
|
|
292
|
+
const chunk = metas.slice(i, i + Keyring.SYNC_CHUNK_SIZE);
|
|
293
|
+
const keysInfo = await Promise.all(chunk.map((meta) => this.buildPublicKeyInfo(meta.armored_key, meta.user_id)));
|
|
294
|
+
keysInfo.forEach((keyInfo) => {
|
|
295
|
+
publicKeys[keyInfo.user_id] = keyInfo;
|
|
296
|
+
});
|
|
270
297
|
}
|
|
271
|
-
|
|
298
|
+
this.store(Keyring.PUBLIC, publicKeys);
|
|
272
299
|
|
|
273
300
|
storage.setItem("latestSync", json.header.servertime);
|
|
274
301
|
|
|
275
|
-
return
|
|
302
|
+
return metas.length;
|
|
276
303
|
}
|
|
277
304
|
|
|
278
305
|
/*
|
|
@@ -402,6 +429,14 @@ class Keyring {
|
|
|
402
429
|
return PUBLIC;
|
|
403
430
|
}
|
|
404
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Keyring.SYNC_CHUNK_SIZE
|
|
434
|
+
* @returns {number}
|
|
435
|
+
*/
|
|
436
|
+
static get SYNC_CHUNK_SIZE() {
|
|
437
|
+
return SYNC_CHUNK_SIZE;
|
|
438
|
+
}
|
|
439
|
+
|
|
405
440
|
/**
|
|
406
441
|
* Keyring.PRIVATE
|
|
407
442
|
* @returns {string}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.13.0
|
|
13
|
+
*/
|
|
14
|
+
import { enableFetchMocks } from "jest-fetch-mock";
|
|
15
|
+
import { v4 as uuidv4 } from "uuid";
|
|
16
|
+
import { pgpKeys } from "passbolt-styleguide/test/fixture/pgpKeys/keys";
|
|
17
|
+
import Keyring from "./keyring";
|
|
18
|
+
import UserSettings from "./userSettings/userSettings";
|
|
19
|
+
|
|
20
|
+
beforeAll(() => {
|
|
21
|
+
enableFetchMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
jest.restoreAllMocks();
|
|
26
|
+
fetch.resetMocks();
|
|
27
|
+
jest.spyOn(UserSettings.prototype, "getDomain").mockImplementation(() => "https://passbolt.dev");
|
|
28
|
+
new Keyring().flush(Keyring.PUBLIC);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const syncResponse = (body) => JSON.stringify({ header: { servertime: "2026-06-02T00:00:00+00:00" }, body });
|
|
32
|
+
|
|
33
|
+
describe("Keyring", () => {
|
|
34
|
+
describe("::sync", () => {
|
|
35
|
+
it("should store every synced public key, tagged with its user id", async () => {
|
|
36
|
+
expect.assertions(5);
|
|
37
|
+
const keyring = new Keyring();
|
|
38
|
+
const adaId = uuidv4();
|
|
39
|
+
const bettyId = uuidv4();
|
|
40
|
+
fetch.mockResponseOnce(
|
|
41
|
+
syncResponse([
|
|
42
|
+
{ user_id: adaId, armored_key: pgpKeys.ada.public },
|
|
43
|
+
{ user_id: bettyId, armored_key: pgpKeys.betty.public },
|
|
44
|
+
]),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const count = await keyring.sync();
|
|
48
|
+
|
|
49
|
+
const publicKeys = keyring.getPublicKeysFromStorage();
|
|
50
|
+
expect(count).toEqual(2);
|
|
51
|
+
expect(publicKeys[adaId]).toBeDefined();
|
|
52
|
+
expect(publicKeys[adaId].user_id).toEqual(adaId);
|
|
53
|
+
expect(publicKeys[bettyId]).toBeDefined();
|
|
54
|
+
expect(publicKeys[bettyId].user_id).toEqual(bettyId);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should persist the keyring only once, not once per key (O(n^2) regression guard)", async () => {
|
|
58
|
+
expect.assertions(2);
|
|
59
|
+
const keyring = new Keyring();
|
|
60
|
+
// Reuse one armored key across many distinct users — we only assert the persistence strategy.
|
|
61
|
+
const body = Array.from({ length: 50 }, () => ({ user_id: uuidv4(), armored_key: pgpKeys.ada.public }));
|
|
62
|
+
fetch.mockResponseOnce(syncResponse(body));
|
|
63
|
+
const storeSpy = jest.spyOn(Keyring.prototype, "store");
|
|
64
|
+
|
|
65
|
+
await keyring.sync();
|
|
66
|
+
|
|
67
|
+
expect(Object.keys(keyring.getPublicKeysFromStorage())).toHaveLength(50);
|
|
68
|
+
// The bug stored the whole keyring once per key (50 times); the fix stores once.
|
|
69
|
+
expect(storeSpy).toHaveBeenCalledTimes(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should ignore body entries missing an armored key or user id", async () => {
|
|
73
|
+
expect.assertions(2);
|
|
74
|
+
const keyring = new Keyring();
|
|
75
|
+
const adaId = uuidv4();
|
|
76
|
+
fetch.mockResponseOnce(
|
|
77
|
+
syncResponse([
|
|
78
|
+
{ user_id: adaId, armored_key: pgpKeys.ada.public },
|
|
79
|
+
{ user_id: uuidv4() }, // missing armored_key
|
|
80
|
+
{ armored_key: pgpKeys.betty.public }, // missing user_id
|
|
81
|
+
]),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const count = await keyring.sync();
|
|
85
|
+
|
|
86
|
+
expect(count).toEqual(1);
|
|
87
|
+
expect(keyring.getPublicKeysFromStorage()[adaId]).toBeDefined();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("::importPublic", () => {
|
|
92
|
+
it("should still import and store a single public key", async () => {
|
|
93
|
+
expect.assertions(2);
|
|
94
|
+
const keyring = new Keyring();
|
|
95
|
+
const adaId = uuidv4();
|
|
96
|
+
const storeSpy = jest.spyOn(Keyring.prototype, "store");
|
|
97
|
+
|
|
98
|
+
await keyring.importPublic(pgpKeys.ada.public, adaId);
|
|
99
|
+
|
|
100
|
+
expect(keyring.getPublicKeysFromStorage()[adaId].user_id).toEqual(adaId);
|
|
101
|
+
expect(storeSpy).toHaveBeenCalledTimes(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should reject an invalid user id", async () => {
|
|
105
|
+
expect.assertions(1);
|
|
106
|
+
const keyring = new Keyring();
|
|
107
|
+
await expect(keyring.importPublic(pgpKeys.ada.public, "not-a-uuid")).rejects.toThrow("The user id is not valid");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
import ResourceLocalStorage from "../../service/local_storage/resourceLocalStorage";
|
|
14
14
|
import ResourceTypeModel from "../../model/resourceType/resourceTypeModel";
|
|
15
15
|
import ResourcesCollection from "../entity/resource/resourcesCollection";
|
|
16
|
-
import PermissionEntity from "
|
|
17
|
-
import PermissionsCollection from "
|
|
16
|
+
import PermissionEntity from "passbolt-styleguide/src/shared/models/entity/permission/permissionEntity";
|
|
17
|
+
import PermissionsCollection from "passbolt-styleguide/src/shared/models/entity/permission/permissionsCollection";
|
|
18
18
|
import ResourceEntity from "../entity/resource/resourceEntity";
|
|
19
19
|
import PermissionChangesCollection from "../entity/permission/change/permissionChangesCollection";
|
|
20
20
|
import ResourceService from "../../service/api/resource/resourceService";
|
|
@@ -88,59 +88,6 @@ class ResourceModel {
|
|
|
88
88
|
* Permission changes
|
|
89
89
|
* ==============================================================
|
|
90
90
|
*/
|
|
91
|
-
/**
|
|
92
|
-
* Calculate permission changes for a move
|
|
93
|
-
* From current permissions, remove the parent folder permissions, add the destination permissions
|
|
94
|
-
* From this new set of permission and the original permission calculate the needed changed
|
|
95
|
-
*
|
|
96
|
-
* NOTE: This function requires permissions to be set for all objects
|
|
97
|
-
*
|
|
98
|
-
* @param {ResourceEntity} resource
|
|
99
|
-
* @param {(FolderEntity|null)} parentFolder
|
|
100
|
-
* @param {(FolderEntity|null)} destFolder
|
|
101
|
-
* @returns {PermissionChangesCollection}
|
|
102
|
-
*/
|
|
103
|
-
calculatePermissionsChangesForMove(resource, parentFolder, destFolder) {
|
|
104
|
-
let remainingPermissions = new PermissionsCollection([], { assertAtLeastOneOwner: false });
|
|
105
|
-
|
|
106
|
-
// Remove permissions from parent if any
|
|
107
|
-
if (parentFolder !== null) {
|
|
108
|
-
if (!resource.permissions || !parentFolder.permissions) {
|
|
109
|
-
throw new TypeError("Resource model calculatePermissionsChangesForMove requires permissions to be set.");
|
|
110
|
-
}
|
|
111
|
-
remainingPermissions = PermissionsCollection.diff(resource.permissions, parentFolder.permissions, false);
|
|
112
|
-
}
|
|
113
|
-
// Add parent permissions
|
|
114
|
-
let permissionsFromParent = new PermissionsCollection([], { assertAtLeastOneOwner: false });
|
|
115
|
-
if (destFolder) {
|
|
116
|
-
if (!destFolder.permissions) {
|
|
117
|
-
throw new TypeError(
|
|
118
|
-
"Resource model calculatePermissionsChangesForMove requires destination permissions to be set.",
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
permissionsFromParent = destFolder.permissions.cloneForAco(PermissionEntity.ACO_RESOURCE, resource.id, false);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const newPermissions = PermissionsCollection.sum(remainingPermissions, permissionsFromParent, false);
|
|
125
|
-
if (!destFolder) {
|
|
126
|
-
/*
|
|
127
|
-
* If the move is toward the root
|
|
128
|
-
* Reuse highest permission
|
|
129
|
-
*/
|
|
130
|
-
newPermissions.addOrReplace(
|
|
131
|
-
new PermissionEntity({
|
|
132
|
-
aco: PermissionEntity.ACO_RESOURCE,
|
|
133
|
-
aro: resource.permission.aro,
|
|
134
|
-
aco_foreign_key: resource.id,
|
|
135
|
-
aro_foreign_key: resource.permission.aroForeignKey,
|
|
136
|
-
type: PermissionEntity.PERMISSION_OWNER,
|
|
137
|
-
}),
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
newPermissions.assertAtLeastOneOwner();
|
|
141
|
-
return PermissionChangesCollection.calculateChanges(resource.permissions, newPermissions);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
91
|
/**
|
|
145
92
|
* Calculate permission changes for a create
|
|
146
93
|
* From current permissions add the destination permissions
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
12
|
*/
|
|
13
13
|
import AccountRecoveryOrganizationPolicyEntity from "../entity/accountRecovery/accountRecoveryOrganizationPolicyEntity";
|
|
14
|
-
import
|
|
14
|
+
import UserApiService from "passbolt-styleguide/src/shared/services/api/user/userApiService";
|
|
15
15
|
import SetupService from "../../service/api/setup/setupService";
|
|
16
16
|
import UserEntity from "../entity/user/userEntity";
|
|
17
17
|
import Validator from "validator";
|
|
@@ -26,7 +26,7 @@ class SetupModel {
|
|
|
26
26
|
*/
|
|
27
27
|
constructor(apiClientOptions) {
|
|
28
28
|
this.setupService = new SetupService(apiClientOptions);
|
|
29
|
-
this.userService = new
|
|
29
|
+
this.userService = new UserApiService(apiClientOptions);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
* @since 3.0.0
|
|
13
13
|
*/
|
|
14
14
|
import UserLocalStorage from "../../service/local_storage/userLocalStorage";
|
|
15
|
-
import
|
|
15
|
+
import UserApiService from "passbolt-styleguide/src/shared/services/api/user/userApiService";
|
|
16
16
|
import UserEntity from "../entity/user/userEntity";
|
|
17
|
-
import UsersCollection from "
|
|
17
|
+
import UsersCollection from "passbolt-styleguide/src/shared/models/entity/user/usersCollection";
|
|
18
18
|
import Validator from "validator";
|
|
19
19
|
import RoleEntity from "passbolt-styleguide/src/shared/models/entity/role/roleEntity";
|
|
20
20
|
import UserMeSessionStorageService from "../../service/sessionStorage/userMeSessionStorageService";
|
|
@@ -32,7 +32,7 @@ class UserModel {
|
|
|
32
32
|
* @public
|
|
33
33
|
*/
|
|
34
34
|
constructor(apiClientOptions, account = null) {
|
|
35
|
-
this.
|
|
35
|
+
this.userApiService = new UserApiService(apiClientOptions);
|
|
36
36
|
this.organisationSettingsModel = new OrganizationSettingsModel(apiClientOptions);
|
|
37
37
|
this.account = account;
|
|
38
38
|
}
|
|
@@ -62,7 +62,7 @@ class UserModel {
|
|
|
62
62
|
contains.missing_metadata_key_ids = true;
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
const usersCollection = await this.findAll(contains, null,
|
|
65
|
+
const usersCollection = await this.findAll(contains, null, true);
|
|
66
66
|
await UserLocalStorage.set(usersCollection);
|
|
67
67
|
return usersCollection;
|
|
68
68
|
}
|
|
@@ -75,7 +75,7 @@ class UserModel {
|
|
|
75
75
|
* @public
|
|
76
76
|
*/
|
|
77
77
|
async resendInvite(username) {
|
|
78
|
-
return this.
|
|
78
|
+
return this.userApiService.resendInvite(username);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -128,7 +128,7 @@ class UserModel {
|
|
|
128
128
|
* @returns {Promise<UserEntity>}
|
|
129
129
|
*/
|
|
130
130
|
async findOne(userId, contains, ignoreInvalidEntity) {
|
|
131
|
-
const userDto = await this.
|
|
131
|
+
const userDto = await this.userApiService.get(userId, contains);
|
|
132
132
|
return new UserEntity(userDto, { ignoreInvalidEntity: ignoreInvalidEntity });
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -137,12 +137,11 @@ class UserModel {
|
|
|
137
137
|
*
|
|
138
138
|
* @param {Object} [contains] optional example: {groups_users: true}
|
|
139
139
|
* @param {Object} [filters] optional
|
|
140
|
-
* @param {Object} [orders] optional
|
|
141
140
|
* @param {boolean?} [ignoreInvalidEntity] Should invalid entities be ignored.
|
|
142
141
|
* @returns {Promise<UsersCollection>}
|
|
143
142
|
*/
|
|
144
|
-
async findAll(contains, filters,
|
|
145
|
-
const usersDto = await this.
|
|
143
|
+
async findAll(contains, filters, ignoreInvalidEntity) {
|
|
144
|
+
const usersDto = (await this.userApiService.findAll(contains, filters)).body ?? [];
|
|
146
145
|
return new UsersCollection(usersDto, { clone: false, ignoreInvalidEntity: ignoreInvalidEntity });
|
|
147
146
|
}
|
|
148
147
|
|
|
@@ -157,9 +156,9 @@ class UserModel {
|
|
|
157
156
|
if (!Validator.isUUID(userId)) {
|
|
158
157
|
throw new TypeError("Error in find all users for users updates. The user id is not a valid uuid.");
|
|
159
158
|
}
|
|
160
|
-
const usersDto = await this.
|
|
159
|
+
const usersDto = (await this.userApiService.findAll(null, { "has-access": userId })).body ?? [];
|
|
161
160
|
const usersCollection = new UsersCollection(usersDto);
|
|
162
|
-
return usersCollection.
|
|
161
|
+
return usersCollection.extract("id");
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
/*
|
|
@@ -176,7 +175,7 @@ class UserModel {
|
|
|
176
175
|
*/
|
|
177
176
|
async create(userEntity) {
|
|
178
177
|
const data = userEntity.toDto({ profile: { avatar: false } });
|
|
179
|
-
const userDto = await this.
|
|
178
|
+
const userDto = await this.userApiService.create(data);
|
|
180
179
|
const newUserEntity = new UserEntity(userDto);
|
|
181
180
|
await UserLocalStorage.addUser(newUserEntity);
|
|
182
181
|
return newUserEntity;
|
|
@@ -192,7 +191,7 @@ class UserModel {
|
|
|
192
191
|
*/
|
|
193
192
|
async update(userEntity, ignoreInvalidEntity) {
|
|
194
193
|
const data = userEntity.toDto({ profile: { avatar: false } });
|
|
195
|
-
const userDto = await this.
|
|
194
|
+
const userDto = await this.userApiService.update(userEntity.id, data);
|
|
196
195
|
const updatedUserEntity = new UserEntity(userDto, { ignoreInvalidEntity });
|
|
197
196
|
await UserLocalStorage.updateUser(updatedUserEntity);
|
|
198
197
|
return updatedUserEntity;
|
|
@@ -208,7 +207,11 @@ class UserModel {
|
|
|
208
207
|
* @public
|
|
209
208
|
*/
|
|
210
209
|
async updateAvatar(userId, avatarUpdateEntity, ignoreInvalidEntity) {
|
|
211
|
-
const userDto = await this.
|
|
210
|
+
const userDto = await this.userApiService.updateAvatar(
|
|
211
|
+
userId,
|
|
212
|
+
avatarUpdateEntity.file,
|
|
213
|
+
avatarUpdateEntity.filename,
|
|
214
|
+
);
|
|
212
215
|
return new UserEntity(userDto, { ignoreInvalidEntity });
|
|
213
216
|
}
|
|
214
217
|
|
|
@@ -222,7 +225,7 @@ class UserModel {
|
|
|
222
225
|
username: account.username,
|
|
223
226
|
case: "lost-passphrase",
|
|
224
227
|
};
|
|
225
|
-
await this.
|
|
228
|
+
await this.userApiService.requestHelpCredentialsLost(requestHelpDto);
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
|
|
@@ -169,7 +169,6 @@ describe("UserModel", () => {
|
|
|
169
169
|
profile: true,
|
|
170
170
|
},
|
|
171
171
|
null,
|
|
172
|
-
null,
|
|
173
172
|
true,
|
|
174
173
|
);
|
|
175
174
|
});
|
|
@@ -218,7 +217,6 @@ describe("UserModel", () => {
|
|
|
218
217
|
profile: true,
|
|
219
218
|
},
|
|
220
219
|
null,
|
|
221
|
-
null,
|
|
222
220
|
true,
|
|
223
221
|
);
|
|
224
222
|
});
|
|
@@ -431,7 +429,7 @@ describe("UserModel", () => {
|
|
|
431
429
|
|
|
432
430
|
const apiClientOption = defaultApiClientOptions();
|
|
433
431
|
const model = new UserModel(apiClientOption);
|
|
434
|
-
const collection = await model.findAll({}, {},
|
|
432
|
+
const collection = await model.findAll({}, {}, true);
|
|
435
433
|
|
|
436
434
|
expect(collection).toHaveLength(3);
|
|
437
435
|
expect(collection.items[0]._props.id).toEqual(dto1.id);
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
import { ApiClient } from "passbolt-styleguide/src/shared/lib/apiClient/apiClient";
|
|
14
14
|
import Validator from "validator";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @deprecated: to be replaced with AbstractService from the styleguide
|
|
18
|
+
*/
|
|
16
19
|
class AbstractService {
|
|
17
20
|
/**
|
|
18
21
|
*
|
|
@@ -75,23 +78,6 @@ class AbstractService {
|
|
|
75
78
|
return result;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
/**
|
|
79
|
-
* Format contain orders
|
|
80
|
-
*
|
|
81
|
-
* @param {object} orders example: {"orders": ['Resources.name ASC']}
|
|
82
|
-
* @param {array} supportedOrders example: ['Resources.name ASC', 'Resources.name DESC']
|
|
83
|
-
* @returns {object} to be used in API request
|
|
84
|
-
*/
|
|
85
|
-
formatOrderOptions(orders, supportedOrders) {
|
|
86
|
-
const result = {};
|
|
87
|
-
for (const order in orders) {
|
|
88
|
-
if (supportedOrders.includes(order)) {
|
|
89
|
-
result[`order[]`] = order;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
81
|
/**
|
|
96
82
|
* Assert that an id is a valid uuid or throw a TypeError
|
|
97
83
|
*
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.13.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import AbstractService from "../abstract/abstractService";
|
|
16
|
+
|
|
17
|
+
export const PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME = "edition/subscription/key";
|
|
18
|
+
|
|
19
|
+
class PassboltEditionApiService extends AbstractService {
|
|
20
|
+
/**
|
|
21
|
+
* Constructor
|
|
22
|
+
*
|
|
23
|
+
* @param {ApiClientOptions} apiClientOptions
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
constructor(apiClientOptions) {
|
|
27
|
+
super(apiClientOptions, PassboltEditionApiService.RESOURCE_NAME);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* API Resource Name
|
|
32
|
+
*
|
|
33
|
+
* @returns {string}
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
static get RESOURCE_NAME() {
|
|
37
|
+
return PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create the subscription on the API (upgrade CE to PRO).
|
|
42
|
+
*
|
|
43
|
+
* @param {Object} keyDto the new subscription key dto
|
|
44
|
+
* @throws {Error} if API call fails, service unreachable, etc.
|
|
45
|
+
* @returns {Promise<Object>} subscriptionDto
|
|
46
|
+
*/
|
|
47
|
+
async create(keyDto) {
|
|
48
|
+
const response = await this.apiClient.create(keyDto);
|
|
49
|
+
return response.body;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Delete the subscription on the API (downgrade PRO to CE).
|
|
54
|
+
*
|
|
55
|
+
* @throws {Error} if API call fails, service unreachable, etc.
|
|
56
|
+
* @returns {Promise<void>}
|
|
57
|
+
*/
|
|
58
|
+
async delete() {
|
|
59
|
+
const url = this.apiClient.buildUrl(this.apiClient.baseUrl.toString());
|
|
60
|
+
await this.apiClient.fetchAndHandleResponse("DELETE", url);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default PassboltEditionApiService;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.13.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { enableFetchMocks } from "jest-fetch-mock";
|
|
16
|
+
|
|
17
|
+
import PassboltBadResponseError from "passbolt-styleguide/src/shared/lib/Error/PassboltBadResponseError";
|
|
18
|
+
import SubscriptionEntity from "passbolt-styleguide/src/shared/models/entity/subscription/subscriptionEntity";
|
|
19
|
+
import { defaultApiClientOptions } from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
|
|
20
|
+
import { mockSubscriptionUpdated } from "passbolt-styleguide/src/react-extension/components/Administration/DisplaySubscriptionKey/DisplaySubscriptionKey.test.data";
|
|
21
|
+
|
|
22
|
+
import { mockApiResponse } from "../../../../../../test/mocks/mockApiResponse";
|
|
23
|
+
import PassboltEditionApiService, { PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME } from "./passboltEditionApiService";
|
|
24
|
+
|
|
25
|
+
describe("PassboltEditionApiService", () => {
|
|
26
|
+
/**
|
|
27
|
+
* @type {PassboltEditionApiService}
|
|
28
|
+
*/
|
|
29
|
+
let service;
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
enableFetchMocks();
|
|
33
|
+
fetch.resetMocks();
|
|
34
|
+
service = new PassboltEditionApiService(defaultApiClientOptions());
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("Should return the expected resource name", () => {
|
|
38
|
+
expect(PassboltEditionApiService.RESOURCE_NAME).toEqual(PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("::create", () => {
|
|
42
|
+
it("Should POST the subscription key and return the response", async () => {
|
|
43
|
+
expect.assertions(4);
|
|
44
|
+
|
|
45
|
+
const subscriptionEntity = new SubscriptionEntity(mockSubscriptionUpdated);
|
|
46
|
+
|
|
47
|
+
fetch.doMockOnceIf(new RegExp(`/${PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME}\\.json`), async () =>
|
|
48
|
+
mockApiResponse(subscriptionEntity),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const result = await service.create({ data: mockSubscriptionUpdated.data });
|
|
52
|
+
|
|
53
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
54
|
+
|
|
55
|
+
const { method, body } = fetch.mock.calls[0][1];
|
|
56
|
+
expect(method).toEqual("POST");
|
|
57
|
+
expect(JSON.parse(body).data).toEqual(mockSubscriptionUpdated.data);
|
|
58
|
+
|
|
59
|
+
expect(result).toEqual(subscriptionEntity.toDto());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("Should throw an error if the response is not properly formatted", async () => {
|
|
63
|
+
expect.assertions(2);
|
|
64
|
+
|
|
65
|
+
fetch.doMockOnceIf(new RegExp(`/${PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME}\\.json`), async () => "wrong");
|
|
66
|
+
|
|
67
|
+
await expect(service.create({ data: mockSubscriptionUpdated.data })).rejects.toBeInstanceOf(
|
|
68
|
+
PassboltBadResponseError,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("::delete", () => {
|
|
76
|
+
it("Should DELETE the subscription key and resolve", async () => {
|
|
77
|
+
expect.assertions(3);
|
|
78
|
+
|
|
79
|
+
fetch.doMockOnceIf(new RegExp(`/${PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME}\\.json`), async () =>
|
|
80
|
+
mockApiResponse({}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
await expect(service.delete()).resolves.not.toThrow();
|
|
84
|
+
|
|
85
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(fetch.mock.calls[0][1].method).toEqual("DELETE");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("Should throw an error if the response is not properly formatted", async () => {
|
|
90
|
+
expect.assertions(2);
|
|
91
|
+
|
|
92
|
+
fetch.doMockOnceIf(new RegExp(`/${PASSBOLT_EDITION_API_SERVICE_RESOURCE_NAME}\\.json`), async () => "wrong");
|
|
93
|
+
|
|
94
|
+
await expect(service.delete()).rejects.toBeInstanceOf(PassboltBadResponseError);
|
|
95
|
+
|
|
96
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|