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.
- package/CHANGELOG.md +30 -1
- package/Gruntfile.js +1 -1
- package/RELEASE_NOTES.md +29 -48
- package/package.json +5 -5
- package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.js +51 -0
- package/src/all/background_page/controller/clipboard/cancelClipboardContentFlushController.test.js +46 -0
- package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.js +53 -0
- package/src/all/background_page/controller/clipboard/copyTemporarilyToClipboardController.test.js +47 -0
- package/src/all/background_page/controller/clipboard/{clipboardController.js → copyToClipboardController.js} +8 -8
- package/src/all/background_page/controller/clipboard/copyToClipboardController.test.js +47 -0
- package/src/all/background_page/controller/extension/getExtensionVersionController.test.js +1 -1
- package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.js +53 -0
- package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.test.js +106 -0
- package/src/all/background_page/controller/user/deleteDryRunUserController.js +73 -0
- package/src/all/background_page/controller/user/deleteDryRunUserController.test.js +129 -0
- package/src/all/background_page/controller/user/deleteUserController.js +76 -0
- package/src/all/background_page/controller/user/deleteUserController.test.js +141 -0
- package/src/all/background_page/event/actionLogEvents.js +5 -12
- package/src/all/background_page/event/appEvents.js +33 -0
- package/src/all/background_page/event/findAllForActionLogController.js +58 -0
- package/src/all/background_page/event/findAllForActionLogController.test.js +43 -0
- package/src/all/background_page/event/quickAccessEvents.js +32 -0
- package/src/all/background_page/event/userEvents.js +7 -20
- package/src/all/background_page/event/webIntegrationEvents.js +11 -0
- package/src/all/background_page/model/actionLog/{actionLogModel.js → findActionLogService.js} +25 -5
- package/src/all/background_page/model/actionLog/findActionLogService.test.js +61 -0
- package/src/all/background_page/model/comment/commentModel.js +5 -5
- package/src/all/background_page/model/entity/actionLog/actionLogsCollection.test.data.js +18 -0
- package/src/all/background_page/model/entity/actionLog/defaultActionLogEntity.test.data.js +34 -0
- package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.test.js +0 -1
- package/src/all/background_page/model/user/userModel.js +0 -60
- package/src/all/background_page/pagemod/appPagemod.js +0 -2
- package/src/all/background_page/pagemod/appPagemod.test.js +2 -6
- package/src/all/background_page/service/alarm/globalAlarmService.js +2 -0
- package/src/all/background_page/service/api/actionLog/{actionLogService.js → actionLogApiService.js} +5 -4
- package/src/all/background_page/service/api/actionLog/actionLogApiService.test.js +55 -0
- package/src/all/background_page/service/api/comment/{commentService.js → commentApiService.js} +6 -7
- package/src/all/background_page/service/api/comment/commentApiService.test.js +122 -0
- package/src/all/background_page/service/auth/postLogoutService.js +2 -0
- package/src/all/background_page/service/auth/postLogoutService.test.js +4 -1
- package/src/all/background_page/service/browser/browserService.js +22 -0
- package/src/all/background_page/service/clipboard/clipboardProviderService.js +40 -0
- package/src/all/background_page/service/clipboard/clipboardProviderService.test.js +61 -0
- package/src/all/background_page/service/clipboard/copyToClipboardService.js +123 -0
- package/src/all/background_page/service/clipboard/copyToClipboardService.test.js +174 -0
- package/src/all/background_page/service/user/deleteUserService.js +97 -0
- package/src/all/background_page/service/user/deleteUserService.test.js +178 -0
- package/src/chrome/manifest.json +1 -1
- package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.js +31 -0
- package/src/chrome/polyfill/clipboard/edgeBackgroundPageClipboardService.test.js +51 -0
- package/src/chrome-mv3/index.js +3 -3
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/chrome-mv3/offscreens/{fetch.html → offscreen.html} +1 -1
- package/src/chrome-mv3/offscreens/{fetch.js → offscreen.js} +2 -2
- package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.js +54 -0
- package/src/chrome-mv3/offscreens/service/clipboard/writeClipobardOffscreenService.test.js +56 -0
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.js +36 -44
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.data.js +0 -1
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.js +90 -120
- package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.js +85 -0
- package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.test.js +99 -0
- package/src/chrome-mv3/polyfill/clipboardOffscreenPolyfill.js +19 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.js +51 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/requestClipboardOffscreenService.test.js +70 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.js +25 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.data.js +21 -0
- package/src/chrome-mv3/serviceWorker/service/clipboard/responseClipboardOffscreenService.test.js +33 -0
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.js +25 -50
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.test.js +16 -39
- package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.js +14 -45
- package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.test.js +5 -37
- package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.js +43 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/createOffscreenDocumentService.test.js +48 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.js +119 -0
- package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.test.js +159 -0
- package/src/firefox/manifest.json +1 -1
- package/src/safari/manifest.json +1 -1
- package/test/jest.setup.js +4 -0
- package/test/mocks/mockNavigatorClipboard.js +40 -0
- package/test/mocks/mockWebExtensionPolyfill.js +2 -1
- package/{webpack-offscreens.fetch.config.js → webpack-offscreens.config.js} +1 -1
- package/webpack.service-worker.config.js +1 -0
- package/src/all/background_page/controller/clipboard/clipboardController.test.js +0 -68
- package/src/all/background_page/event/clipboardEvents.js +0 -28
|
@@ -0,0 +1,97 @@
|
|
|
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 UserService from "../api/user/userService";
|
|
15
|
+
import {assertType, assertUuid} from "../../utils/assertions";
|
|
16
|
+
import DeleteDryRunError from "../../error/deleteDryRunError";
|
|
17
|
+
import PassboltApiFetchError from "passbolt-styleguide/src/shared/lib/Error/PassboltApiFetchError";
|
|
18
|
+
import UserDeleteTransferEntity from "../../model/entity/user/transfer/userDeleteTransferEntity";
|
|
19
|
+
import UserLocalStorage from "../local_storage/userLocalStorage";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The service aims to delete user from the API.
|
|
23
|
+
*/
|
|
24
|
+
export default class DeleteUserService {
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @param {AccountEntity} account The user account
|
|
28
|
+
* @param {ApiClientOptions} apiClientOptions The api client options
|
|
29
|
+
*/
|
|
30
|
+
constructor(account, apiClientOptions) {
|
|
31
|
+
this.account = account;
|
|
32
|
+
this.userServiceApi = new UserService(apiClientOptions);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Delete dry run a user.
|
|
37
|
+
* Check if a user can be deleted.
|
|
38
|
+
*
|
|
39
|
+
* A user can not be deleted if:
|
|
40
|
+
* - they are the only owner of a shared resource
|
|
41
|
+
* - they are the only group manager of a group that owns a shared resource
|
|
42
|
+
* In such case ownership transfer is required.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} userId The user id.
|
|
45
|
+
* @returns {Promise<void>}
|
|
46
|
+
* @throws {DeleteDryRunError} if the data should be transferred to someone.
|
|
47
|
+
* @throws {Error} if the data returned by the API is not a PassboltApiFetchError with error code 400 and groups, resources or folders to transfer.
|
|
48
|
+
*/
|
|
49
|
+
async deleteDryRun(userId) {
|
|
50
|
+
assertUuid(userId, "The parameter \"userId\" should be a UUID");
|
|
51
|
+
try {
|
|
52
|
+
await this.userServiceApi.delete(userId, {}, true);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
await this.handleError(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Delete a user and transfer ownership if needed.
|
|
60
|
+
* @param {string} userId The user id.
|
|
61
|
+
* @param {UserDeleteTransferEntity} [transfer] optional ownership transfer information if needed.
|
|
62
|
+
* @returns {Promise<void>}
|
|
63
|
+
* @throws {DeleteDryRunError} if some permissions must be transferred.
|
|
64
|
+
* @throws {Error} if the data returned by the API is not a PassboltApiFetchError with error code 400 and groups, resources or folders to transfer.
|
|
65
|
+
*/
|
|
66
|
+
async delete(userId, transfer) {
|
|
67
|
+
assertUuid(userId, "The parameter \"userId\" should be a UUID");
|
|
68
|
+
if (transfer !== null) {
|
|
69
|
+
assertType(transfer, UserDeleteTransferEntity, 'The `transfer` parameter should be a UserDeleteTransferEntity.');
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const deleteData = transfer ? transfer.toDto() : {};
|
|
73
|
+
await this.userServiceApi.delete(userId, deleteData);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
await this.handleError(error);
|
|
76
|
+
}
|
|
77
|
+
// Update local storage
|
|
78
|
+
await UserLocalStorage.delete(userId);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @private
|
|
83
|
+
* @param {object} error The error
|
|
84
|
+
* @throws {DeleteDryRunError} if some permissions must be transferred.
|
|
85
|
+
* @throws {Error} if the data returned by the API is not a PassboltApiFetchError with error code 400 and groups, resources or folders to transfer.
|
|
86
|
+
*/
|
|
87
|
+
async handleError(error) {
|
|
88
|
+
if (error instanceof PassboltApiFetchError && error.data.code === 400 && error.data.body.errors) {
|
|
89
|
+
/*
|
|
90
|
+
* recast generic 400 error into a delete dry run error
|
|
91
|
+
* allowing validation of the returned entities and reuse down the line to transfer permissions
|
|
92
|
+
*/
|
|
93
|
+
throw new DeleteDryRunError(error.message, error.data.body.errors);
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
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 DeleteUserService from "./deleteUserService";
|
|
19
|
+
import {v4 as uuidv4} from "uuid";
|
|
20
|
+
import DeleteDryRunError from "../../error/deleteDryRunError";
|
|
21
|
+
import PassboltApiFetchError from "passbolt-styleguide/src/shared/lib/Error/PassboltApiFetchError";
|
|
22
|
+
import UserDeleteTransferEntity from "../../model/entity/user/transfer/userDeleteTransferEntity";
|
|
23
|
+
import {defaultUserDeleteTransferDto} from "passbolt-styleguide/src/shared/models/entity/user/userDeleteTransferEntity.test.data";
|
|
24
|
+
import {defaultResourceDto} from "passbolt-styleguide/src/shared/models/entity/resource/resourceEntity.test.data";
|
|
25
|
+
import UserLocalStorage from "../local_storage/userLocalStorage";
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("DeleteUserService", () => {
|
|
32
|
+
let deleteUserService, apiClientOptions;
|
|
33
|
+
const account = new AccountEntity(defaultAccountDto());
|
|
34
|
+
|
|
35
|
+
beforeEach(async() => {
|
|
36
|
+
apiClientOptions = defaultApiClientOptions();
|
|
37
|
+
deleteUserService = new DeleteUserService(account, apiClientOptions);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("::deleteDryRun", () => {
|
|
41
|
+
it("Do not need to transfer ownership.", async() => {
|
|
42
|
+
expect.assertions(1);
|
|
43
|
+
|
|
44
|
+
const usersId = uuidv4();
|
|
45
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => {});
|
|
46
|
+
|
|
47
|
+
await deleteUserService.deleteDryRun(usersId);
|
|
48
|
+
|
|
49
|
+
expect(deleteUserService.userServiceApi.delete).toHaveBeenCalledWith(usersId, {}, true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("Need to transfer ownership.", async() => {
|
|
53
|
+
expect.assertions(1);
|
|
54
|
+
|
|
55
|
+
const usersId = uuidv4();
|
|
56
|
+
const resourceDto = defaultResourceDto();
|
|
57
|
+
const error = {
|
|
58
|
+
code: 400,
|
|
59
|
+
body: {
|
|
60
|
+
errors: {
|
|
61
|
+
resources: {
|
|
62
|
+
sole_owner: [resourceDto]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => { throw new PassboltApiFetchError("Error", error); });
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await deleteUserService.deleteDryRun(usersId);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
expect(error).toBeInstanceOf(DeleteDryRunError);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("throw any error not handled.", async() => {
|
|
77
|
+
expect.assertions(1);
|
|
78
|
+
|
|
79
|
+
const usersId = uuidv4();
|
|
80
|
+
const error = {
|
|
81
|
+
code: 404,
|
|
82
|
+
};
|
|
83
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => { throw new PassboltApiFetchError("Error", error); });
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await deleteUserService.deleteDryRun(usersId);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
expect(error).toBeInstanceOf(PassboltApiFetchError);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("throws if user id is not an uuid.", async() => {
|
|
93
|
+
expect.assertions(1);
|
|
94
|
+
|
|
95
|
+
const promise = deleteUserService.deleteDryRun({});
|
|
96
|
+
|
|
97
|
+
expect(promise).rejects.toThrow(Error("The parameter \"userId\" should be a UUID"));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("::delete", () => {
|
|
102
|
+
it("delete a user with no transfer.", async() => {
|
|
103
|
+
expect.assertions(2);
|
|
104
|
+
|
|
105
|
+
const usersId = uuidv4();
|
|
106
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => {});
|
|
107
|
+
jest.spyOn(UserLocalStorage, "delete").mockImplementationOnce(() => {});
|
|
108
|
+
|
|
109
|
+
await deleteUserService.delete(usersId, null);
|
|
110
|
+
|
|
111
|
+
expect(deleteUserService.userServiceApi.delete).toHaveBeenCalledWith(usersId, {});
|
|
112
|
+
expect(UserLocalStorage.delete).toHaveBeenCalledWith(usersId);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("delete a user with transfer.", async() => {
|
|
116
|
+
expect.assertions(2);
|
|
117
|
+
|
|
118
|
+
const usersId = uuidv4();
|
|
119
|
+
const dto = defaultUserDeleteTransferDto();
|
|
120
|
+
const userDeleteTransfer = new UserDeleteTransferEntity(dto);
|
|
121
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => {});
|
|
122
|
+
jest.spyOn(UserLocalStorage, "delete").mockImplementationOnce(() => {});
|
|
123
|
+
|
|
124
|
+
await deleteUserService.delete(usersId, userDeleteTransfer);
|
|
125
|
+
|
|
126
|
+
expect(deleteUserService.userServiceApi.delete).toHaveBeenCalledWith(usersId, dto);
|
|
127
|
+
expect(UserLocalStorage.delete).toHaveBeenCalledWith(usersId);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("delete a user with an error and need to transfer ownership.", async() => {
|
|
131
|
+
expect.assertions(1);
|
|
132
|
+
|
|
133
|
+
const usersId = uuidv4();
|
|
134
|
+
const resourceDto = defaultResourceDto();
|
|
135
|
+
const error = {
|
|
136
|
+
code: 400,
|
|
137
|
+
body: {
|
|
138
|
+
errors: {
|
|
139
|
+
resources: {
|
|
140
|
+
sole_owner: [resourceDto]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => { throw new PassboltApiFetchError("Error", error); });
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await deleteUserService.delete(usersId, null);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
expect(error).toBeInstanceOf(DeleteDryRunError);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("delete a user with an unexpected error.", async() => {
|
|
155
|
+
expect.assertions(1);
|
|
156
|
+
|
|
157
|
+
const usersId = uuidv4();
|
|
158
|
+
const error = {
|
|
159
|
+
code: 404,
|
|
160
|
+
};
|
|
161
|
+
jest.spyOn(deleteUserService.userServiceApi, "delete").mockImplementationOnce(() => { throw new PassboltApiFetchError("Error", error); });
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await deleteUserService.delete(usersId, null);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
expect(error).toBeInstanceOf(PassboltApiFetchError);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("throws if user id is not an uuid.", async() => {
|
|
171
|
+
expect.assertions(1);
|
|
172
|
+
|
|
173
|
+
const promise = deleteUserService.delete({});
|
|
174
|
+
|
|
175
|
+
expect(promise).rejects.toThrow(Error("The parameter \"userId\" should be a UUID"));
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
package/src/chrome/manifest.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
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.3.2
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The service aims to create metadata key.
|
|
17
|
+
*/
|
|
18
|
+
export default class EdgeBackgroundPageClipboardService {
|
|
19
|
+
/**
|
|
20
|
+
* @inheritDoc navigator.clipboard.writeText
|
|
21
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText
|
|
22
|
+
*/
|
|
23
|
+
static async writeText(data) {
|
|
24
|
+
const textarea = document.createElement("textarea");
|
|
25
|
+
document.body.appendChild(textarea);
|
|
26
|
+
textarea.value = data;
|
|
27
|
+
textarea.select();
|
|
28
|
+
document.execCommand("cut");
|
|
29
|
+
document.body.removeChild(textarea);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
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.3.2
|
|
13
|
+
*/
|
|
14
|
+
import EdgeBackgroundPageClipboardService from "./edgeBackgroundPageClipboardService";
|
|
15
|
+
|
|
16
|
+
describe("EdgeBackgroundPageClipboardService", () => {
|
|
17
|
+
describe("::writeText", () => {
|
|
18
|
+
it("should write the given data to the clipboard", async() => {
|
|
19
|
+
expect.assertions(8);
|
|
20
|
+
|
|
21
|
+
let copiedValue = "";
|
|
22
|
+
const fakeElement = {
|
|
23
|
+
value: "",
|
|
24
|
+
select: jest.fn().mockImplementation(() => { copiedValue = fakeElement.value; }),
|
|
25
|
+
};
|
|
26
|
+
// Faking document for this specific context
|
|
27
|
+
global.document = {
|
|
28
|
+
createElement: () => fakeElement,
|
|
29
|
+
body: {
|
|
30
|
+
appendChild: jest.fn(),
|
|
31
|
+
removeChild: jest.fn(),
|
|
32
|
+
},
|
|
33
|
+
execCommand: jest.fn()
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
document.execCommand.mockImplementation(() => {});
|
|
37
|
+
|
|
38
|
+
const dataToWrite = "text-to-copy";
|
|
39
|
+
await EdgeBackgroundPageClipboardService.writeText(dataToWrite);
|
|
40
|
+
|
|
41
|
+
expect(copiedValue).toStrictEqual(dataToWrite);
|
|
42
|
+
expect(fakeElement.select).toHaveBeenCalledTimes(1);
|
|
43
|
+
expect(document.body.appendChild).toHaveBeenCalledTimes(1);
|
|
44
|
+
expect(document.body.appendChild).toHaveBeenCalledWith(fakeElement);
|
|
45
|
+
expect(document.body.removeChild).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(document.body.removeChild).toHaveBeenCalledWith(fakeElement);
|
|
47
|
+
expect(document.execCommand).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(document.execCommand).toHaveBeenCalledWith("cut");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/chrome-mv3/index.js
CHANGED
|
@@ -18,9 +18,9 @@ import TabService from "../all/background_page/service/tab/tabService";
|
|
|
18
18
|
import OnExtensionUpdateAvailableService
|
|
19
19
|
from "../all/background_page/service/extension/onExtensionUpdateAvailableService";
|
|
20
20
|
import GlobalAlarmService from "../all/background_page/service/alarm/globalAlarmService";
|
|
21
|
-
import ResponseFetchOffscreenService from "./serviceWorker/service/network/responseFetchOffscreenService";
|
|
22
21
|
import OnStartUpService from "../all/background_page/service/extension/onStartUpService";
|
|
23
22
|
import ToolbarService from "../all/background_page/service/toolbar/toolbarService";
|
|
23
|
+
import HandleOffscreenResponseService from "./serviceWorker/service/offscreen/handleOffscreenResponseService";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Load all system requirement
|
|
@@ -68,9 +68,9 @@ browser.alarms.onAlarm.removeListener(GlobalAlarmService.exec);
|
|
|
68
68
|
browser.alarms.onAlarm.addListener(GlobalAlarmService.exec);
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Handle offscreen
|
|
71
|
+
* Handle offscreen responses.
|
|
72
72
|
*/
|
|
73
|
-
chrome.runtime.onMessage.addListener(
|
|
73
|
+
chrome.runtime.onMessage.addListener(HandleOffscreenResponseService.handleOffscreenResponse);
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Handle suggested resources on toolbar icon
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import
|
|
15
|
+
import HandleOffscreenRequestService from "./service/offscreen/handleOffscreenRequestService";
|
|
16
16
|
|
|
17
|
-
chrome.runtime.onMessage.addListener(
|
|
17
|
+
chrome.runtime.onMessage.addListener(HandleOffscreenRequestService.handleOffscreenRequest);
|
|
@@ -0,0 +1,54 @@
|
|
|
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.3.2
|
|
13
|
+
*/
|
|
14
|
+
export const SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN = "clipboard-write-offscreen";
|
|
15
|
+
export const SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN_RESPONSE_HANDLER = "service-worker-clipboard-write-text-offscreen-response-handler";
|
|
16
|
+
|
|
17
|
+
export default class WriteClipobardOffscreenService {
|
|
18
|
+
/**
|
|
19
|
+
* Handle clipboard request.
|
|
20
|
+
* @param {{clipboardContent: string}} message arguments to pass to the clipboard.writeText.
|
|
21
|
+
* @returns {Promise<object>}
|
|
22
|
+
*/
|
|
23
|
+
static async handleClipboardRequest({clipboardContent}) {
|
|
24
|
+
await WriteClipobardOffscreenService._handleClipboardWriteTextRequest(clipboardContent);
|
|
25
|
+
return WriteClipobardOffscreenService._endClipboardWrite();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Handles and emulates a clipboard::writeText(data)
|
|
30
|
+
* @param {string} clipboardContent
|
|
31
|
+
* @returns {Promise<void>}
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
static async _handleClipboardWriteTextRequest(clipboardContent) {
|
|
35
|
+
const textarea = document.createElement("textarea");
|
|
36
|
+
document.body.appendChild(textarea);
|
|
37
|
+
textarea.value = clipboardContent;
|
|
38
|
+
textarea.select();
|
|
39
|
+
document.execCommand("cut");
|
|
40
|
+
document.body.removeChild(textarea);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Return the clipboard write success data to send back to the requester.
|
|
45
|
+
* @returns {object}
|
|
46
|
+
* @private
|
|
47
|
+
*/
|
|
48
|
+
static _endClipboardWrite() {
|
|
49
|
+
return {
|
|
50
|
+
data: null,
|
|
51
|
+
target: SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN_RESPONSE_HANDLER,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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.3.2
|
|
13
|
+
*/
|
|
14
|
+
import WriteClipobardOffscreenService from "./writeClipobardOffscreenService";
|
|
15
|
+
|
|
16
|
+
export const SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN = "clipboard-write-offscreen";
|
|
17
|
+
export const SEND_MESSAGE_TARGET_CLIPBOARD_WRITE_OFFSCREEN_RESPONSE_HANDLER = "service-worker-clipboard-write-text-offscreen-response-handler";
|
|
18
|
+
|
|
19
|
+
describe("WriteClipobardOffscreenService", () => {
|
|
20
|
+
describe("::handleClipboardRequest", () => {
|
|
21
|
+
it("should run the copy to clipboard and return a message tailored for the requester", async() => {
|
|
22
|
+
expect.assertions(9);
|
|
23
|
+
|
|
24
|
+
let copiedValue = "";
|
|
25
|
+
const fakeElement = {
|
|
26
|
+
value: "",
|
|
27
|
+
select: jest.fn().mockImplementation(() => { copiedValue = fakeElement.value; }),
|
|
28
|
+
};
|
|
29
|
+
global.document = {
|
|
30
|
+
createElement: () => fakeElement,
|
|
31
|
+
body: {
|
|
32
|
+
appendChild: jest.fn(),
|
|
33
|
+
removeChild: jest.fn(),
|
|
34
|
+
},
|
|
35
|
+
execCommand: jest.fn()
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const clipboardContent = "test";
|
|
39
|
+
const result = await WriteClipobardOffscreenService.handleClipboardRequest({clipboardContent});
|
|
40
|
+
const expectedResult = {
|
|
41
|
+
target: "service-worker-clipboard-write-text-offscreen-response-handler",
|
|
42
|
+
data: null,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
expect(copiedValue).toStrictEqual(clipboardContent);
|
|
46
|
+
expect(fakeElement.select).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(document.body.appendChild).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(document.body.appendChild).toHaveBeenCalledWith(fakeElement);
|
|
49
|
+
expect(document.body.removeChild).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(document.body.removeChild).toHaveBeenCalledWith(fakeElement);
|
|
51
|
+
expect(document.execCommand).toHaveBeenCalledTimes(1);
|
|
52
|
+
expect(document.execCommand).toHaveBeenCalledWith("cut");
|
|
53
|
+
expect(result).toStrictEqual(expectedResult);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
|
-
|
|
15
|
-
import Validator from "validator";
|
|
16
14
|
import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
|
|
17
15
|
|
|
18
16
|
export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN = "fetch-offscreen";
|
|
@@ -43,94 +41,88 @@ export default class FetchOffscreenService {
|
|
|
43
41
|
|
|
44
42
|
/**
|
|
45
43
|
* Handle fetch request.
|
|
46
|
-
* @param {
|
|
47
|
-
* @
|
|
44
|
+
* @param {string} id the request id.
|
|
45
|
+
* @param {{resource: string, options: object}} data the fetch parameters
|
|
46
|
+
* @returns {Promise<object>}
|
|
48
47
|
*/
|
|
49
|
-
static async handleFetchRequest(
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
return;
|
|
48
|
+
static async handleFetchRequest({resource, options}) {
|
|
49
|
+
const validationErrors = FetchOffscreenService.validateMessageData(resource, options);
|
|
50
|
+
if (validationErrors) {
|
|
51
|
+
return validationErrors;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
if (!(await FetchOffscreenService.validateMessageData(message.data))) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const {id, resource, options} = message?.data || {};
|
|
60
54
|
// Update the body to fit the data type to send (JSON or FORM DATA)
|
|
61
|
-
options.body = options.body
|
|
55
|
+
options.body = options.body?.dataType === FETCH_OFFSCREEN_DATA_TYPE_JSON ? options.body.data : FormDataUtils.arrayToFormData(options.body.data);
|
|
62
56
|
await FetchOffscreenService.increaseAwaitingRequests();
|
|
63
57
|
try {
|
|
64
58
|
const response = await fetch(resource, options);
|
|
65
|
-
await FetchOffscreenService.handleSuccessResponse(
|
|
59
|
+
return await FetchOffscreenService.handleSuccessResponse(response);
|
|
66
60
|
} catch (error) {
|
|
67
|
-
|
|
61
|
+
console.log(error);
|
|
62
|
+
return FetchOffscreenService.handleErrorResponse(error);
|
|
63
|
+
} finally {
|
|
64
|
+
await FetchOffscreenService.decreaseAwaitingRequests();
|
|
68
65
|
}
|
|
69
|
-
await FetchOffscreenService.decreaseAwaitingRequests();
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
/**
|
|
73
69
|
* Validate message data.
|
|
74
|
-
* @param {
|
|
75
|
-
* @
|
|
70
|
+
* @param {string} resource
|
|
71
|
+
* @param {object} options
|
|
72
|
+
* @returns {object|null}
|
|
76
73
|
*/
|
|
77
|
-
static
|
|
74
|
+
static validateMessageData(resource, options) {
|
|
78
75
|
let error;
|
|
79
|
-
|
|
80
|
-
if (!messageData.id || typeof messageData.id !== "string" || !Validator.isUUID(messageData.id)) {
|
|
81
|
-
error = new Error("FetchOffscreenService: message.id should be a valid uuid.");
|
|
82
|
-
} else if (typeof messageData.resource !== "string") {
|
|
76
|
+
if (typeof resource !== "string") {
|
|
83
77
|
error = new Error("FetchOffscreenService: message.resource should be a valid valid.");
|
|
84
|
-
} else if (typeof
|
|
78
|
+
} else if (typeof options === "undefined" || !(options instanceof Object)) {
|
|
85
79
|
error = new Error("FetchOffscreenService: message.options should be an object.");
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
if (error) {
|
|
89
|
-
|
|
90
|
-
return false;
|
|
83
|
+
return FetchOffscreenService.handleErrorResponse(error);
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
return
|
|
86
|
+
return null;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
/**
|
|
97
90
|
* Handle fetch success, and send response to the service worker.
|
|
98
|
-
* @param {string} id The fetch offscreen request id
|
|
99
91
|
* @param {Response} response The fetch response
|
|
100
92
|
* @returns {Promise<void>}
|
|
93
|
+
* @private
|
|
101
94
|
*/
|
|
102
|
-
static async handleSuccessResponse(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
id: id,
|
|
95
|
+
static async handleSuccessResponse(response) {
|
|
96
|
+
return {
|
|
97
|
+
data: await FetchOffscreenService.serializeResponse(response),
|
|
106
98
|
type: FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS,
|
|
107
|
-
|
|
108
|
-
}
|
|
99
|
+
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER,
|
|
100
|
+
};
|
|
109
101
|
}
|
|
110
102
|
|
|
111
103
|
/**
|
|
112
104
|
* Handle fetch error, and communicate it the service worker.
|
|
113
|
-
* @param {string} id The fetch offscreen request id
|
|
114
105
|
* @param {Error} error The fetch error
|
|
115
|
-
* @returns {
|
|
106
|
+
* @returns {object}
|
|
107
|
+
* @private
|
|
116
108
|
*/
|
|
117
|
-
static
|
|
109
|
+
static handleErrorResponse(error) {
|
|
118
110
|
console.error(error);
|
|
119
|
-
|
|
120
|
-
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER,
|
|
121
|
-
id: id,
|
|
122
|
-
type: FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR,
|
|
111
|
+
return {
|
|
123
112
|
data: {
|
|
124
113
|
name: error?.name,
|
|
125
114
|
message: error?.message || "FetchOffscreenService: an unexpected error occurred"
|
|
126
|
-
}
|
|
127
|
-
|
|
115
|
+
},
|
|
116
|
+
type: FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR,
|
|
117
|
+
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER,
|
|
118
|
+
};
|
|
128
119
|
}
|
|
129
120
|
|
|
130
121
|
/**
|
|
131
122
|
* Serialize the fetch response to return to the service worker.
|
|
132
123
|
* @param {Response} response The response to serialize
|
|
133
124
|
* @returns {Promise<object>}
|
|
125
|
+
* @private
|
|
134
126
|
*/
|
|
135
127
|
static async serializeResponse(response) {
|
|
136
128
|
return {
|