passbolt-browser-extension 5.3.0 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/Gruntfile.js +1 -1
  3. package/RELEASE_NOTES.md +29 -48
  4. package/package.json +5 -5
  5. package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.js +51 -0
  6. package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.test.js +46 -0
  7. package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.js +53 -0
  8. package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.test.js +47 -0
  9. package/src/all/background_page/controller/clipboard/{clipboardController.js → copyToClipboardController.js} +8 -8
  10. package/src/all/background_page/controller/clipboard/copyToClipboardController.test.js +47 -0
  11. package/src/all/background_page/controller/extension/getExtensionVersionController.test.js +1 -1
  12. package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.js +53 -0
  13. package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.test.js +106 -0
  14. package/src/all/background_page/controller/user/deleteDryRunUserController.js +73 -0
  15. package/src/all/background_page/controller/user/deleteDryRunUserController.test.js +129 -0
  16. package/src/all/background_page/controller/user/deleteUserController.js +76 -0
  17. package/src/all/background_page/controller/user/deleteUserController.test.js +141 -0
  18. package/src/all/background_page/event/actionLogEvents.js +5 -12
  19. package/src/all/background_page/event/appEvents.js +33 -0
  20. package/src/all/background_page/event/findAllForActionLogController.js +58 -0
  21. package/src/all/background_page/event/findAllForActionLogController.test.js +43 -0
  22. package/src/all/background_page/event/quickAccessEvents.js +32 -0
  23. package/src/all/background_page/event/userEvents.js +7 -20
  24. package/src/all/background_page/event/webIntegrationEvents.js +11 -0
  25. package/src/all/background_page/model/actionLog/{actionLogModel.js → findActionLogService.js} +25 -5
  26. package/src/all/background_page/model/actionLog/findActionLogService.test.js +61 -0
  27. package/src/all/background_page/model/comment/commentModel.js +5 -5
  28. package/src/all/background_page/model/entity/actionLog/actionLogsCollection.test.data.js +18 -0
  29. package/src/all/background_page/model/entity/actionLog/defaultActionLogEntity.test.data.js +34 -0
  30. package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.test.js +0 -1
  31. package/src/all/background_page/model/user/userModel.js +0 -60
  32. package/src/all/background_page/pagemod/appPagemod.js +0 -2
  33. package/src/all/background_page/pagemod/appPagemod.test.js +2 -6
  34. package/src/all/background_page/service/alarm/globalAlarmService.js +2 -0
  35. package/src/all/background_page/service/api/actionLog/{actionLogService.js → actionLogApiService.js} +5 -4
  36. package/src/all/background_page/service/api/actionLog/actionLogApiService.test.js +55 -0
  37. package/src/all/background_page/service/api/comment/{commentService.js → commentApiService.js} +6 -7
  38. package/src/all/background_page/service/api/comment/commentApiService.test.js +122 -0
  39. package/src/all/background_page/service/auth/postLogoutService.js +2 -0
  40. package/src/all/background_page/service/auth/postLogoutService.test.js +4 -1
  41. package/src/all/background_page/service/browser/browserService.js +22 -0
  42. package/src/all/background_page/service/clipboard/clipboardProviderService.js +40 -0
  43. package/src/all/background_page/service/clipboard/clipboardProviderService.test.js +61 -0
  44. package/src/all/background_page/service/clipboard/copyToClipboardService.js +123 -0
  45. package/src/all/background_page/service/clipboard/copyToClipboardService.test.js +174 -0
  46. package/src/all/background_page/service/user/deleteUserService.js +97 -0
  47. package/src/all/background_page/service/user/deleteUserService.test.js +178 -0
  48. package/src/chrome/manifest.json +1 -1
  49. package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.js +31 -0
  50. package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.test.js +51 -0
  51. package/src/chrome-mv3/index.js +3 -3
  52. package/src/chrome-mv3/manifest.json +1 -1
  53. package/src/chrome-mv3/offscreens/{fetch.html → offscreen.html} +1 -1
  54. package/src/chrome-mv3/offscreens/{fetch.js → offscreen.js} +2 -2
  55. package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.js +54 -0
  56. package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.test.js +56 -0
  57. package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.js +36 -44
  58. package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.data.js +0 -1
  59. package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.js +90 -120
  60. package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.js +85 -0
  61. package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.test.js +99 -0
  62. package/src/chrome-mv3/polyfill/clipboardOffscreenPolyfill.js +19 -0
  63. package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.js +51 -0
  64. package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.test.js +70 -0
  65. package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.js +25 -0
  66. package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.data.js +21 -0
  67. package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.js +33 -0
  68. package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.js +25 -50
  69. package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.test.js +16 -39
  70. package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.js +14 -45
  71. package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.test.js +5 -37
  72. package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.js +43 -0
  73. package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.test.js +48 -0
  74. package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.js +119 -0
  75. package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.test.js +159 -0
  76. package/src/firefox/manifest.json +1 -1
  77. package/src/safari/manifest.json +1 -1
  78. package/test/jest.setup.js +4 -0
  79. package/test/mocks/mockNavigatorClipboard.js +40 -0
  80. package/test/mocks/mockWebExtensionPolyfill.js +2 -1
  81. package/{webpack-offscreens.fetch.config.js → webpack-offscreens.config.js} +1 -1
  82. package/webpack.service-worker.config.js +1 -0
  83. package/src/all/background_page/controller/clipboard/clipboardController.test.js +0 -68
  84. package/src/all/background_page/event/clipboardEvents.js +0 -28
@@ -0,0 +1,106 @@
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.4.0
13
+ */
14
+
15
+ import AccountEntity from "../../model/entity/account/accountEntity";
16
+ import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data";
17
+ import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
18
+ import GetOrFindMetadataKeysController from "./getOrFindMetadataKeysSettingsController";
19
+ import {
20
+ defaultMetadataKeysSettingsDto,
21
+ } from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysSettingsEntity.test.data";
22
+ import MetadataKeysSettingsEntity
23
+ from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysSettingsEntity";
24
+ import {enableFetchMocks} from "jest-fetch-mock";
25
+ import {
26
+ defaultCeOrganizationSettings
27
+ } from "../../model/entity/organizationSettings/organizationSettingsEntity.test.data";
28
+
29
+ beforeEach(() => {
30
+ enableFetchMocks();
31
+ });
32
+
33
+ jest.mock("../../service/passphrase/getPassphraseService");
34
+
35
+ describe("GetOrFindMetadataKeysSettingsController", () => {
36
+ let controller, account, apiClientOptions;
37
+
38
+ beforeEach(async() => {
39
+ account = new AccountEntity(defaultAccountDto());
40
+ apiClientOptions = defaultApiClientOptions();
41
+ controller = new GetOrFindMetadataKeysController(null, null, apiClientOptions, account);
42
+ // flush account related storage before each.
43
+ await controller.getOrFindMetadaSettingsService.metadataKeysSettingsLocalStorage.flush();
44
+ });
45
+
46
+ describe("::exec", () => {
47
+ it("get or find metadata keys settings for a v5.", async() => {
48
+ expect.assertions(3);
49
+
50
+ const metadataKeysSettingsDto = defaultMetadataKeysSettingsDto();
51
+ const siteSettingsDto = defaultCeOrganizationSettings();
52
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.findMetadataSettingsService, "findKeysSettings")
53
+ .mockImplementationOnce(() => new MetadataKeysSettingsEntity(metadataKeysSettingsDto));
54
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.organisationSettingsModel.organizationSettingsService, "find")
55
+ .mockImplementation(() => siteSettingsDto);
56
+
57
+ const metadataKeysSettings = await controller.exec();
58
+
59
+ expect(metadataKeysSettings).toBeInstanceOf(MetadataKeysSettingsEntity);
60
+ expect(metadataKeysSettings.toDto()).toEqual(metadataKeysSettingsDto);
61
+ const storageValue = await controller.getOrFindMetadaSettingsService.metadataKeysSettingsLocalStorage.get();
62
+ expect(storageValue).toEqual(metadataKeysSettingsDto);
63
+ });
64
+
65
+ it("get or find metadata keys settings with usage of the shared key for a v5.", async() => {
66
+ expect.assertions(3);
67
+
68
+ const metadataKeysSettingsDto = defaultMetadataKeysSettingsDto({
69
+ allow_usage_of_personal_keys: false,
70
+ zero_knowledge_key_share: true,
71
+ });
72
+ const siteSettingsDto = defaultCeOrganizationSettings();
73
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.findMetadataSettingsService, "findKeysSettings")
74
+ .mockImplementationOnce(() => new MetadataKeysSettingsEntity(metadataKeysSettingsDto));
75
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.organisationSettingsModel.organizationSettingsService, "find")
76
+ .mockImplementation(() => siteSettingsDto);
77
+
78
+ const metadataKeysSettings = await controller.exec();
79
+
80
+ expect(metadataKeysSettings).toBeInstanceOf(MetadataKeysSettingsEntity);
81
+ expect(metadataKeysSettings.toDto()).toEqual(metadataKeysSettingsDto);
82
+ const storageValue = await controller.getOrFindMetadaSettingsService.metadataKeysSettingsLocalStorage.get();
83
+ expect(storageValue).toEqual(metadataKeysSettingsDto);
84
+ });
85
+
86
+ it("get or find metadata keys settings for a v4 should have the default.", async() => {
87
+ expect.assertions(3);
88
+
89
+ const siteSettingsDto = defaultCeOrganizationSettings();
90
+ // disable the plugin metadata.
91
+ delete siteSettingsDto.passbolt.plugins.metadata;
92
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.organisationSettingsModel.organizationSettingsService, "find")
93
+ .mockImplementation(() => siteSettingsDto);
94
+ jest.spyOn(controller.getOrFindMetadaSettingsService.findAndUpdateMetadataSettingsLocalStorageService.findMetadataSettingsService.metadataKeysSettingsApiService, "findSettings")
95
+ .mockImplementationOnce(() => {});
96
+
97
+ const metadataKeysSettings = await controller.exec();
98
+
99
+ const expectedMetadataKeysSettingsDto = MetadataKeysSettingsEntity.createFromDefault().toDto();
100
+ expect(metadataKeysSettings).toBeInstanceOf(MetadataKeysSettingsEntity);
101
+ expect(metadataKeysSettings.toDto()).toEqual(expectedMetadataKeysSettingsDto);
102
+ const storageValue = await controller.getOrFindMetadaSettingsService.metadataKeysSettingsLocalStorage.get();
103
+ expect(storageValue).toEqual(expectedMetadataKeysSettingsDto);
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,73 @@
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.4.0
13
+ */
14
+
15
+ import DeleteDryRunError from "../../error/deleteDryRunError";
16
+ import GetPassphraseService from "../../service/passphrase/getPassphraseService";
17
+ import DeleteUserService from "../../service/user/deleteUserService";
18
+ import DecryptMetadataService from "../../service/metadata/decryptMetadataService";
19
+
20
+ class DeleteDryRunUserController {
21
+ /**
22
+ * Constructor
23
+ * @param {Worker} worker
24
+ * @param {string} requestId uuid
25
+ * @param {ApiClientOptions} apiClientOptions The api client options
26
+ * @param {AccountEntity} account The account associated to the worker.
27
+ */
28
+ constructor(worker, requestId, apiClientOptions, account) {
29
+ this.worker = worker;
30
+ this.requestId = requestId;
31
+ this.deleteUserService = new DeleteUserService(account, apiClientOptions);
32
+ this.getPassphraseService = new GetPassphraseService(account);
33
+ this.decryptMetadataService = new DecryptMetadataService(apiClientOptions, account);
34
+ }
35
+
36
+ /**
37
+ * Controller executor.
38
+ * @returns {Promise<void>}
39
+ */
40
+ async _exec(userId) {
41
+ try {
42
+ await this.exec(userId);
43
+ this.worker.port.emit(this.requestId, 'SUCCESS');
44
+ } catch (error) {
45
+ console.error(error);
46
+ this.worker.port.emit(this.requestId, 'ERROR', error);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Delete dry run the user.
52
+ * @param {string} userId The user id.
53
+ * @return {Promise<void>}
54
+ * @throws {DeleteDryRunError} if the data should be transferred to someone.
55
+ * @throws {Error} if the data returned by the API is not a PassboltApiFetchError with error code 400.
56
+ */
57
+ async exec(userId) {
58
+ try {
59
+ await this.deleteUserService.deleteDryRun(userId);
60
+ } catch (error) {
61
+ if (error instanceof DeleteDryRunError) {
62
+ const resourcesCollection = error.errors.resources?.sole_owner;
63
+ if (resourcesCollection?.items.some(resourceEntity => !resourceEntity.isMetadataDecrypted())) {
64
+ const passphrase = await this.getPassphraseService.getPassphrase(this.worker);
65
+ await this.decryptMetadataService.decryptAllFromForeignModels(error.errors.resources.sole_owner, passphrase);
66
+ }
67
+ }
68
+ throw error;
69
+ }
70
+ }
71
+ }
72
+
73
+ export default DeleteDryRunUserController;
@@ -0,0 +1,129 @@
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.4.0
13
+ */
14
+
15
+ import {enableFetchMocks} from "jest-fetch-mock";
16
+ import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse";
17
+ import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
18
+ import RoleEntity from "passbolt-styleguide/src/shared/models/entity/role/roleEntity";
19
+ import AccountEntity from "../../model/entity/account/accountEntity";
20
+ import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data";
21
+ import {defaultSharedResourcesWithEncryptedMetadataDtos, defaultResourcesDtos} from "passbolt-styleguide/src/shared/models/entity/resource/resourcesCollection.test.data";
22
+ import {defaultDecryptedSharedMetadataKeysDtos} from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysCollection.test.data";
23
+ import PassboltApiFetchError from "passbolt-styleguide/src/shared/lib/Error/PassboltApiFetchError";
24
+ import DeleteDryRunUserController from "./deleteDryRunUserController";
25
+ import {v4 as uuidv4} from "uuid";
26
+ import DeleteDryRunError from "../../error/deleteDryRunError";
27
+ import {pgpKeys} from 'passbolt-styleguide/test/fixture/pgpKeys/keys';
28
+ import MetadataKeysCollection from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysCollection";
29
+
30
+ beforeEach(() => {
31
+ enableFetchMocks();
32
+ });
33
+
34
+ describe("DeleteDryRunUserController", () => {
35
+ describe("::exec", () => {
36
+ const accountDto = defaultAccountDto();
37
+ accountDto.role_name = RoleEntity.ROLE_ADMIN;
38
+ const account = new AccountEntity(accountDto);
39
+
40
+ it("Should delete dry run the user without transfer", async() => {
41
+ expect.assertions(1);
42
+
43
+ const userId = uuidv4();
44
+
45
+ fetch.doMockOnceIf(new RegExp(`/users/${userId}/dry-run.json`), () => mockApiResponse({}));
46
+
47
+ const controller = new DeleteDryRunUserController(null, null, defaultApiClientOptions(), account);
48
+ jest.spyOn(controller.deleteUserService, "deleteDryRun");
49
+
50
+ await controller.exec(userId);
51
+
52
+ expect(controller.deleteUserService.deleteDryRun).toHaveBeenCalledWith(userId);
53
+ });
54
+
55
+ it("Should throw an error if the user has transfer and decrypt", async() => {
56
+ expect.assertions(2);
57
+
58
+ const userId = uuidv4();
59
+ const metadataKeysDtos = defaultDecryptedSharedMetadataKeysDtos();
60
+ const metadataKeys = new MetadataKeysCollection(metadataKeysDtos);
61
+ const collectionDto = defaultSharedResourcesWithEncryptedMetadataDtos(10, {
62
+ metadata_key_id: metadataKeysDtos[0].id
63
+ });
64
+ const body = {
65
+ errors: {
66
+ resources: {
67
+ sole_owner: collectionDto
68
+ }
69
+ }
70
+ };
71
+
72
+ fetch.doMockOnceIf(new RegExp(`/users/${userId}/dry-run.json`), () => mockApiResponseError(400, "Need transfer", body));
73
+
74
+ const controller = new DeleteDryRunUserController(null, null, defaultApiClientOptions(), account);
75
+ jest.spyOn(controller.decryptMetadataService.getOrFindMetadataKeysService, "getOrFindAll").mockImplementation(() => metadataKeys);
76
+ jest.spyOn(controller.getPassphraseService, "getPassphrase").mockImplementationOnce(() => pgpKeys.ada.passphrase);
77
+
78
+ try {
79
+ await controller.exec(userId);
80
+ } catch (error) {
81
+ expect(error).toBeInstanceOf(DeleteDryRunError);
82
+ expect(error.errors.resources.sole_owner.items.every(resourceEntity => resourceEntity.isMetadataDecrypted())).toBeTruthy();
83
+ }
84
+ });
85
+
86
+ it("Should throw an error if the user has transfer without decryption", async() => {
87
+ expect.assertions(4);
88
+
89
+ const userId = uuidv4();
90
+ const collectionDto = defaultResourcesDtos();
91
+ const body = {
92
+ errors: {
93
+ resources: {
94
+ sole_owner: collectionDto
95
+ }
96
+ }
97
+ };
98
+
99
+ fetch.doMockOnceIf(new RegExp(`/users/${userId}/dry-run.json`), () => mockApiResponseError(400, "Need transfer", body));
100
+
101
+ const controller = new DeleteDryRunUserController(null, null, defaultApiClientOptions(), account);
102
+ jest.spyOn(controller.decryptMetadataService.getOrFindMetadataKeysService, "getOrFindAll");
103
+ jest.spyOn(controller.getPassphraseService, "getPassphrase");
104
+
105
+ try {
106
+ await controller.exec(userId);
107
+ } catch (error) {
108
+ expect(error).toBeInstanceOf(DeleteDryRunError);
109
+ expect(error.errors.resources.sole_owner.items.every(resourceEntity => resourceEntity.isMetadataDecrypted())).toBeTruthy();
110
+ expect(controller.decryptMetadataService.getOrFindMetadataKeysService.getOrFindAll).not.toHaveBeenCalled();
111
+ expect(controller.getPassphraseService.getPassphrase).not.toHaveBeenCalled();
112
+ }
113
+ });
114
+
115
+ it("Should throw an error if something wrong happens on the API", async() => {
116
+ expect.assertions(1);
117
+
118
+ const userId = uuidv4();
119
+ fetch.doMockOnceIf(new RegExp(`/users/${userId}/dry-run.json`), async() => mockApiResponseError(500, "Something went wrong"));
120
+
121
+ const controller = new DeleteDryRunUserController(null, null, defaultApiClientOptions(), account);
122
+ try {
123
+ await controller.exec(userId);
124
+ } catch (e) {
125
+ expect(e).toBeInstanceOf(PassboltApiFetchError);
126
+ }
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,76 @@
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.4.0
13
+ */
14
+
15
+ import DeleteDryRunError from "../../error/deleteDryRunError";
16
+ import GetPassphraseService from "../../service/passphrase/getPassphraseService";
17
+ import DeleteUserService from "../../service/user/deleteUserService";
18
+ import DecryptMetadataService from "../../service/metadata/decryptMetadataService";
19
+ import UserDeleteTransferEntity from "../../model/entity/user/transfer/userDeleteTransferEntity";
20
+
21
+ class DeleteUserController {
22
+ /**
23
+ * Constructor
24
+ * @param {Worker} worker
25
+ * @param {string} requestId uuid
26
+ * @param {ApiClientOptions} apiClientOptions The api client options
27
+ * @param {AccountEntity} account The account associated to the worker.
28
+ */
29
+ constructor(worker, requestId, apiClientOptions, account) {
30
+ this.worker = worker;
31
+ this.requestId = requestId;
32
+ this.deleteUserService = new DeleteUserService(account, apiClientOptions);
33
+ this.getPassphraseService = new GetPassphraseService(account);
34
+ this.decryptMetadataService = new DecryptMetadataService(apiClientOptions, account);
35
+ }
36
+
37
+ /**
38
+ * Controller executor.
39
+ * @returns {Promise<void>}
40
+ */
41
+ async _exec(userId, transferDto) {
42
+ try {
43
+ await this.exec(userId, transferDto);
44
+ this.worker.port.emit(this.requestId, 'SUCCESS');
45
+ } catch (error) {
46
+ console.error(error);
47
+ this.worker.port.emit(this.requestId, 'ERROR', error);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Delete dry run the user.
53
+ * @param {string} userId The user id.
54
+ * @param {object} [transferDto = null] The transfer dto.
55
+ * @return {Promise<void>}
56
+ * @throws {DeleteDryRunError} if the data should be transferred to someone.
57
+ * @throws {Error} if the data returned by the API is not a PassboltApiFetchError with error code 400.
58
+ */
59
+ async exec(userId, transferDto = null) {
60
+ try {
61
+ const transferEntity = transferDto ? new UserDeleteTransferEntity(transferDto) : null;
62
+ await this.deleteUserService.delete(userId, transferEntity);
63
+ } catch (error) {
64
+ if (error instanceof DeleteDryRunError) {
65
+ const resourcesCollection = error.errors.resources?.sole_owner;
66
+ if (resourcesCollection?.items.some(resourceEntity => !resourceEntity.isMetadataDecrypted())) {
67
+ const passphrase = this.getPassphraseService.getPassphrase(this.worker);
68
+ await this.decryptMetadataService.decryptAllFromForeignModels(error.errors.resources.sole_owner, passphrase);
69
+ }
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+ }
75
+
76
+ export default DeleteUserController;
@@ -0,0 +1,141 @@
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.4.0
13
+ */
14
+
15
+ import {enableFetchMocks} from "jest-fetch-mock";
16
+ import {mockApiResponse, mockApiResponseError} from "../../../../../test/mocks/mockApiResponse";
17
+ import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
18
+ import RoleEntity from "passbolt-styleguide/src/shared/models/entity/role/roleEntity";
19
+ import AccountEntity from "../../model/entity/account/accountEntity";
20
+ import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data";
21
+ import {defaultSharedResourcesWithEncryptedMetadataDtos, defaultResourcesDtos} from "passbolt-styleguide/src/shared/models/entity/resource/resourcesCollection.test.data";
22
+ import {defaultDecryptedSharedMetadataKeysDtos} from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysCollection.test.data";
23
+ import PassboltApiFetchError from "passbolt-styleguide/src/shared/lib/Error/PassboltApiFetchError";
24
+ import {v4 as uuidv4} from "uuid";
25
+ import DeleteDryRunError from "../../error/deleteDryRunError";
26
+ import {pgpKeys} from 'passbolt-styleguide/test/fixture/pgpKeys/keys';
27
+ import MetadataKeysCollection from "passbolt-styleguide/src/shared/models/entity/metadata/metadataKeysCollection";
28
+ import {defaultUserDto} from "passbolt-styleguide/src/shared/models/entity/user/userEntity.test.data";
29
+ import DeleteUserController from "./deleteUserController";
30
+ import UserLocalStorage from "../../service/local_storage/userLocalStorage";
31
+ import UsersCollection from "../../model/entity/user/usersCollection";
32
+
33
+ beforeEach(() => {
34
+ enableFetchMocks();
35
+ });
36
+
37
+ describe("DeleteUserController", () => {
38
+ describe("::exec", () => {
39
+ const accountDto = defaultAccountDto();
40
+ accountDto.role_name = RoleEntity.ROLE_ADMIN;
41
+ const account = new AccountEntity(accountDto);
42
+
43
+ it("Should delete the user without transfer", async() => {
44
+ expect.assertions(2);
45
+
46
+ const userDto = defaultUserDto();
47
+ await UserLocalStorage.set(new UsersCollection([userDto]));
48
+
49
+ fetch.doMockOnceIf(new RegExp(`/users/${userDto.id}.json`), () => mockApiResponse({}));
50
+
51
+ const controller = new DeleteUserController(null, null, defaultApiClientOptions(), account);
52
+ jest.spyOn(controller.deleteUserService, "delete");
53
+ jest.spyOn(UserLocalStorage, "delete");
54
+
55
+ await controller.exec(userDto.id);
56
+
57
+ expect(controller.deleteUserService.delete).toHaveBeenCalledWith(userDto.id, null);
58
+ expect(UserLocalStorage.delete).toHaveBeenCalledWith(userDto.id);
59
+ });
60
+
61
+ it("Should throw an error if the user has transfer and decrypt", async() => {
62
+ expect.assertions(3);
63
+
64
+ const userDto = defaultUserDto();
65
+ await UserLocalStorage.set(new UsersCollection([userDto]));
66
+ const metadataKeysDtos = defaultDecryptedSharedMetadataKeysDtos();
67
+ const metadataKeys = new MetadataKeysCollection(metadataKeysDtos);
68
+ const collectionDto = defaultSharedResourcesWithEncryptedMetadataDtos(10, {
69
+ metadata_key_id: metadataKeysDtos[0].id
70
+ });
71
+ const body = {
72
+ errors: {
73
+ resources: {
74
+ sole_owner: collectionDto
75
+ }
76
+ }
77
+ };
78
+
79
+ fetch.doMockOnceIf(new RegExp(`/users/${userDto.id}.json`), () => mockApiResponseError(400, "Need transfer", body));
80
+
81
+ const controller = new DeleteUserController(null, null, defaultApiClientOptions(), account);
82
+ jest.spyOn(controller.decryptMetadataService.getOrFindMetadataKeysService, "getOrFindAll").mockImplementation(() => metadataKeys);
83
+ jest.spyOn(controller.getPassphraseService, "getPassphrase").mockImplementationOnce(() => pgpKeys.ada.passphrase);
84
+ jest.spyOn(UserLocalStorage, "delete");
85
+
86
+ try {
87
+ await controller.exec(userDto.id);
88
+ } catch (error) {
89
+ expect(error).toBeInstanceOf(DeleteDryRunError);
90
+ expect(error.errors.resources.sole_owner.items.every(resourceEntity => resourceEntity.isMetadataDecrypted())).toBeTruthy();
91
+ expect(UserLocalStorage.delete).not.toHaveBeenCalledWith(userDto.id);
92
+ }
93
+ });
94
+
95
+ it("Should throw an error if the user has transfer without decryption", async() => {
96
+ expect.assertions(5);
97
+
98
+ const userDto = defaultUserDto();
99
+ await UserLocalStorage.set(new UsersCollection([userDto]));
100
+ const collectionDto = defaultResourcesDtos();
101
+ const body = {
102
+ errors: {
103
+ resources: {
104
+ sole_owner: collectionDto
105
+ }
106
+ }
107
+ };
108
+
109
+ fetch.doMockOnceIf(new RegExp(`/users/${userDto.id}.json`), () => mockApiResponseError(400, "Need transfer", body));
110
+
111
+ const controller = new DeleteUserController(null, null, defaultApiClientOptions(), account);
112
+ jest.spyOn(controller.decryptMetadataService.getOrFindMetadataKeysService, "getOrFindAll");
113
+ jest.spyOn(controller.getPassphraseService, "getPassphrase");
114
+ jest.spyOn(UserLocalStorage, "delete");
115
+
116
+ try {
117
+ await controller.exec(userDto.id);
118
+ } catch (error) {
119
+ expect(error).toBeInstanceOf(DeleteDryRunError);
120
+ expect(error.errors.resources.sole_owner.items.every(resourceEntity => resourceEntity.isMetadataDecrypted())).toBeTruthy();
121
+ expect(controller.decryptMetadataService.getOrFindMetadataKeysService.getOrFindAll).not.toHaveBeenCalled();
122
+ expect(controller.getPassphraseService.getPassphrase).not.toHaveBeenCalled();
123
+ expect(UserLocalStorage.delete).not.toHaveBeenCalledWith(userDto.id);
124
+ }
125
+ });
126
+
127
+ it("Should throw an error if something wrong happens on the API", async() => {
128
+ expect.assertions(1);
129
+
130
+ const userId = uuidv4();
131
+ fetch.doMockOnceIf(new RegExp(`/users/${userId}.json`), async() => mockApiResponseError(500, "Something went wrong"));
132
+
133
+ const controller = new DeleteUserController(null, null, defaultApiClientOptions(), account);
134
+ try {
135
+ await controller.exec(userId);
136
+ } catch (e) {
137
+ expect(e).toBeInstanceOf(PassboltApiFetchError);
138
+ }
139
+ });
140
+ });
141
+ });
@@ -10,14 +10,14 @@
10
10
  * @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11
11
  * @link https://www.passbolt.com Passbolt(tm)
12
12
  */
13
- import ActionLogModel from "../model/actionLog/actionLogModel";
13
+ import FindAllForActionLogController from "./findAllForActionLogController";
14
14
 
15
15
  /**
16
16
  * Listens to the action logs events
17
17
  * @param {Worker} worker The worker
18
18
  * @param {ApiClientOptions} apiClientOptions The api client options
19
19
  */
20
- const listen = function(worker, apiClientOptions) {
20
+ const listen = function(worker, apiClientOptions, account) {
21
21
  /*
22
22
  * Find all action logs for a given instance
23
23
  *
@@ -25,16 +25,9 @@ const listen = function(worker, apiClientOptions) {
25
25
  * @param requestId {uuid} The request identifier
26
26
  * @param options {object} The options to apply to the find
27
27
  */
28
- worker.port.on('passbolt.actionlogs.find-all-for', async(requestId, foreignModel, foreignId, options) => {
29
- try {
30
- const actionLogModel = new ActionLogModel(apiClientOptions);
31
- const {limit, page} = options;
32
- const actionLogs = await actionLogModel.findAllFor(foreignModel, foreignId, page, limit);
33
- worker.port.emit(requestId, 'SUCCESS', actionLogs);
34
- } catch (error) {
35
- console.error(error);
36
- worker.port.emit(requestId, 'ERROR', error);
37
- }
28
+ worker.port.on('passbolt.actionlogs.find-all-for', async(requestId, foreignModel, foreignId, {page, limit}) => {
29
+ const controller = new FindAllForActionLogController(worker, requestId, apiClientOptions, account);
30
+ await controller._exec(foreignModel, foreignId, {page, limit});
38
31
  });
39
32
  };
40
33
 
@@ -67,6 +67,8 @@ import FindMetadataMigrateResourcesController from "../controller/migrateMetadat
67
67
  import MigrateMetadataResourcesController from "../controller/migrateMetadata/migrateMetadataResourcesController";
68
68
  import DownloadOrganizationGeneratedKey from "../controller/accountRecovery/downloadOrganizationGenerateKeyController";
69
69
  import ShareMetadataKeyPrivateController from "../controller/metadata/shareMetadataKeyPrivateController";
70
+ import CopyToClipboardController from "../controller/clipboard/copyToClipboardController";
71
+ import CopyTemporarilyToClipboardController from "../controller/clipboard/copyTemporarilyToClipboardController";
70
72
 
71
73
  const listen = function(worker, apiClientOptions, account) {
72
74
  /*
@@ -433,5 +435,36 @@ const listen = function(worker, apiClientOptions, account) {
433
435
  const controller = new ShareMetadataKeyPrivateController(worker, requestId, apiClientOptions, account);
434
436
  await controller._exec(userId);
435
437
  });
438
+
439
+
440
+ /*
441
+ * ==================================================================================
442
+ * Clipboard events.
443
+ * ==================================================================================
444
+ */
445
+
446
+ /**
447
+ * Copies the given content into the clipboard and clear any clipboard flush alarms.
448
+ *
449
+ * @listens assbolt.clipboard.copy
450
+ * @param {string} requestId The request identifier
451
+ * @param {string} text the content to copy
452
+ */
453
+ worker.port.on('passbolt.clipboard.copy', async(requestId, text) => {
454
+ const clipboardController = new CopyToClipboardController(worker, requestId);
455
+ await clipboardController._exec(text);
456
+ });
457
+
458
+ /**
459
+ * Copies temporarily the given content into the clipboard and set a clipboard flush alarm.
460
+ *
461
+ * @listens assbolt.clipboard.copy-temporarily
462
+ * @param {string} requestId The request identifier
463
+ * @param {string} text the content to copy
464
+ */
465
+ worker.port.on('passbolt.clipboard.copy-temporarily', async(requestId, text) => {
466
+ const clipboardController = new CopyTemporarilyToClipboardController(worker, requestId);
467
+ await clipboardController._exec(text);
468
+ });
436
469
  };
437
470
  export const AppEvents = {listen};
@@ -0,0 +1,58 @@
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.4.0
13
+ */
14
+ import FindActionLogService from "../model/actionLog/findActionLogService";
15
+
16
+ class FindAllForActionLogController {
17
+ /**
18
+ * @constructor
19
+ * @param {Worker} worker The worker
20
+ * @param {string} requestId uuid
21
+ * @param {ApiClientOptions} apiClientOptions The api client options
22
+ * @param {AccountEntity} account the user account
23
+ */
24
+ constructor(worker, requestId, apiClientOptions, account) {
25
+ this.worker = worker;
26
+ this.requestId = requestId;
27
+ this.findAllForActionLogService = new FindActionLogService(apiClientOptions, account);
28
+ }
29
+
30
+ /**
31
+ * Controller executor.
32
+ * @param {string} foreignModel The foreign model
33
+ * @param {string} foreignId The foreign ID
34
+ * @param {object} options Options
35
+ * @param {number} options.page The page number
36
+ * @param {number} options.limit The limit of activities per page
37
+ * @returns {Promise<void>}
38
+ */
39
+ async _exec(foreignModel, foreignId, {page, limit}) {
40
+ try {
41
+ const result = await this.exec(foreignModel, foreignId, {page, limit});
42
+ this.worker.port.emit(this.requestId, 'SUCCESS', result);
43
+ } catch (error) {
44
+ console.error(error);
45
+ this.worker.port.emit(this.requestId, 'ERROR', error);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Find all action logs
51
+ * @returns {Promise<*>}
52
+ */
53
+ async exec(foreignModel, foreignId, {page, limit}) {
54
+ return this.findAllForActionLogService.findAllFor(foreignModel, foreignId, page, limit);
55
+ }
56
+ }
57
+
58
+ export default FindAllForActionLogController;