passbolt-browser-extension 4.7.0 → 4.7.3-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -1
- package/RELEASE_NOTES.md +4 -68
- package/package.json +1 -1
- package/src/all/background_page/model/entity/avatar/update/avatarUpdateEntity.js +2 -2
- package/src/all/background_page/service/extension/onExtensionUpdateAvailableService.js +6 -3
- package/src/all/background_page/utils/format/base64.js +50 -22
- package/src/all/background_page/utils/format/formDataUtils.js +93 -0
- package/src/all/background_page/utils/format/formDataUtils.test.data.js +49 -0
- package/src/all/background_page/utils/format/formDataUtils.test.js +105 -0
- package/src/chrome/manifest.json +1 -1
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.js +5 -1
- package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.data.js +6 -3
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.js +17 -10
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.test.js +26 -10
- package/src/firefox/manifest.json +1 -1
- package/src/safari/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file.
|
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
|
+
## [4.7.3] - 2024-05-07
|
|
7
|
+
### Maintenance
|
|
8
|
+
- PB-33235 Convert formData file into a json serializable in offscreen
|
|
9
|
+
- PB-33297 Extension update available should store the state if user signed in
|
|
10
|
+
|
|
11
|
+
## [4.7.1] - 2024-05-02
|
|
12
|
+
### Maintenance
|
|
13
|
+
- PB-33225 MV3 beta rollout
|
|
14
|
+
|
|
6
15
|
## [4.7.0] - 2024-04-26
|
|
7
16
|
### Added
|
|
8
17
|
- PB-32931 As administrator, I see SSO and Directory Sync health checks in Passbolt API Status page
|
|
@@ -1586,7 +1595,9 @@ self registration settings option in the left-side bar
|
|
|
1586
1595
|
- AP: User with plugin installed
|
|
1587
1596
|
- LU: Logged in user
|
|
1588
1597
|
|
|
1589
|
-
[Unreleased]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.
|
|
1598
|
+
[Unreleased]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.3...HEAD
|
|
1599
|
+
[4.7.3]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.1...v4.7.3
|
|
1600
|
+
[4.7.1]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.7.0...v4.7.1
|
|
1590
1601
|
[4.7.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.6.2...v4.7.0
|
|
1591
1602
|
[4.6.2]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.6.0...v4.6.2
|
|
1592
1603
|
[4.6.0]: https://github.com/passbolt/passbolt_browser_extension/compare/v4.5.2...v4.6.0
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,72 +1,8 @@
|
|
|
1
1
|
Song: https://www.youtube.com/watch?v=3L4YrGaR8E4
|
|
2
2
|
|
|
3
|
-
Passbolt v4.7 is a
|
|
4
|
-
Furthermore, this release supports the commitment to improving customization options and integration features, making it easier for organizations to tailor the system to their specific needs.
|
|
5
|
-
|
|
6
|
-
A key enhancement in this release is the ability for administrators to use custom SSL certificates for SMTP and Users directory server connections (PRO only).
|
|
7
|
-
These long-awaited features are particularly beneficial for organizations operating in air-gapped environments or those using their own root CAs, enabling passbolt to more securely integrate with internal communication tools.
|
|
8
|
-
All of these customizations are visible in the API status report of the administration workspace, providing a clear and manageable overview for administrators.
|
|
9
|
-
|
|
10
|
-
Moreover, the integration with user directories has been enhanced, now enabling the synchronization of user accounts using multiple fields as email identifiers.
|
|
11
|
-
This allows organizations with heterogeneous data environments to synchronize more seamlessly with Passbolt.
|
|
12
|
-
This improvement is part of a broader initiative aimed at modernizing the integration with your user directories.
|
|
13
|
-
Stay tuned, more enhancements are planned for future releases.
|
|
14
|
-
|
|
15
|
-
## [4.7.0] - 2024-04-26
|
|
16
|
-
### Added
|
|
17
|
-
- PB-32931 As administrator, I see SSO and Directory Sync health checks in Passbolt API Status page
|
|
18
|
-
- PB-33065 As an administrator I can add a fallback property to map my organisation AD user username
|
|
19
|
-
- PB-33070 Request passphrase when exporting account kit
|
|
20
|
-
|
|
21
|
-
### Fixed
|
|
22
|
-
- PB-32420 Fix double calls to PwnedPassword API service
|
|
23
|
-
- PB-32631 Fix healthCheck Entity to support air gapped instances
|
|
24
|
-
- PB-33066 As AD, I should not see directorySync and SSO checks if they are disabled
|
|
25
|
-
- PB-33067 After an unexpected error during setup, recover or account recovery, only the iframe reload and the port cannot reconnect
|
|
3
|
+
Passbolt v4.7.3 is a test release focused on addressing problems identified with MV3.
|
|
26
4
|
|
|
5
|
+
## [4.7.3] - 2024-05-07
|
|
27
6
|
### Maintenance
|
|
28
|
-
- PB-
|
|
29
|
-
- PB-
|
|
30
|
-
- PB-22644 The passbolt icon should detect if the user is still connected after the service worker awake
|
|
31
|
-
- PB-23928 Handle when the extension is updated, the webIntegration should be destroy and injected again
|
|
32
|
-
- PB-29622 Simulate user keyboard input for autofill event
|
|
33
|
-
- PB-29946 When the service worker is shutdown and a navigation is detected the service worker do not reconnect port and stay in error mode
|
|
34
|
-
- PB-29965 Use a dedicated service to verify the server
|
|
35
|
-
- PB-29966 Update apiClient to support form data body and custom header
|
|
36
|
-
- PB-29967 Use a dedicated service to do the step challenge with the server
|
|
37
|
-
- PB-29968 use a dedicated service to check the user authentication status
|
|
38
|
-
- PB-29969 Use a dedicated service to logout the user
|
|
39
|
-
- PB-29988 Update the alarm in the class StartLoopAuthSessionCheckService to use the property periodInMinutes
|
|
40
|
-
- PB-29989 Put the alarm listener at the top level for the StartLoopAuthSessionCheckService to check the authentication status
|
|
41
|
-
- PB-29990 Move PassphraseStorageService keep alive alarm listener in top level
|
|
42
|
-
- PB-30272 Add message service in the app content script in order to reconnect the port from a message sent by the service worker
|
|
43
|
-
- PB-30273 On the post logout event the service worker should reconnect port that needs to receive the post logout message
|
|
44
|
-
- PB-30274 Add message service in the browser integration content script in order to reconnect the port from a message sent by the service worker
|
|
45
|
-
- PB-30310 Improve invalid groups users sanitization strategy
|
|
46
|
-
- PB-30335 Use timeout instead alarms for service worker
|
|
47
|
-
- PB-30336 Use timeout instead alarms for promise timeout service
|
|
48
|
-
- PB-30337 Put the alarm listener at the top level for the passphraseStorageService to flush passphrase after a time duration
|
|
49
|
-
- PB-30341 Remove alarms for toolbar controller
|
|
50
|
-
- PB-30342 Use timeout instead of alarm for the resource in progress cache service to flush the resource not consumed
|
|
51
|
-
- PB-30374 Check if AuthService from styleguide is still used in the Bext otherwise remove it
|
|
52
|
-
- PB-30375 Improve CI unit test performance by running them in band
|
|
53
|
-
- PB-32291 Cleanup legacy code and unused passbolt.auth.is-authenticated related elements
|
|
54
|
-
- PB-32335 Split PassphraseStorageService to put the KeepSessionAlive feature on its own service
|
|
55
|
-
- PB-32345 Ensures on the desktop app during import account that the file to import is taken into account
|
|
56
|
-
- PB-32597 Ensure ToolbarController are set on index.js
|
|
57
|
-
- PB-32598 Ensure add listener from authentication event controller are set on index.js
|
|
58
|
-
- PB-32599 Ensure add listener from StartLoopAuthSessionCheckService are set on index.js
|
|
59
|
-
- PB-32604 Ensure add listener from on extension update available controller are set on index.js
|
|
60
|
-
- PB-32602 Ensure add listener from user.js are set on index.js
|
|
61
|
-
- PB-32603 Ensure add listener from ResourceInProgressCacheService are set on index.js
|
|
62
|
-
- PB-32915 Update code to remove the destruction of the public web sign-in on port disconnected
|
|
63
|
-
- PB-32916 Update code to remove the destruction of the setup on port disconnected
|
|
64
|
-
- PB-32917 Update code to remove the destruction of the recover on port disconnected
|
|
65
|
-
- PB-33018 Automate browser extension npm publication
|
|
66
|
-
- PB-33024 Ensure only stable tags of the styleguide are published to npm
|
|
67
|
-
- PB-33024 Ensure only stable tag of the browser extension are sent for review or publish to the store
|
|
68
|
-
- PB-33061 Create account temporary storage
|
|
69
|
-
- PB-33062 Use temporary account storage for setup process
|
|
70
|
-
- PB-33063 Use temporary account storage for recover process
|
|
71
|
-
- PB-33064 Use temporary account storage for account recovery process
|
|
72
|
-
- PB-33068 Remove beta information for the windows app
|
|
7
|
+
- PB-33235 Convert formData file into a json serializable in offscreen
|
|
8
|
+
- PB-33297 Extension update available should store the state if user signed in
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import Entity from "passbolt-styleguide/src/shared/models/entity/abstract/entity";
|
|
15
15
|
import EntitySchema from "passbolt-styleguide/src/shared/models/entity/abstract/entitySchema";
|
|
16
|
-
import
|
|
16
|
+
import Base64Utils from "../../../../utils/format/base64";
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
const ENTITY_NAME = 'AvatarUpdate';
|
|
@@ -47,7 +47,7 @@ class AvatarUpdateEntity extends Entity {
|
|
|
47
47
|
const filename = avatarBase64UpdateDto.filename;
|
|
48
48
|
const fileBase64 = avatarBase64UpdateDto.fileBase64;
|
|
49
49
|
const mimeType = avatarBase64UpdateDto.mimeType;
|
|
50
|
-
const file =
|
|
50
|
+
const file = Base64Utils.base64ToBlob(fileBase64, mimeType);
|
|
51
51
|
const avatarUpdateDto = {file: file, filename: filename, mimeType: mimeType};
|
|
52
52
|
return new AvatarUpdateEntity(avatarUpdateDto);
|
|
53
53
|
}
|
|
@@ -20,6 +20,8 @@ import WebIntegrationPagemod from "../../pagemod/webIntegrationPagemod";
|
|
|
20
20
|
import WorkerService from "../worker/workerService";
|
|
21
21
|
import PublicWebsiteSignInPagemod from "../../pagemod/publicWebsiteSignInPagemod";
|
|
22
22
|
|
|
23
|
+
const PASSBOLT_EXTENSION_UPDATE = "passboltExtensionUpdate";
|
|
24
|
+
|
|
23
25
|
class OnExtensionUpdateAvailableService {
|
|
24
26
|
/**
|
|
25
27
|
* Execute the OnExtensionUpdateAvailableService process
|
|
@@ -27,7 +29,7 @@ class OnExtensionUpdateAvailableService {
|
|
|
27
29
|
*/
|
|
28
30
|
static async exec() {
|
|
29
31
|
if (await isUserAuthenticated()) {
|
|
30
|
-
|
|
32
|
+
await browser.storage.session.set({[PASSBOLT_EXTENSION_UPDATE]: true});
|
|
31
33
|
} else {
|
|
32
34
|
await this.cleanAndReload();
|
|
33
35
|
}
|
|
@@ -47,8 +49,9 @@ class OnExtensionUpdateAvailableService {
|
|
|
47
49
|
* It triggers a runtime reload if an extension update was available while the user was signed in.
|
|
48
50
|
*/
|
|
49
51
|
static async handleUserLoggedOut() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
const shouldUpdate = await browser.storage.session.get(PASSBOLT_EXTENSION_UPDATE);
|
|
53
|
+
if (shouldUpdate && shouldUpdate[PASSBOLT_EXTENSION_UPDATE]) {
|
|
54
|
+
await browser.storage.session.remove(PASSBOLT_EXTENSION_UPDATE);
|
|
52
55
|
await this.cleanAndReload();
|
|
53
56
|
}
|
|
54
57
|
}
|
|
@@ -1,33 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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 4.8.0
|
|
8
13
|
*/
|
|
9
|
-
function b64ToBlob(b64Data, contentType, sliceSize) {
|
|
10
|
-
contentType = contentType || '';
|
|
11
|
-
sliceSize = sliceSize || 512;
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The class that deals with Passbolt to convert base64.
|
|
17
|
+
*/
|
|
18
|
+
class Base64Utils {
|
|
19
|
+
/**
|
|
20
|
+
* Transforms a base 64 encoded file content into a file object.
|
|
21
|
+
* Useful when we need to transmit a file from the content code to the add-on code.
|
|
22
|
+
* @param {string} b64Data The base64 data
|
|
23
|
+
* @param {string} contentType The content type
|
|
24
|
+
* @param {number} sliceSize The slice size
|
|
25
|
+
* @returns {*}
|
|
26
|
+
*/
|
|
27
|
+
static base64ToBlob(b64Data, contentType = "", sliceSize = 512) {
|
|
28
|
+
const byteCharacters = atob(b64Data);
|
|
29
|
+
const byteArrays = [];
|
|
15
30
|
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
|
|
32
|
+
const slice = byteCharacters.slice(offset, offset + sliceSize);
|
|
18
33
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
const byteNumbers = new Array(slice.length);
|
|
35
|
+
for (let i = 0; i < slice.length; i++) {
|
|
36
|
+
byteNumbers[i] = slice.charCodeAt(i);
|
|
37
|
+
}
|
|
23
38
|
|
|
24
|
-
|
|
39
|
+
const byteArray = new Uint8Array(byteNumbers);
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
byteArrays.push(byteArray);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new Blob(byteArrays, {type: contentType});
|
|
27
45
|
}
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Transforms a file object into a base 64 encoded file content.
|
|
49
|
+
* @param {Blob} blob
|
|
50
|
+
* @returns {Promise<string>}
|
|
51
|
+
*/
|
|
52
|
+
static blobToBase64(blob) {
|
|
53
|
+
return new Promise(resolve => {
|
|
54
|
+
const reader = new FileReader();
|
|
55
|
+
reader.onloadend = () => resolve(reader.result);
|
|
56
|
+
reader.readAsDataURL(blob);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
31
59
|
}
|
|
32
60
|
|
|
33
|
-
export default
|
|
61
|
+
export default Base64Utils;
|
|
@@ -0,0 +1,93 @@
|
|
|
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 4.8.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import Base64Utils from "./base64";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The class that deals with Passbolt to convert formData.
|
|
19
|
+
*/
|
|
20
|
+
class FormDataUtils {
|
|
21
|
+
/**
|
|
22
|
+
* Transform a form data to an array of object
|
|
23
|
+
* @param {FormData} formData The form data
|
|
24
|
+
* @return {Promise<Array<Object>>}
|
|
25
|
+
*/
|
|
26
|
+
static async formDataToArray(formData) {
|
|
27
|
+
const formDataSerialized = [];
|
|
28
|
+
for (const [key, value] of formData.entries()) {
|
|
29
|
+
const formDataObject = {
|
|
30
|
+
key: key
|
|
31
|
+
};
|
|
32
|
+
// BLOB in FormData is transformed into a File
|
|
33
|
+
if (value instanceof File) {
|
|
34
|
+
formDataObject.value = await Base64Utils.blobToBase64(value);
|
|
35
|
+
formDataObject.name = value.name;
|
|
36
|
+
formDataObject.type = FormDataUtils.TYPE_FILE;
|
|
37
|
+
} else {
|
|
38
|
+
formDataObject.value = value;
|
|
39
|
+
formDataObject.type = FormDataUtils.TYPE_SCALAR;
|
|
40
|
+
}
|
|
41
|
+
formDataSerialized.push(formDataObject);
|
|
42
|
+
}
|
|
43
|
+
return formDataSerialized;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Transform an array of object to a form data
|
|
48
|
+
* @param {Array<Object>} array
|
|
49
|
+
* @return {FormData}
|
|
50
|
+
*/
|
|
51
|
+
static arrayToFormData(array) {
|
|
52
|
+
const formData = new FormData();
|
|
53
|
+
array.forEach(data => {
|
|
54
|
+
if (data.type === FormDataUtils.TYPE_SCALAR) {
|
|
55
|
+
formData.append(data.key, data.value);
|
|
56
|
+
} else {
|
|
57
|
+
const base64UrlSplit = data.value.split(',');
|
|
58
|
+
const blobBase64 = base64UrlSplit[1];
|
|
59
|
+
const mimeType = base64UrlSplit[0].split(':')[1].split(';')[0];
|
|
60
|
+
const blob = Base64Utils.base64ToBlob(blobBase64, mimeType);
|
|
61
|
+
formData.append(data.key, blob, data.name);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return formData;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the type scalar
|
|
69
|
+
* @return {string}
|
|
70
|
+
*/
|
|
71
|
+
static get TYPE_SCALAR() {
|
|
72
|
+
return "SCALAR";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the type file
|
|
77
|
+
* @return {string}
|
|
78
|
+
*/
|
|
79
|
+
static get TYPE_FILE() {
|
|
80
|
+
return "FILE";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get the type blob
|
|
85
|
+
* @return {string}
|
|
86
|
+
* @constructor
|
|
87
|
+
*/
|
|
88
|
+
static get TYPE_BLOB() {
|
|
89
|
+
return "FILE";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default FormDataUtils;
|
|
@@ -0,0 +1,49 @@
|
|
|
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 4.8.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const formDataString = () => {
|
|
16
|
+
const formDataBody = new FormData();
|
|
17
|
+
formDataBody.append("prop1", "value 1");
|
|
18
|
+
formDataBody.append("prop1", "value 2");
|
|
19
|
+
return formDataBody;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const formDataFile = () => {
|
|
23
|
+
const formDataBody = new FormData();
|
|
24
|
+
const file1 = new File(['test'], "file 1", {type: 'image/png'});
|
|
25
|
+
const file2 = new File(['test'], "file 2", {type: 'image/png'});
|
|
26
|
+
formDataBody.append("file", file1, "file 1");
|
|
27
|
+
formDataBody.append("file", file2, "file 2");
|
|
28
|
+
return formDataBody;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const formDataBlob = () => {
|
|
32
|
+
const formDataBody = new FormData();
|
|
33
|
+
const blob1 = new Blob(['test'], {type: 'text/plain'});
|
|
34
|
+
const blob2 = new Blob(['test'], {type: 'text/plain'});
|
|
35
|
+
formDataBody.append("blob", blob1, "blob 1");
|
|
36
|
+
formDataBody.append("blob", blob2, "blob 2");
|
|
37
|
+
return formDataBody;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const formDataMixed = () => {
|
|
41
|
+
const formDataBody = new FormData();
|
|
42
|
+
formDataBody.append("prop1", "value 1");
|
|
43
|
+
const file = new File(['test'], "file 1", {type: 'image/png'});
|
|
44
|
+
formDataBody.append("file", file, "file 1");
|
|
45
|
+
const blob = new Blob(['test'], {type: 'text/plain'});
|
|
46
|
+
formDataBody.append("blob", blob, "blob 1");
|
|
47
|
+
return formDataBody;
|
|
48
|
+
};
|
|
49
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
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 4.8.0
|
|
13
|
+
*/
|
|
14
|
+
import {formDataMixed, formDataString} from "./formDataUtils.test.data";
|
|
15
|
+
import FormDataUtils from "./formDataUtils";
|
|
16
|
+
import {formDataBlob, formDataFile} from "./formDataUtils.test.data";
|
|
17
|
+
|
|
18
|
+
describe("FormDataUtils", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.resetModules();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("FormDataUtils::formDataToArray", () => {
|
|
24
|
+
it("Should create an array of scalar object", async() => {
|
|
25
|
+
expect.assertions(1);
|
|
26
|
+
// data mocked
|
|
27
|
+
const formData = formDataString();
|
|
28
|
+
// process
|
|
29
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
30
|
+
// expectations
|
|
31
|
+
const expectedArray = [
|
|
32
|
+
{key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
|
|
33
|
+
{key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}
|
|
34
|
+
];
|
|
35
|
+
expect(arrayObject).toStrictEqual(expectedArray);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("Should create an array of file object", async() => {
|
|
39
|
+
expect.assertions(1);
|
|
40
|
+
// data mocked
|
|
41
|
+
const formData = formDataFile();
|
|
42
|
+
// process
|
|
43
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
44
|
+
// expectations
|
|
45
|
+
const expectedArray = [
|
|
46
|
+
{key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 1", type: FormDataUtils.TYPE_FILE},
|
|
47
|
+
{key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 2", type: FormDataUtils.TYPE_FILE}
|
|
48
|
+
];
|
|
49
|
+
expect(arrayObject).toStrictEqual(expectedArray);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("Should create an array of blob object", async() => {
|
|
53
|
+
expect.assertions(1);
|
|
54
|
+
// data mocked
|
|
55
|
+
const formData = formDataBlob();
|
|
56
|
+
// process
|
|
57
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
58
|
+
// expectations
|
|
59
|
+
const expectedArray = [
|
|
60
|
+
{key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 1", type: FormDataUtils.TYPE_BLOB},
|
|
61
|
+
{key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 2", type: FormDataUtils.TYPE_BLOB}
|
|
62
|
+
];
|
|
63
|
+
expect(arrayObject).toStrictEqual(expectedArray);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("Should create an array of mixed object", async() => {
|
|
67
|
+
expect.assertions(1);
|
|
68
|
+
// data mocked
|
|
69
|
+
const formData = formDataMixed();
|
|
70
|
+
// process
|
|
71
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
72
|
+
// expectations
|
|
73
|
+
const expectedArray = [
|
|
74
|
+
{key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
|
|
75
|
+
{key: "file", value: "data:image/png;base64,dGVzdA==", name: "file 1", type: FormDataUtils.TYPE_FILE},
|
|
76
|
+
{key: "blob", value: "data:text/plain;base64,dGVzdA==", name: "blob 1", type: FormDataUtils.TYPE_BLOB}
|
|
77
|
+
];
|
|
78
|
+
expect(arrayObject).toStrictEqual(expectedArray);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("FormDataUtils::arrayToFormData", () => {
|
|
83
|
+
it("should form the same formData string from the origin", async() => {
|
|
84
|
+
expect.assertions(1);
|
|
85
|
+
// data mocked
|
|
86
|
+
const formData = formDataString();
|
|
87
|
+
// process
|
|
88
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
89
|
+
const formDataReceived = FormDataUtils.arrayToFormData(arrayObject);
|
|
90
|
+
// expectations
|
|
91
|
+
expect(formData).toStrictEqual(formDataReceived);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should form the same formData mixed from the origin", async() => {
|
|
95
|
+
expect.assertions(1);
|
|
96
|
+
// data mocked
|
|
97
|
+
const formData = formDataMixed();
|
|
98
|
+
// process
|
|
99
|
+
const arrayObject = await FormDataUtils.formDataToArray(formData);
|
|
100
|
+
const formDataReceived = FormDataUtils.arrayToFormData(arrayObject);
|
|
101
|
+
// expectations
|
|
102
|
+
expect(formData).toStrictEqual(formDataReceived);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
package/src/chrome/manifest.json
CHANGED
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import Validator from "validator";
|
|
16
|
+
import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
|
|
16
17
|
|
|
17
18
|
export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN = "fetch-offscreen";
|
|
18
19
|
export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_RESPONSE_HANDLER = "service-worker-fetch-offscreen-response-handler";
|
|
19
20
|
export const SEND_MESSAGE_TARGET_FETCH_OFFSCREEN_POLLING_HANDLER = "service-worker-fetch-offscreen-polling-handler";
|
|
20
21
|
export const FETCH_OFFSCREEN_RESPONSE_TYPE_SUCCESS = "success";
|
|
21
22
|
export const FETCH_OFFSCREEN_RESPONSE_TYPE_ERROR = "error";
|
|
23
|
+
export const FETCH_OFFSCREEN_DATA_TYPE_JSON = "JSON";
|
|
24
|
+
export const FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA = "FORM_DATA";
|
|
22
25
|
const POLLING_COUNTER_UPDATE_LOCK = "POLLING_COUNTER_UPDATE_LOCK";
|
|
23
26
|
const POLLING_PERIOD = 25_000;
|
|
24
27
|
|
|
@@ -54,7 +57,8 @@ export default class FetchOffscreenService {
|
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
59
|
const {id, resource, options} = message?.data || {};
|
|
57
|
-
|
|
60
|
+
// Update the body to fit the data type to send (JSON or FORM DATA)
|
|
61
|
+
options.body = options.body.dataType === FETCH_OFFSCREEN_DATA_TYPE_JSON ? options.body.data : FormDataUtils.arrayToFormData(options.body.data);
|
|
58
62
|
await FetchOffscreenService.increaseAwaitingRequests();
|
|
59
63
|
try {
|
|
60
64
|
const response = await fetch(resource, options);
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
14
|
import {fetchOptionsHeaders} from "../../../serviceWorker/service/network/requestFetchOffscreenService.test.data";
|
|
15
|
-
import {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "./fetchOffscreenService";
|
|
15
|
+
import {FETCH_OFFSCREEN_DATA_TYPE_JSON, SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} from "./fetchOffscreenService";
|
|
16
16
|
|
|
17
17
|
export const defaultFetchMessage = message => ({
|
|
18
18
|
target: SEND_MESSAGE_TARGET_FETCH_OFFSCREEN,
|
|
@@ -23,8 +23,11 @@ export const defaultFetchMessage = message => ({
|
|
|
23
23
|
credentials: "include",
|
|
24
24
|
headers: fetchOptionsHeaders(),
|
|
25
25
|
body: {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
data: {
|
|
27
|
+
prop1: "value 1",
|
|
28
|
+
prop2: "value 2"
|
|
29
|
+
},
|
|
30
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
},
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
12
|
* @since 4.7.0
|
|
13
13
|
*/
|
|
14
|
+
import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
|
|
15
|
+
import {
|
|
16
|
+
FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA,
|
|
17
|
+
FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
18
|
+
} from "../../../offscreens/service/network/fetchOffscreenService";
|
|
14
19
|
|
|
15
20
|
const {SEND_MESSAGE_TARGET_FETCH_OFFSCREEN} = require("../../../offscreens/service/network/fetchOffscreenService");
|
|
16
21
|
|
|
@@ -92,7 +97,7 @@ export class RequestFetchOffscreenService {
|
|
|
92
97
|
RequestFetchOffscreenService.createIfNotExistOffscreenDocument);
|
|
93
98
|
|
|
94
99
|
const offscreenFetchId = crypto.randomUUID();
|
|
95
|
-
const offscreenFetchData = RequestFetchOffscreenService.buildOffscreenData(offscreenFetchId, resource, options);
|
|
100
|
+
const offscreenFetchData = await RequestFetchOffscreenService.buildOffscreenData(offscreenFetchId, resource, options);
|
|
96
101
|
|
|
97
102
|
return new Promise((resolve, reject) => {
|
|
98
103
|
// Stack the response listener callbacks.
|
|
@@ -130,19 +135,21 @@ export class RequestFetchOffscreenService {
|
|
|
130
135
|
* @param {object} fetchOptions The fetch options, similar to the native fetch option parameter.
|
|
131
136
|
* @returns {object}
|
|
132
137
|
*/
|
|
133
|
-
static buildOffscreenData(id, resource, fetchOptions = {}) {
|
|
138
|
+
static async buildOffscreenData(id, resource, fetchOptions = {}) {
|
|
134
139
|
const options = JSON.parse(JSON.stringify(fetchOptions));
|
|
135
140
|
|
|
136
141
|
// Format FormData fetch options to allow its serialization.
|
|
137
142
|
if (fetchOptions?.body instanceof FormData) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
options.
|
|
145
|
-
|
|
143
|
+
const formDataSerialized = await FormDataUtils.formDataToArray(fetchOptions.body);
|
|
144
|
+
options.body = {
|
|
145
|
+
data: formDataSerialized,
|
|
146
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
|
|
147
|
+
};
|
|
148
|
+
} else {
|
|
149
|
+
options.body = {
|
|
150
|
+
data: fetchOptions.body,
|
|
151
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
152
|
+
};
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
return {id, resource, options};
|
|
@@ -19,8 +19,12 @@ import {
|
|
|
19
19
|
IS_FETCH_OFFSCREEN_PREFERRED_STORAGE_KEY,
|
|
20
20
|
RequestFetchOffscreenService
|
|
21
21
|
} from "./requestFetchOffscreenService";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA, FETCH_OFFSCREEN_DATA_TYPE_JSON,
|
|
24
|
+
SEND_MESSAGE_TARGET_FETCH_OFFSCREEN
|
|
25
|
+
} from "../../../offscreens/service/network/fetchOffscreenService";
|
|
23
26
|
import {fetchOptionsWithBodyFormData, fetchOptionWithBodyData} from "./requestFetchOffscreenService.test.data";
|
|
27
|
+
import FormDataUtils from "../../../../all/background_page/utils/format/formDataUtils";
|
|
24
28
|
|
|
25
29
|
beforeEach(() => {
|
|
26
30
|
enableFetchMocks();
|
|
@@ -108,19 +112,24 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
108
112
|
const id = crypto.randomUUID();
|
|
109
113
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
110
114
|
const options = fetchOptionWithBodyData();
|
|
111
|
-
const offscreenData = RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
|
|
115
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
|
|
116
|
+
options.body = {
|
|
117
|
+
data: options.body,
|
|
118
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_JSON
|
|
119
|
+
}
|
|
112
120
|
// Ensure body remains a form data after serialization.
|
|
113
121
|
expect(offscreenData).toEqual({id, resource, options});
|
|
114
122
|
});
|
|
115
123
|
|
|
116
124
|
it("should ensure given fetch options body will not be altered", async() => {
|
|
117
|
-
expect.assertions(
|
|
125
|
+
expect.assertions(2);
|
|
118
126
|
const id = crypto.randomUUID();
|
|
119
127
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
120
128
|
const fetchOptions = fetchOptionsWithBodyFormData();
|
|
121
|
-
RequestFetchOffscreenService.buildOffscreenData(id, resource, fetchOptions);
|
|
129
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, fetchOptions);
|
|
122
130
|
// Ensure body remains a form data after serialization.
|
|
123
|
-
expect(
|
|
131
|
+
expect(offscreenData.options.body.data).toBeInstanceOf(Array);
|
|
132
|
+
expect(offscreenData.options.body.dataType).toStrictEqual(FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA);
|
|
124
133
|
});
|
|
125
134
|
|
|
126
135
|
it("should transform FormData body into serialized encoded url parameters", async() => {
|
|
@@ -129,7 +138,7 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
129
138
|
const resource = "https://test.passbolt.com/passbolt-unit-test/test.json";
|
|
130
139
|
const options = fetchOptionsWithBodyFormData();
|
|
131
140
|
|
|
132
|
-
const offscreenData = RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
|
|
141
|
+
const offscreenData = await RequestFetchOffscreenService.buildOffscreenData(id, resource, options);
|
|
133
142
|
// eslint-disable-next-line object-shorthand
|
|
134
143
|
const expectedOffscreenMessageData = {
|
|
135
144
|
id,
|
|
@@ -138,9 +147,11 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
138
147
|
...options,
|
|
139
148
|
headers: {
|
|
140
149
|
...options.headers,
|
|
141
|
-
"Content-type": "application/x-www-form-urlencoded" // ensure the content type reflect the body type parameter.
|
|
142
150
|
},
|
|
143
|
-
body:
|
|
151
|
+
body: {
|
|
152
|
+
data: [{key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR}, {key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}],
|
|
153
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
|
|
154
|
+
} // ensure the body is serialized as url encoded parameter
|
|
144
155
|
}
|
|
145
156
|
};
|
|
146
157
|
expect(offscreenData).toEqual(expectedOffscreenMessageData);
|
|
@@ -172,9 +183,14 @@ describe("RequestFetchOffscreenService", () => {
|
|
|
172
183
|
...message.data.options,
|
|
173
184
|
headers: {
|
|
174
185
|
...message.data.options.headers,
|
|
175
|
-
"Content-type": "application/x-www-form-urlencoded" // ensure the content type reflect the body type parameter.
|
|
176
186
|
},
|
|
177
|
-
body:
|
|
187
|
+
body: {
|
|
188
|
+
data: [
|
|
189
|
+
{key: "prop1", value: "value 1", type: FormDataUtils.TYPE_SCALAR},
|
|
190
|
+
{key: "prop1", value: "value 2", type: FormDataUtils.TYPE_SCALAR}
|
|
191
|
+
],
|
|
192
|
+
dataType: FETCH_OFFSCREEN_DATA_TYPE_FORM_DATA
|
|
193
|
+
}, // ensure the body is serialized as url encoded parameter
|
|
178
194
|
}
|
|
179
195
|
},
|
|
180
196
|
};
|