passbolt-browser-extension 5.2.0-rc.1 → 5.3.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 +49 -0
- package/RELEASE_NOTES.md +53 -4
- package/doc/browser-extension-class-diagram.md +9 -0
- package/package.json +2 -2
- package/src/all/_locales/sl/messages.json +2 -2
- package/src/all/background_page/controller/resource/updateResourceLocalStorageByFolderParentIdController.js +70 -0
- package/src/all/background_page/controller/resource/updateResourceLocalStorageByFolderParentIdController.test.js +78 -0
- package/src/all/background_page/controller/share/shareOneFolderController.test.js +2 -1
- package/src/all/background_page/controller/share/shareResourcesController.test.js +1 -1
- package/src/all/background_page/event/resourceEvents.js +11 -0
- package/src/all/background_page/model/entity/resource/resourcesCollection.js +25 -0
- package/src/all/background_page/model/entity/resource/resourcesCollection.test.js +34 -0
- package/src/all/background_page/service/api/resource/resourceService.js +1 -0
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.js +52 -1
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.test.js +100 -0
- package/src/all/background_page/service/resource/findResourcesService.js +53 -19
- package/src/all/background_page/service/resource/findResourcesService.test.js +191 -0
- package/src/all/background_page/service/resourceType/updateResourceTypesService.test.js +1 -1
- package/src/all/background_page/service/share/shareResourceService.test.js +1 -1
- package/src/all/locales/de-DE/common.json +2 -2
- package/src/all/locales/es-ES/common.json +2 -2
- package/src/all/locales/fr-FR/common.json +5 -5
- package/src/all/locales/it-IT/common.json +2 -2
- package/src/all/locales/ja-JP/common.json +2 -2
- package/src/all/locales/ko-KR/common.json +2 -2
- package/src/all/locales/lt-LT/common.json +2 -2
- package/src/all/locales/nl-NL/common.json +2 -2
- package/src/all/locales/pl-PL/common.json +5 -5
- package/src/all/locales/pt-BR/common.json +2 -2
- package/src/all/locales/ro-RO/common.json +2 -2
- package/src/all/locales/ru-RU/common.json +2 -2
- package/src/all/locales/sl-SI/common.json +33 -33
- package/src/all/locales/sv-SE/common.json +2 -2
- package/src/all/locales/uk-UA/common.json +3 -3
- package/src/chrome/manifest.json +1 -1
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/firefox/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,55 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [5.3.0] - 2025-06-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- PB-43269 Create the entity CustomFieldEntity
|
|
11
|
+
- PB-43271 Create the entity collection CustomFieldsCollection
|
|
12
|
+
- PB-43273 Create the entity SecretDataV5StandaloneCustomFieldsCollection
|
|
13
|
+
- PB-43275 Update the resource types schema definitions
|
|
14
|
+
- PB-43277 Update the ResourceMetadataEntity
|
|
15
|
+
- PB-43278 Update the ResourceFormEntity
|
|
16
|
+
- PB-43279 Update the Secret Entities
|
|
17
|
+
- PB-43283 Display a new entry the create/edit dialog to set custom fields on the left sidebar and the menu
|
|
18
|
+
- PB-43284 Create the CustomFieldForm for the create/edit dialog
|
|
19
|
+
- PB-43285 Handle the CustomFieldForm warnings and limitation
|
|
20
|
+
- PB-43286 Update create/edit resource to select secret custom fields for a standalone custom fields
|
|
21
|
+
- PB-43287 Display the Custom Fields section on the right sidebar
|
|
22
|
+
- PB-43289 Display standalone custom fields in the component DisplayResourceCreationMenu
|
|
23
|
+
- PB-43290 Display standalone custom fields in the component DisplayResourcesWorkspaceMainMenu
|
|
24
|
+
- PB-43291 Display the URIs section in the right sidebar
|
|
25
|
+
- PB-43374 Add validation on keys and values of each elements of custom fields for the resource form entity
|
|
26
|
+
- PB-43377 Add set collection into entity v2
|
|
27
|
+
- PB-43145 Find a list of resources based on IDs and that are suitable for local storage from the API
|
|
28
|
+
- PB-43146 Find a list of resources based on a parent folder id and that are suitable for the local storage from the API
|
|
29
|
+
- PB-43133 Display padding below tags in resource workspace left sidebar
|
|
30
|
+
- PB-42185 The folder caret that expands or collapses folders in the tree should have a larger clickable area to make it easier to use
|
|
31
|
+
- PB-43222 Improve the group dialog to match the new share dimensions
|
|
32
|
+
- PB-43147 Find and update resources based on parent folder id for the local storage
|
|
33
|
+
- PB-43148 Create a connector for finding resources based on a parent id for the styleguide to call it later
|
|
34
|
+
- PB-43149 Create a ResourcesServiceWorkerService to call the service worker for resource related operations
|
|
35
|
+
- PB-43150 Implement the optimised call in the Styleguide when filtering by a folder
|
|
36
|
+
- PB-43151 Optimise the data retrieved from the API such that updates are not done if unnecessary
|
|
37
|
+
- PB-43156 Clarify implications for backups and other devices before changing the passphrase in the user settings workspace
|
|
38
|
+
- PB-43489 Display unexpected error if there is any issue during the secret decryption
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- PB-43109 Fix: from the sidebar when upgrade from v4 to v5 goes wrong the error message in the notification
|
|
42
|
+
- PB-43118 Hide the "Share metadata keys" button in the users workspace action bar for the current signed-in user
|
|
43
|
+
- PB-43215 Fix account recovery creator name
|
|
44
|
+
- PB-43063 Fix group edit dialog double warning message has broken UI
|
|
45
|
+
- PB-43117 Hide the "Share metadata keys" button in the users workspace action bar after sharing missing metadata keys with a user
|
|
46
|
+
- PB-43064 Fix group edit dialog can show a mix of error and warning messages
|
|
47
|
+
- PB-43150: fix folder not being reloaded
|
|
48
|
+
- PB-43424 Clicking on the "open in a new tab” call to action in the quick application should open the resource url in a new tab
|
|
49
|
+
- PB-43108 Display attention required icon on "metadata keys" label in the user details sidebar if the user is not having access to some metadata keys
|
|
50
|
+
- PB-43217 The default icon stroke width is too thick in the grid and doesn't match the custom icons
|
|
51
|
+
- PB-43220 Copy URL field action button lacks padding and is broken in the SSO settings
|
|
52
|
+
- PB-43168 Align vertically resources workspace select check-boxes
|
|
53
|
+
- PB-43211 The feedback message notifying the administrator when a metadata key has been shared with a user contains a typo
|
|
54
|
+
- PB-43471 Center vertically the icon on the create and edit dialog
|
|
55
|
+
|
|
7
56
|
## [5.2.0] - 2025-06-04
|
|
8
57
|
|
|
9
58
|
### Added
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,7 +1,56 @@
|
|
|
1
|
-
|
|
1
|
+
Release song: https://www.youtube.com/watch?v=ubWL8VAPoYw
|
|
2
2
|
|
|
3
|
-
Passbolt
|
|
4
|
-
|
|
3
|
+
Passbolt 5.3 adds custom fields, one of the five most‑requested features from the community. Built on top of encrypted‑metadata introduced earlier this year, custom fields let users attach additional key‑value pairs to a resource or as a standalone one. Typical use‑cases include centralising CI/CD job variables and storing environment‑specific configuration values that need more structure than a general note.
|
|
4
|
+
|
|
5
|
+
Custom fields rely on encrypted metadata, therefore the feature is still in beta and is not yet available on Passbolt Cloud. A step‑by‑step guide on how to enable the encrypted metadata on a self‑hosted instance will be available in a blog post that will be published soon. The encrypted‑metadata feature is scheduled to be marked as stable in Passbolt 5.4, planned for August 2025.
|
|
6
|
+
|
|
7
|
+
As part of our continuous performance work, this release concentrates on folder browsing. Loading folders and their resources is now faster and reduces the load on API and client, improving day-to-day usability for organizations having thousands of credentials under management.
|
|
8
|
+
|
|
9
|
+
Several bugs reported by the community have also been fixed. As always, thank you to everyone who took the time to file issues, test patches and suggest improvements. For a complete list of changes, consult the changelog.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- PB-43269 Create the entity CustomFieldEntity
|
|
13
|
+
- PB-43271 Create the entity collection CustomFieldsCollection
|
|
14
|
+
- PB-43273 Create the entity SecretDataV5StandaloneCustomFieldsCollection
|
|
15
|
+
- PB-43275 Update the resource types schema definitions
|
|
16
|
+
- PB-43277 Update the ResourceMetadataEntity
|
|
17
|
+
- PB-43278 Update the ResourceFormEntity
|
|
18
|
+
- PB-43279 Update the Secret Entities
|
|
19
|
+
- PB-43283 Display a new entry the create/edit dialog to set custom fields on the left sidebar and the menu
|
|
20
|
+
- PB-43284 Create the CustomFieldForm for the create/edit dialog
|
|
21
|
+
- PB-43285 Handle the CustomFieldForm warnings and limitation
|
|
22
|
+
- PB-43286 Update create/edit resource to select secret custom fields for a standalone custom fields
|
|
23
|
+
- PB-43287 Display the Custom Fields section on the right sidebar
|
|
24
|
+
- PB-43289 Display standalone custom fields in the component DisplayResourceCreationMenu
|
|
25
|
+
- PB-43290 Display standalone custom fields in the component DisplayResourcesWorkspaceMainMenu
|
|
26
|
+
- PB-43291 Display the URIs section in the right sidebar
|
|
27
|
+
- PB-43374 Add validation on keys and values of each elements of custom fields for the resource form entity
|
|
28
|
+
- PB-43377 Add set collection into entity v2
|
|
29
|
+
- PB-43145 Find a list of resources based on IDs and that are suitable for local storage from the API
|
|
30
|
+
- PB-43146 Find a list of resources based on a parent folder id and that are suitable for the local storage from the API
|
|
31
|
+
- PB-43133 Display padding below tags in resource workspace left sidebar
|
|
32
|
+
- PB-42185 The folder caret that expands or collapses folders in the tree should have a larger clickable area to make it easier to use
|
|
33
|
+
- PB-43222 Improve the group dialog to match the new share dimensions
|
|
34
|
+
- PB-43147 Find and update resources based on parent folder id for the local storage
|
|
35
|
+
- PB-43148 Create a connector for finding resources based on a parent id for the styleguide to call it later
|
|
36
|
+
- PB-43149 Create a ResourcesServiceWorkerService to call the service worker for resource related operations
|
|
37
|
+
- PB-43150 Implement the optimised call in the Styleguide when filtering by a folder
|
|
38
|
+
- PB-43151 Optimise the data retrieved from the API such that updates are not done if unnecessary
|
|
39
|
+
- PB-43156 Clarify implications for backups and other devices before changing the passphrase in the user settings workspace
|
|
40
|
+
- PB-43489 Display unexpected error if there is any issue during the secret decryption
|
|
5
41
|
|
|
6
42
|
### Fixed
|
|
7
|
-
- PB-
|
|
43
|
+
- PB-43109 Fix: from the sidebar when upgrade from v4 to v5 goes wrong the error message in the notification
|
|
44
|
+
- PB-43118 Hide the "Share metadata keys" button in the users workspace action bar for the current signed-in user
|
|
45
|
+
- PB-43215 Fix account recovery creator name
|
|
46
|
+
- PB-43063 Fix group edit dialog double warning message has broken UI
|
|
47
|
+
- PB-43117 Hide the "Share metadata keys" button in the users workspace action bar after sharing missing metadata keys with a user
|
|
48
|
+
- PB-43064 Fix group edit dialog can show a mix of error and warning messages
|
|
49
|
+
- PB-43150: fix folder not being reloaded
|
|
50
|
+
- PB-43424 Clicking on the "open in a new tab” call to action in the quick application should open the resource url in a new tab
|
|
51
|
+
- PB-43108 Display attention required icon on "metadata keys" label in the user details sidebar if the user is not having access to some metadata keys
|
|
52
|
+
- PB-43217 The default icon stroke width is too thick in the grid and doesn't match the custom icons
|
|
53
|
+
- PB-43220 Copy URL field action button lacks padding and is broken in the SSO settings
|
|
54
|
+
- PB-43168 Align vertically resources workspace select check-boxes
|
|
55
|
+
- PB-43211 The feedback message notifying the administrator when a metadata key has been shared with a user contains a typo
|
|
56
|
+
- PB-43471 Center vertically the icon on the create and edit dialog
|
|
@@ -48,6 +48,11 @@ classDiagram
|
|
|
48
48
|
+exec() Promise
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
class UpdateResourceLocalStorageByFolderParentIdController{
|
|
52
|
+
event "passbolt.resources.update-local-storage-for-parent-folder"
|
|
53
|
+
+exec(string parentFolderId) Promise
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
52
57
|
%% Resources services
|
|
53
58
|
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
@@ -62,6 +67,7 @@ classDiagram
|
|
|
62
67
|
|
|
63
68
|
class FindAndUpdateResourcesLocalStorageService {
|
|
64
69
|
+findAndUpdateAll(FindAndUpdateResourcesLocalStorageOptions) Promise~ResourcesCollection~
|
|
70
|
+
+findAndUpdateAllByParentFolderId(uuid parentFolderId, string passphrase) Promise~ResourcesCollection~
|
|
65
71
|
+findAndUpdateByIsSharedWithGroup(uuid groupId) Promise~ResourcesCollection~
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -79,6 +85,8 @@ classDiagram
|
|
|
79
85
|
+findAllByIdsWithPermissions(array~uuid~ resourcesIds) Promise~ResourcesCollection~
|
|
80
86
|
+findAllByIsSharedWithGroupForLocalStorage(uuid groupId) Promise~ResourcesCollection~
|
|
81
87
|
+findAllForDecrypt(array~uuid~ resourceIds) Promise~ResourcesCollection~
|
|
88
|
+
+findAllByIdsForLocalStorage(Array~uuid~ resourcesIds) Promise~ResourcesCollection~
|
|
89
|
+
+findAllByParentFolderIdForLocalStorage(string uuid) Promise~ResourcesCollection~
|
|
82
90
|
+findOneById(string uuid, object contains) Promise~ResourceEntity~
|
|
83
91
|
+findOneByIdForDetails(string uuid) Promise~ResourceEntity~
|
|
84
92
|
}
|
|
@@ -1065,6 +1073,7 @@ classDiagram
|
|
|
1065
1073
|
UpdateAllResourcesLocalStorageController*--FindAndUpdateResourcesLocalStorageService
|
|
1066
1074
|
%% UpdateResourceController*--GetPassphraseService
|
|
1067
1075
|
UpdateResourceController*--UpdateResourceService
|
|
1076
|
+
UpdateResourceLocalStorageByFolderParentIdController *--FindAndUpdateResourcesLocalStorage
|
|
1068
1077
|
style CreateResourceController fill:#D2E0FB
|
|
1069
1078
|
style ExportResourcesFileController fill:#D2E0FB
|
|
1070
1079
|
style FindAllIdsByIsSharedWithGroupController fill:#D2E0FB
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "passbolt-browser-extension",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"license": "AGPL-3.0",
|
|
5
5
|
"copyright": "Copyright 2025 Passbolt SA",
|
|
6
6
|
"description": "Passbolt web extension for the open source password manager for teams",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"locutus": "~2.0.9",
|
|
23
23
|
"openpgp": "^6.1.1",
|
|
24
24
|
"papaparse": "^5.5.2",
|
|
25
|
-
"passbolt-styleguide": "^5.
|
|
25
|
+
"passbolt-styleguide": "^5.3.1",
|
|
26
26
|
"react": "17.0.2",
|
|
27
27
|
"react-dom": "17.0.2",
|
|
28
28
|
"secrets-passbolt": "github:passbolt/secrets.js#v2.0.1",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"appName": {
|
|
3
|
-
"message": "Passbolt -
|
|
3
|
+
"message": "Passbolt - Odprtokodni upravitelj gesel",
|
|
4
4
|
"description": "The application name of the extension, displayed in the web store. 45 characters max."
|
|
5
5
|
},
|
|
6
6
|
"appDescription": {
|
|
7
|
-
"message": "Razširitev Passbolt za
|
|
7
|
+
"message": "Razširitev Passbolt za odprtokodnega upravitelja gesel za ekipe.",
|
|
8
8
|
"description": "The description of the extension, displayed in the web store. 85 characters max."
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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.0
|
|
13
|
+
*/
|
|
14
|
+
import {assertUuid} from "../../utils/assertions";
|
|
15
|
+
import FindAndUpdateResourcesLocalStorage from "../../service/resource/findAndUpdateResourcesLocalStorageService";
|
|
16
|
+
import UserPassphraseRequiredError from "passbolt-styleguide/src/shared/error/userPassphraseRequiredError";
|
|
17
|
+
import GetPassphraseService from "../../service/passphrase/getPassphraseService";
|
|
18
|
+
|
|
19
|
+
class UpdateResourceLocalStorageByFolderParentIdController {
|
|
20
|
+
/**
|
|
21
|
+
* Constructor.
|
|
22
|
+
* @param {Worker} worker The associated worker.
|
|
23
|
+
* @param {string} requestId The associated request id.
|
|
24
|
+
* @param {ApiClientOptions} apiClientOptions The api client options.
|
|
25
|
+
* @param {AccountEntity} account The user account
|
|
26
|
+
*/
|
|
27
|
+
constructor(worker, requestId, apiClientOptions, account) {
|
|
28
|
+
this.worker = worker;
|
|
29
|
+
this.requestId = requestId;
|
|
30
|
+
this.findAndUpdateResourcesLocalStorage = new FindAndUpdateResourcesLocalStorage(account, apiClientOptions);
|
|
31
|
+
this.getPassphraseService = new GetPassphraseService(account);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Controller executor.
|
|
36
|
+
* @param {string} parentFolderId the resources parent folder id
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
async _exec(parentFolderId) {
|
|
40
|
+
try {
|
|
41
|
+
await this.exec(parentFolderId);
|
|
42
|
+
this.worker.port.emit(this.requestId, 'SUCCESS');
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(error);
|
|
45
|
+
this.worker.port.emit(this.requestId, 'ERROR', error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Update resource local storage resource for a given folder.
|
|
51
|
+
* @param {string} parentFolderId the resources parent folder id
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
async exec(parentFolderId) {
|
|
55
|
+
assertUuid(parentFolderId);
|
|
56
|
+
try {
|
|
57
|
+
await this.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId(parentFolderId);
|
|
58
|
+
return;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (!(error instanceof UserPassphraseRequiredError)) {
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const passphrase = await this.getPassphraseService.getPassphrase(this.worker, 60);
|
|
66
|
+
await this.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId(parentFolderId, passphrase);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default UpdateResourceLocalStorageByFolderParentIdController;
|
|
@@ -0,0 +1,78 @@
|
|
|
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.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
|
|
16
|
+
import AccountEntity from "../../model/entity/account/accountEntity";
|
|
17
|
+
import {defaultAccountDto} from "../../model/entity/account/accountEntity.test.data";
|
|
18
|
+
import {v4 as uuidv4} from "uuid";
|
|
19
|
+
import UpdateResourceLocalStorageByFolderParentIdController from "./updateResourceLocalStorageByFolderParentIdController";
|
|
20
|
+
import UserPassphraseRequiredError from "passbolt-styleguide/src/shared/error/userPassphraseRequiredError";
|
|
21
|
+
|
|
22
|
+
describe("UpdateResourceLocalStorageByFolderParentIdController", () => {
|
|
23
|
+
let controller, worker;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
worker = {
|
|
27
|
+
port: {
|
|
28
|
+
emit: jest.fn()
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const account = new AccountEntity(defaultAccountDto());
|
|
32
|
+
const apiClientOptions = defaultApiClientOptions();
|
|
33
|
+
controller = new UpdateResourceLocalStorageByFolderParentIdController(worker, null, apiClientOptions, account);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("::exec", () => {
|
|
37
|
+
it("should call for update the local storage given a folder ID", async() => {
|
|
38
|
+
expect.assertions(2);
|
|
39
|
+
|
|
40
|
+
const parentFolderId = uuidv4();
|
|
41
|
+
|
|
42
|
+
jest.spyOn(controller.findAndUpdateResourcesLocalStorage, "findAndUpdateAllByParentFolderId").mockImplementation(() => {});
|
|
43
|
+
|
|
44
|
+
await controller.exec(parentFolderId);
|
|
45
|
+
|
|
46
|
+
expect(controller.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(controller.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId).toHaveBeenCalledWith(parentFolderId);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should call for update the local storage given a folder ID a second time with a passphrase if the first time it failed", async() => {
|
|
51
|
+
expect.assertions(3);
|
|
52
|
+
|
|
53
|
+
const parentFolderId = uuidv4();
|
|
54
|
+
const passphrase = "passphrase";
|
|
55
|
+
|
|
56
|
+
jest.spyOn(controller.getPassphraseService, "getPassphrase").mockImplementation(async() => passphrase);
|
|
57
|
+
jest.spyOn(controller.findAndUpdateResourcesLocalStorage, "findAndUpdateAllByParentFolderId").mockImplementation(async(_, passphrase) => {
|
|
58
|
+
if (!passphrase) {
|
|
59
|
+
throw new UserPassphraseRequiredError("Missing passphrase");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await controller.exec(parentFolderId);
|
|
64
|
+
|
|
65
|
+
expect(controller.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId).toHaveBeenCalledTimes(2);
|
|
66
|
+
expect(controller.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId).toHaveBeenCalledWith(parentFolderId);
|
|
67
|
+
expect(controller.findAndUpdateResourcesLocalStorage.findAndUpdateAllByParentFolderId).toHaveBeenCalledWith(parentFolderId, passphrase);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should throw an error if the parent folder id is not a valid uuid", async() => {
|
|
71
|
+
expect.assertions(1);
|
|
72
|
+
|
|
73
|
+
const promise = controller.exec(42);
|
|
74
|
+
|
|
75
|
+
await expect(promise).rejects.toThrowError("The given parameter is not a valid UUID");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -15,7 +15,7 @@ import {v4 as uuidv4} from "uuid";
|
|
|
15
15
|
import AccountEntity from "../../model/entity/account/accountEntity";
|
|
16
16
|
import {adminAccountDto} from "../../model/entity/account/accountEntity.test.data";
|
|
17
17
|
import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
|
|
18
|
-
import MockPort from "passbolt-styleguide/test/
|
|
18
|
+
import MockPort from "passbolt-styleguide/src/react-extension/test/mock/MockPort";
|
|
19
19
|
import {
|
|
20
20
|
defaultPermissionDto,
|
|
21
21
|
minimumPermissionDto
|
|
@@ -26,6 +26,7 @@ import FoldersCollection from "../../model/entity/folder/foldersCollection";
|
|
|
26
26
|
import {defaultFolderDto} from "passbolt-styleguide/src/shared/models/entity/folder/folderEntity.test.data";
|
|
27
27
|
const {pgpKeys} = require("passbolt-styleguide/test/fixture/pgpKeys/keys");
|
|
28
28
|
|
|
29
|
+
|
|
29
30
|
describe("ShareOneFolderController", () => {
|
|
30
31
|
describe("::exec", () => {
|
|
31
32
|
let account, controller;
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import ResourcesCollection from "../../model/entity/resource/resourcesCollection";
|
|
24
24
|
import expect from "expect";
|
|
25
25
|
import {defaultApiClientOptions} from "passbolt-styleguide/src/shared/lib/apiClient/apiClientOptions.test.data";
|
|
26
|
-
import MockPort from "passbolt-styleguide/test/
|
|
26
|
+
import MockPort from "passbolt-styleguide/src/react-extension/test/mock/MockPort";
|
|
27
27
|
import {v4 as uuidv4} from "uuid";
|
|
28
28
|
import {minimumPermissionDto} from "passbolt-styleguide/src/shared/models/entity/permission/permissionEntity.test.data";
|
|
29
29
|
import EncryptMessageService from "../../service/crypto/encryptMessageService";
|
|
@@ -22,6 +22,7 @@ import FindAllByIdsForDisplayPermissionsController
|
|
|
22
22
|
import MoveResourcesController from "../controller/move/moveResourcesController";
|
|
23
23
|
import ResetResourceGridUserSettingController
|
|
24
24
|
from "../controller/resourceGridSetting/resetResourceGridUserSettingController";
|
|
25
|
+
import UpdateResourceLocalStorageByFolderParentIdController from "../controller/resource/updateResourceLocalStorageByFolderParentIdController";
|
|
25
26
|
|
|
26
27
|
const listen = function(worker, apiClientOptions, account) {
|
|
27
28
|
/*
|
|
@@ -173,6 +174,16 @@ const listen = function(worker, apiClientOptions, account) {
|
|
|
173
174
|
const controller = new MoveResourcesController(worker, requestId, apiClientOptions, account);
|
|
174
175
|
await controller._exec(resourcesIds, destinationFolderId);
|
|
175
176
|
});
|
|
177
|
+
|
|
178
|
+
/*
|
|
179
|
+
* Update the local storage for resources in the specified parent folder.
|
|
180
|
+
* @listens passbolt.resources.update-local-storage-by-folder-parent-id
|
|
181
|
+
* @param {string} parentFolderId The id of the parent folder.
|
|
182
|
+
*/
|
|
183
|
+
worker.port.on('passbolt.resources.update-local-storage-by-folder-parent-id', async(requestId, parentFolderId) => {
|
|
184
|
+
const controller = new UpdateResourceLocalStorageByFolderParentIdController(worker, requestId, apiClientOptions, account);
|
|
185
|
+
await controller._exec(parentFolderId);
|
|
186
|
+
});
|
|
176
187
|
};
|
|
177
188
|
|
|
178
189
|
export const ResourceEvents = {listen};
|
|
@@ -336,6 +336,31 @@ class ResourcesCollection extends EntityV2Collection {
|
|
|
336
336
|
return result;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Update the current resource collection with the given collection.
|
|
341
|
+
* If an element already exists, it is replaced, otherwise, it's added.
|
|
342
|
+
* Note: mutation of the original collection does not trigger collection validation (schema or build rules).
|
|
343
|
+
* @param {ResourcesCollection} resourcesCollection
|
|
344
|
+
*/
|
|
345
|
+
updateWithCollection(resourcesCollection) {
|
|
346
|
+
assertType(resourcesCollection, ResourcesCollection);
|
|
347
|
+
|
|
348
|
+
const resourceMapByIdWithIndex = this.items.reduce((result, resource, currentIndex) => {
|
|
349
|
+
result[resource.id] = currentIndex;
|
|
350
|
+
return result;
|
|
351
|
+
}, {});
|
|
352
|
+
|
|
353
|
+
for (let i = 0; i < resourcesCollection.length; i++) {
|
|
354
|
+
const resource = resourcesCollection.items[i];
|
|
355
|
+
const mappedResourceIndex = resourceMapByIdWithIndex[resource.id];
|
|
356
|
+
if (typeof mappedResourceIndex === "undefined") {
|
|
357
|
+
this.push(resource, {validate: false});
|
|
358
|
+
} else {
|
|
359
|
+
this._items[mappedResourceIndex] = resource;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
339
364
|
/*
|
|
340
365
|
* ==================================================
|
|
341
366
|
* Static getters
|
|
@@ -446,4 +446,38 @@ describe("ResourcesCollection", () => {
|
|
|
446
446
|
expect(() => collection.setDecryptedMetadataFromCollection("test")).toThrow('The `resourcesCollection` parameter should be a ResourcesCollection.');
|
|
447
447
|
});
|
|
448
448
|
});
|
|
449
|
+
|
|
450
|
+
describe("::updateWithCollection", () => {
|
|
451
|
+
it("should update the existing resources and add the new ones", () => {
|
|
452
|
+
expect.assertions(5);
|
|
453
|
+
const resource1 = defaultResourceDto();
|
|
454
|
+
const resource2 = defaultResourceDto();
|
|
455
|
+
const collection = new ResourcesCollection([resource1, resource2]);
|
|
456
|
+
|
|
457
|
+
const updatedResource2 = {
|
|
458
|
+
...resource2,
|
|
459
|
+
metadata: defaultResourceMetadataDto({
|
|
460
|
+
name: "UPDATE - RESOURCE",
|
|
461
|
+
}),
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const resource3 = defaultResourceDto();
|
|
465
|
+
const collectionForUpdate = new ResourcesCollection([resource3, updatedResource2]);
|
|
466
|
+
|
|
467
|
+
collection.updateWithCollection(collectionForUpdate);
|
|
468
|
+
|
|
469
|
+
expect(collection).toHaveLength(3);
|
|
470
|
+
expect(collection.items[0].id).toStrictEqual(resource1.id);
|
|
471
|
+
expect(collection.items[1].id).toStrictEqual(resource2.id);
|
|
472
|
+
expect(collection.items[1].metadata.name).toStrictEqual(updatedResource2.metadata.name);
|
|
473
|
+
expect(collection.items[2].id).toStrictEqual(resource3.id);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should assert its parameters", () => {
|
|
477
|
+
expect.assertions(1);
|
|
478
|
+
const collection = new ResourcesCollection([]);
|
|
479
|
+
|
|
480
|
+
expect(() => collection.updateWithCollection("test")).toThrow();
|
|
481
|
+
});
|
|
482
|
+
});
|
|
449
483
|
});
|
package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @since 4.6.0
|
|
13
13
|
*/
|
|
14
14
|
import ResourceLocalStorage from "../local_storage/resourceLocalStorage";
|
|
15
|
-
import {assertNumber} from "../../utils/assertions";
|
|
15
|
+
import {assertNumber, assertUuid} from "../../utils/assertions";
|
|
16
16
|
import FindResourcesService from "./findResourcesService";
|
|
17
17
|
import ResourcesCollection from "../../model/entity/resource/resourcesCollection";
|
|
18
18
|
import ResourceTypeModel from "../../model/resourceType/resourceTypeModel";
|
|
@@ -113,6 +113,57 @@ class FindAndUpdateResourcesLocalStorage {
|
|
|
113
113
|
await ResourceLocalStorage.addOrReplaceResourcesCollection(resourcesCollection);
|
|
114
114
|
return resourcesCollection;
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Find and update the local storage with the resources filtered by parent folder id retrieved from the API.
|
|
119
|
+
* @param {string} parentFolderId The parent folder id to filter the resources with.
|
|
120
|
+
* @param {string|null} [passphrase = null] The passphrase to use to decrypt the metadata. Marked as optional as it
|
|
121
|
+
* might be available in the passphrase session storage.
|
|
122
|
+
* @return {Promise<ResourcesCollection>} The resource shared with the group
|
|
123
|
+
* @throw {TypeError} If the parentFolderId is not valid UUID
|
|
124
|
+
*/
|
|
125
|
+
async findAndUpdateAllByParentFolderId(parentFolderId, passphrase = null) {
|
|
126
|
+
assertUuid(parentFolderId);
|
|
127
|
+
const resourceTypes = await this.resourceTypeModel.getOrFindAll();
|
|
128
|
+
|
|
129
|
+
const apiResourcesCollection = await this.findResourcesServices.findAllByParentFolderIdForLocalStorage(parentFolderId);
|
|
130
|
+
apiResourcesCollection.filterByResourceTypes(resourceTypes);
|
|
131
|
+
|
|
132
|
+
const apiResourcesCollectionMapIds = apiResourcesCollection.items.reduce((result, resource) => {
|
|
133
|
+
result[resource.id] = resource;
|
|
134
|
+
return result;
|
|
135
|
+
}, {});
|
|
136
|
+
|
|
137
|
+
const localStorageResourcesCollection = new ResourcesCollection(await ResourceLocalStorage.get(), {validate: false});
|
|
138
|
+
const knownResourcesCollectionInFolderIds = localStorageResourcesCollection.items.reduce((result, resource) => {
|
|
139
|
+
if (resource.folderParentId === parentFolderId) {
|
|
140
|
+
result.push(resource.id);
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}, Array(apiResourcesCollection.length));
|
|
144
|
+
|
|
145
|
+
apiResourcesCollection.setDecryptedMetadataFromCollection(localStorageResourcesCollection);
|
|
146
|
+
localStorageResourcesCollection.updateWithCollection(apiResourcesCollection);
|
|
147
|
+
|
|
148
|
+
const movedOrRemovedResourcesIds = knownResourcesCollectionInFolderIds.filter(id => !apiResourcesCollectionMapIds[id]);
|
|
149
|
+
if (movedOrRemovedResourcesIds.length > 0) {
|
|
150
|
+
const updatedResources = await this.findResourcesServices.findAllByIdsForLocalStorage(movedOrRemovedResourcesIds);
|
|
151
|
+
|
|
152
|
+
updatedResources.setDecryptedMetadataFromCollection(localStorageResourcesCollection);
|
|
153
|
+
localStorageResourcesCollection.updateWithCollection(updatedResources);
|
|
154
|
+
|
|
155
|
+
// These resources have been deleted (or permissions revoked) and need to be removed from the local storage.
|
|
156
|
+
const remainingResourcesIds = movedOrRemovedResourcesIds
|
|
157
|
+
.filter(id => !updatedResources.getFirstById(id));
|
|
158
|
+
|
|
159
|
+
localStorageResourcesCollection.removeMany(remainingResourcesIds);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await this.decryptMetadataService.decryptAllFromForeignModels(localStorageResourcesCollection, passphrase, {ignoreDecryptionError: true, updateSessionKeys: true});
|
|
163
|
+
localStorageResourcesCollection.filterOutMetadataEncrypted();
|
|
164
|
+
|
|
165
|
+
await ResourceLocalStorage.set(localStorageResourcesCollection);
|
|
166
|
+
}
|
|
116
167
|
}
|
|
117
168
|
|
|
118
169
|
export default FindAndUpdateResourcesLocalStorage;
|
package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.test.js
CHANGED
|
@@ -371,4 +371,104 @@ describe("UpdateResourcesLocalStorage", () => {
|
|
|
371
371
|
expect(expectLocalStorageResult[1]).toEqual(ResourceEntity.transformDtoFromV4toV5(resourceDto2));
|
|
372
372
|
});
|
|
373
373
|
});
|
|
374
|
+
|
|
375
|
+
describe("::findAndUpdateAllByParentFolderId", () => {
|
|
376
|
+
let service;
|
|
377
|
+
|
|
378
|
+
beforeEach(() => {
|
|
379
|
+
service = new FindAndUpdateResourcesLocalStorage(account, apiClientOptions);
|
|
380
|
+
jest.spyOn(ResourceTypeService.prototype, "findAll").mockImplementation(() => resourceTypesCollectionDto());
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("should extract the id from the resource collection", async() => {
|
|
384
|
+
expect.assertions(2);
|
|
385
|
+
|
|
386
|
+
const parentFolderId = uuidv4();
|
|
387
|
+
|
|
388
|
+
const resourcesDto = multipleResourceDtos();
|
|
389
|
+
jest.spyOn(ResourceService.prototype, "findAll").mockImplementation(() => resourcesDto);
|
|
390
|
+
jest.spyOn(service.findResourcesServices, "findAllByParentFolderIdForLocalStorage");
|
|
391
|
+
|
|
392
|
+
await service.findAndUpdateAllByParentFolderId(parentFolderId);
|
|
393
|
+
|
|
394
|
+
expect(service.findResourcesServices.findAllByParentFolderIdForLocalStorage).toHaveBeenCalledTimes(1);
|
|
395
|
+
expect(service.findResourcesServices.findAllByParentFolderIdForLocalStorage).toHaveBeenCalledWith(parentFolderId);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it("should assert its parameter", async() => {
|
|
399
|
+
expect.assertions(1);
|
|
400
|
+
await expect(() => service.findAndUpdateAllByParentFolderId("test")).rejects.toThrow();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it("should update the local storage resource collection by removing deleted resources, moving resources and updating resources data", async() => {
|
|
404
|
+
expect.assertions(8);
|
|
405
|
+
|
|
406
|
+
const parentFolderId = uuidv4();
|
|
407
|
+
const otherParentFolderId = uuidv4();
|
|
408
|
+
|
|
409
|
+
/*
|
|
410
|
+
* Resources collection in local storage:
|
|
411
|
+
* Resource0.folderParentId = null;
|
|
412
|
+
* Resource1.folderParentId = parentFolderId; // on API it should be modified only
|
|
413
|
+
* Resource2.folderParentId = parentFolderId; // on API it should be moved
|
|
414
|
+
* Resource3.folderParentId = parentFolderId; // on API it should be removed
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
const resourcesDto = multipleResourceDtos();
|
|
418
|
+
resourcesDto[1].folder_parent_id = parentFolderId;
|
|
419
|
+
resourcesDto[2].folder_parent_id = parentFolderId;
|
|
420
|
+
resourcesDto[3].folder_parent_id = parentFolderId;
|
|
421
|
+
|
|
422
|
+
delete resourcesDto[0].name;
|
|
423
|
+
delete resourcesDto[1].name;
|
|
424
|
+
delete resourcesDto[2].name;
|
|
425
|
+
delete resourcesDto[3].name;
|
|
426
|
+
|
|
427
|
+
const localStorageResourceCollection = new ResourcesCollection(resourcesDto);
|
|
428
|
+
await ResourceLocalStorage.set(localStorageResourceCollection);
|
|
429
|
+
|
|
430
|
+
// the resources in the folder on the API does not have resource2 anymore but resources1 remains and is changed.
|
|
431
|
+
const apiResourcesDtoInFolder = [
|
|
432
|
+
{...resourcesDto[1]},
|
|
433
|
+
];
|
|
434
|
+
apiResourcesDtoInFolder[0].metadata.name = "Resource1 - UPDATED";
|
|
435
|
+
apiResourcesDtoInFolder[0].modified = (new Date()).toISOString();
|
|
436
|
+
|
|
437
|
+
// resource2 on the API will be return and updated, resource3 will never be sent back as it is deleted
|
|
438
|
+
const allIdsApiResourcesDto = [
|
|
439
|
+
{...resourcesDto[2]},
|
|
440
|
+
];
|
|
441
|
+
allIdsApiResourcesDto[0].folder_parent_id = otherParentFolderId;
|
|
442
|
+
|
|
443
|
+
async function mockedFindAllApi(_, filter) {
|
|
444
|
+
const isParentFolderSearchRequest = Boolean(filter["has-parent"]);
|
|
445
|
+
|
|
446
|
+
return isParentFolderSearchRequest
|
|
447
|
+
? apiResourcesDtoInFolder
|
|
448
|
+
: allIdsApiResourcesDto;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
jest.spyOn(ResourceService.prototype, "findAll").mockImplementation(mockedFindAllApi);
|
|
452
|
+
|
|
453
|
+
await service.findAndUpdateAllByParentFolderId(parentFolderId);
|
|
454
|
+
|
|
455
|
+
const updatedResourceLocalStorage = new ResourcesCollection(await ResourceLocalStorage.get());
|
|
456
|
+
|
|
457
|
+
expect(updatedResourceLocalStorage).toHaveLength(3); //1 resource should be removed compared to the original data
|
|
458
|
+
const resource0 = updatedResourceLocalStorage.getFirstById(resourcesDto[0].id);
|
|
459
|
+
expect(resource0.toDto(ResourceEntity.ALL_CONTAIN_OPTIONS)).toStrictEqual(resourcesDto[0]); //this resource should remain unchanged
|
|
460
|
+
|
|
461
|
+
const updatedResource1 = updatedResourceLocalStorage.getFirstById(resourcesDto[1].id); // this resource should have been updated but not moved
|
|
462
|
+
expect(updatedResource1.metadata.name).toStrictEqual("Resource1 - UPDATED");
|
|
463
|
+
expect(updatedResource1.modified).toStrictEqual(apiResourcesDtoInFolder[0].modified);
|
|
464
|
+
expect(updatedResource1.folderParentId).toStrictEqual(parentFolderId);
|
|
465
|
+
|
|
466
|
+
const updatedResource2 = updatedResourceLocalStorage.getFirstById(resourcesDto[2].id); // this resource should not have changed per say but only moved
|
|
467
|
+
expect(updatedResource2.modified).toStrictEqual(resourcesDto[2].modified);
|
|
468
|
+
expect(updatedResource2.folderParentId).toStrictEqual(otherParentFolderId);
|
|
469
|
+
|
|
470
|
+
const updatedResource3 = updatedResourceLocalStorage.getFirstById(resourcesDto[3].id); // this resource should have been removed
|
|
471
|
+
expect(updatedResource3).toBeUndefined();
|
|
472
|
+
});
|
|
473
|
+
});
|
|
374
474
|
});
|