passbolt-browser-extension 5.13.0 → 5.14.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/.devcontainer/Dockerfile +33 -3
- package/.devcontainer/devcontainer-lock.json +9 -0
- package/.devcontainer/post-start.sh +1 -2
- package/.github/workflows/codeql-analysis.yml +2 -1
- package/.github/workflows/release_candidate.yaml +3 -1
- package/.gitlab-ci.yml +1 -0
- package/CHANGELOG.md +202 -70
- package/CODEOWNERS +2 -0
- package/RELEASE_NOTES.md +4 -2
- package/package.json +9 -7
- package/renovate.json +6 -0
- package/src/all/background_page/controller/InformMenuController/InformMenuController.js +2 -5
- package/src/all/background_page/controller/InformMenuController/InformMenuController.test.js +31 -1
- package/src/all/background_page/controller/account/getAndInitializeAccountLocaleController.js +1 -1
- package/src/all/background_page/controller/account/getAndInitializeAccountLocaleController.test.js +6 -14
- package/src/all/background_page/controller/account/updatePrivateKeyController.js +7 -5
- package/src/all/background_page/controller/account/updatePrivateKeyController.test.js +18 -13
- package/src/all/background_page/controller/accountRecovery/accountRecoveryGenerateOrganizationKeyController.js +4 -2
- package/src/all/background_page/controller/accountRecovery/accountRecoveryGenerateOrganizationKeyController.test.js +25 -2
- package/src/all/background_page/controller/accountRecovery/accountRecoveryLoginController.js +1 -1
- package/src/all/background_page/controller/accountRecovery/accountRecoveryLoginController.test.js +10 -5
- package/src/all/background_page/controller/accountRecovery/recoverAccountController.js +6 -2
- package/src/all/background_page/controller/accountRecovery/recoverAccountController.test.js +11 -4
- package/src/all/background_page/controller/auth/authCheckStatus.test.data.js +6 -6
- package/src/all/background_page/controller/auth/authCheckStatusController.js +11 -3
- package/src/all/background_page/controller/auth/authCheckStatusController.test.js +74 -44
- package/src/all/background_page/controller/auth/authIsMfaRequiredController.js +11 -3
- package/src/all/background_page/controller/auth/authIsMfaRequiredController.test.js +18 -9
- package/src/all/background_page/controller/auth/authLoginController.js +1 -1
- package/src/all/background_page/controller/auth/authLoginController.test.js +10 -5
- package/src/all/background_page/controller/autofill/AutofillController.js +3 -3
- package/src/all/background_page/controller/autofill/AutofillController.test.js +20 -12
- package/src/all/background_page/controller/export/exportResourcesFileController.js +6 -3
- package/src/all/background_page/controller/export/exportResourcesFileController.test.js +34 -4
- package/src/all/background_page/controller/extension/onExtensionInstalledController.js +8 -2
- package/src/all/background_page/controller/extension/onExtensionInstalledController.test.js +5 -1
- package/src/all/background_page/controller/group/{getOrFindGroupsController.js → findGroupsByIdsForShareController.js} +9 -10
- package/src/all/background_page/controller/group/{getOrFindGroupsController.test.js → findGroupsByIdsForShareController.test.js} +8 -8
- package/src/all/background_page/controller/group/groupUpdateController.test.js +1 -0
- package/src/all/background_page/controller/import/importResourcesFileController.test.js +8 -1
- package/src/all/background_page/controller/informCallToActionController/informCallToActionController.js +2 -2
- package/src/all/background_page/controller/informCallToActionController/informCallToActionController.test.js +7 -4
- package/src/all/background_page/controller/locale/getLocaleController.js +3 -2
- package/src/all/background_page/controller/metadata/findMetadataTypesSettingsController.test.js +9 -15
- package/src/all/background_page/controller/metadata/getMetadataTypesSettingsController.test.js +9 -15
- package/src/all/background_page/controller/metadata/getOrFindMetadataKeysSettingsController.test.js +12 -22
- package/src/all/background_page/controller/metadata/shareMetadataKeyPrivateController.test.js +14 -2
- package/src/all/background_page/controller/move/moveFolderController.test.js +8 -0
- package/src/all/background_page/controller/move/moveResourcesController.test.js +8 -0
- package/src/all/background_page/controller/passwordExpiry/getOrFindPasswordExpirySettingsController.test.js +13 -8
- package/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.js +4 -4
- package/src/all/background_page/controller/passwordExpiry/savePasswordExpirySettingsController.test.js +8 -10
- package/src/all/background_page/controller/permission/findPermissionsByIdsForShareController.js +62 -0
- package/src/all/background_page/controller/permission/findPermissionsByIdsForShareController.test.js +71 -0
- package/src/all/background_page/controller/recover/generateRecoverAccountRecoveryRequestKeyController.js +1 -1
- package/src/all/background_page/controller/resource/resourceCreateController.js +11 -4
- package/src/all/background_page/controller/resource/resourceCreateController.test.js +12 -1
- package/src/all/background_page/controller/resource/resourceUpdateController.js +11 -4
- package/src/all/background_page/controller/resource/resourceUpdateController.test.js +35 -2
- package/src/all/background_page/controller/resourceType/getResourceTypesController.js +5 -4
- package/src/all/background_page/controller/resourceType/getResourceTypesController.test.js +5 -2
- package/src/all/background_page/controller/resourceType/updateAllResourceTypesDeletedStatusController.js +3 -2
- package/src/all/background_page/controller/resourceType/updateAllResourceTypesDeletedStatusController.test.js +16 -3
- package/src/all/background_page/controller/secret/findSecretByResourceIdController.js +3 -3
- package/src/all/background_page/controller/secret/findSecretByResourceIdController.test.js +24 -0
- package/src/all/background_page/controller/secretRevision/findResourceSecretRevisionsForDisplayController.js +2 -1
- package/src/all/background_page/controller/setup/generateSetupKeyPairController.js +6 -3
- package/src/all/background_page/controller/setup/generateSetupKeyPairController.test.js +11 -1
- package/src/all/background_page/controller/setup/getAndInitSetupLocaleController.js +1 -1
- package/src/all/background_page/controller/setup/getAndInitSetupLocaleController.test.js +14 -18
- package/src/all/background_page/controller/setup/setSetupLocaleController.js +1 -1
- package/src/all/background_page/controller/setup/setSetupLocaleController.test.js +6 -10
- package/src/all/background_page/controller/setup/signInSetupController.js +6 -2
- package/src/all/background_page/controller/setup/signInSetupController.test.js +10 -5
- package/src/all/background_page/controller/share/shareOneFolderController.test.js +8 -0
- package/src/all/background_page/controller/share/shareResourcesController.test.js +9 -1
- package/src/all/background_page/controller/siteSettings/getOrFindSiteSettingsController.js +59 -0
- package/src/all/background_page/controller/siteSettings/getOrFindSiteSettingsController.test.js +150 -0
- package/src/all/background_page/controller/{group/getOrFindGroupsUsersController.js → user/getOrFindAllUsersController.js} +12 -17
- package/src/all/background_page/controller/user/getOrFindAllUsersController.test.js +55 -0
- package/src/all/background_page/controller/user/getOrFindLoggedInUserController.js +3 -3
- package/src/all/background_page/controller/user/getOrFindLoggedInUserController.test.js +19 -15
- package/src/all/background_page/controller/user/updateUserLocalStorageController.test.js +8 -5
- package/src/all/background_page/event/accountRecoveryEvents.js +4 -4
- package/src/all/background_page/event/appEvents.js +7 -2
- package/src/all/background_page/event/authEvents.js +2 -2
- package/src/all/background_page/event/groupEvents.js +4 -17
- package/src/all/background_page/event/informCallToActionEvents.js +1 -1
- package/src/all/background_page/event/informMenuEvents.js +1 -1
- package/src/all/background_page/event/localeEvents.js +4 -3
- package/src/all/background_page/event/permissionEvents.js +13 -0
- package/src/all/background_page/event/recoverEvents.js +4 -4
- package/src/all/background_page/event/resourceEvents.js +6 -15
- package/src/all/background_page/event/resourceTypeEvents.js +4 -3
- package/src/all/background_page/event/setupEvents.js +4 -4
- package/src/all/background_page/event/siteSettingsEvents.js +29 -0
- package/src/all/background_page/event/userEvents.js +4 -8
- package/src/all/background_page/index.js +0 -37
- package/src/all/background_page/model/entity/account/abstractAccountEntity.js +2 -2
- package/src/all/background_page/model/entity/account/accountAccountRecoveryEntity.js +7 -17
- package/src/all/background_page/model/entity/account/accountEntity.js +7 -14
- package/src/all/background_page/model/entity/account/accountKitEntity.js +1 -2
- package/src/all/background_page/model/entity/account/accountRecoverEntity.js +4 -14
- package/src/all/background_page/model/entity/account/accountSetupEntity.js +4 -14
- package/src/all/background_page/model/entity/account/accountTemporaryEntity.js +2 -5
- package/src/all/background_page/model/entity/gpgkey/generate/generateGpgKeyPairOptionsEntity.test.js +5 -5
- package/src/all/background_page/model/entity/localStorage/localStorageMetadataEntity.js +49 -0
- package/src/all/background_page/model/entity/localStorage/localStorageMetadataEntity.test.data.js +23 -0
- package/src/all/background_page/model/entity/localStorage/localStorageMetadataEntity.test.js +51 -0
- package/src/all/background_page/model/entity/{organizationSettings/organizationSettingsEntity.js → siteSettings/siteSettingsEntity.js} +17 -24
- package/src/all/background_page/model/entity/{organizationSettings/organizationSettingsEntity.test.data.js → siteSettings/siteSettingsEntity.test.data.js} +9 -9
- package/src/all/background_page/model/entity/{organizationSettings/organizationSettingsEntity.test.js → siteSettings/siteSettingsEntity.test.js} +30 -39
- package/src/all/background_page/model/entity/user/userEntity.test.js +6 -6
- package/src/all/background_page/model/export/resources/resourcesKdbxExporter.js +40 -3
- package/src/all/background_page/model/export/resources/resourcesKdbxExporter.test.js +122 -0
- package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.js +1 -1
- package/src/all/background_page/model/locale/localeModel.js +13 -8
- package/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.js +6 -6
- package/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.test.js +3 -2
- package/src/all/background_page/model/resource/resourceModel.js +4 -4
- package/src/all/background_page/model/user/userModel.js +7 -28
- package/src/all/background_page/model/user/userModel.test.js +51 -99
- package/src/all/background_page/pagemod/appBootstrapPagemod.js +4 -1
- package/src/all/background_page/pagemod/appBootstrapPagemod.test.js +5 -2
- package/src/all/background_page/pagemod/appPagemod.js +32 -6
- package/src/all/background_page/pagemod/appPagemod.test.js +22 -10
- package/src/all/background_page/pagemod/authPagemod.js +2 -10
- package/src/all/background_page/pagemod/authPagemod.test.js +4 -4
- package/src/all/background_page/pagemod/pagemodManager.test.js +5 -2
- package/src/all/background_page/pagemod/quickAccessPagemod.js +2 -2
- package/src/all/background_page/pagemod/quickAccessPagemod.test.js +4 -4
- package/src/all/background_page/pagemod/recoverPagemod.js +2 -2
- package/src/all/background_page/pagemod/recoverPagemod.test.js +6 -0
- package/src/all/background_page/pagemod/setupPagemod.js +2 -2
- package/src/all/background_page/pagemod/setupPagemod.test.js +6 -0
- package/src/all/background_page/pagemod/webIntegrationPagemod.js +21 -2
- package/src/all/background_page/pagemod/webIntegrationPagemod.test.js +16 -25
- package/src/all/background_page/service/account/getActiveAccountService.js +2 -3
- package/src/all/background_page/service/account/getLegacyAccountService.js +3 -14
- package/src/all/background_page/service/account/updateSsoCredentialsService.js +9 -5
- package/src/all/background_page/service/api/group/groupApiService.js +9 -16
- package/src/all/background_page/service/api/group/groupApiService.test.js +59 -26
- package/src/all/background_page/service/api/siteSettings/siteSettingsApiService.js +46 -0
- package/src/all/background_page/service/api/siteSettings/siteSettingsApiService.test.js +63 -0
- package/src/all/background_page/service/auth/checkAuthStatusService.js +27 -14
- package/src/all/background_page/service/auth/checkAuthStatusService.test.js +80 -62
- package/src/all/background_page/service/auth/postLoginService.js +3 -3
- package/src/all/background_page/service/auth/postLoginService.test.js +4 -4
- package/src/all/background_page/service/auth/startLoopAuthSessionCheckService.js +5 -1
- package/src/all/background_page/service/auth/startLoopAuthSessionCheckService.test.js +14 -8
- package/src/all/background_page/service/authenticationStatusService.js +13 -18
- package/src/all/background_page/service/authenticationStatusService.test.js +9 -6
- package/src/all/background_page/service/crypto/generateGpgKeyPairService.test.js +5 -5
- package/src/all/background_page/service/crypto/getGpgKeyCreationDateService.js +7 -6
- package/src/all/background_page/service/crypto/getGpgKeyCreationDateService.test.js +10 -7
- package/src/all/background_page/service/extension/onExtensionUpdateAvailableService.js +7 -2
- package/src/all/background_page/service/extension/onExtensionUpdateAvailableService.test.js +5 -5
- package/src/all/background_page/service/group/findGroupsService.js +42 -1
- package/src/all/background_page/service/group/findGroupsService.test.js +82 -0
- package/src/all/background_page/service/group/groupUpdateSecretsCryptoService.js +82 -0
- package/src/all/background_page/service/group/groupUpdateSecretsCryptoService.test.js +102 -0
- package/src/all/background_page/service/group/groupUpdateService.js +49 -78
- package/src/all/background_page/service/group/groupUpdateService.test.js +136 -0
- package/src/all/background_page/service/localStorage/localStorageService.js +8 -6
- package/src/all/background_page/service/localStorage/localStorageService.test.js +33 -15
- package/src/all/background_page/service/local_storage/abstractLocalStorage.js +193 -0
- package/src/all/background_page/service/local_storage/abstractLocalStorage.test.data.js +45 -0
- package/src/all/background_page/service/local_storage/abstractLocalStorage.test.js +271 -0
- package/src/all/background_page/service/local_storage/activeSessionLocalStorage.js +105 -0
- package/src/all/background_page/service/local_storage/activeSessionLocalStorage.test.js +153 -0
- package/src/all/background_page/service/local_storage/rbacLocalStorage.js +10 -82
- package/src/all/background_page/service/local_storage/rbacLocalStorage.test.js +8 -8
- package/src/all/background_page/service/local_storage/resourceTypeLocalStorage.js +10 -43
- package/src/all/background_page/service/local_storage/resourceTypeLocalStorage.test.js +68 -0
- package/src/all/background_page/service/local_storage/siteSettingsLocalStorage.js +104 -0
- package/src/all/background_page/service/local_storage/siteSettingsLocalStorage.test.js +114 -0
- package/src/all/background_page/service/local_storage/userMeLocalStorage.js +50 -0
- package/src/all/background_page/service/{sessionStorage/userMeSessionStorageService.test.js → local_storage/userMeLocalStorage.test.js} +55 -36
- package/src/all/background_page/service/metadata/encryptMetadataService.js +3 -3
- package/src/all/background_page/service/metadata/encryptMetadataService.test.js +11 -11
- package/src/all/background_page/service/metadata/findAndUpdateMetadataSettingsLocalStorageService.js +4 -4
- package/src/all/background_page/service/metadata/findAndUpdateMetadataSettingsLocalStorageService.test.js +18 -16
- package/src/all/background_page/service/metadata/getOrFindMetadataSettingsService.test.js +6 -8
- package/src/all/background_page/service/metadata/rotateMetadata/rotateResourcesMetadataKeyService.test.js +4 -4
- package/src/all/background_page/service/metadata/shareMetadataKeyPrivateService.js +3 -3
- package/src/all/background_page/service/metadata/shareMetadataKeyPrivateService.test.js +1 -1
- package/src/all/background_page/service/metadata/verifyOrTrustMetadataKeyService.js +4 -4
- package/src/all/background_page/service/metadata/verifyOrTrustMetadataKeyService.test.js +9 -7
- package/src/all/background_page/service/migrateMetadata/migrateMetadataResourcesService.js +4 -4
- package/src/all/background_page/service/migrateMetadata/migrateMetadataResourcesService.test.js +6 -6
- package/src/all/background_page/service/rbac/findAndUpdateRbacsLocalStorageService.js +2 -2
- package/src/all/background_page/service/rbac/getOrFindRbacService.js +1 -1
- package/src/all/background_page/service/resource/create/resourceCreateService.js +41 -51
- package/src/all/background_page/service/resource/create/resourceCreateService.test.js +100 -98
- package/src/all/background_page/service/resource/export/exportResourcesService.js +7 -7
- package/src/all/background_page/service/resource/findAndUpdateResourcesLocalStorageService.js +4 -4
- package/src/all/background_page/service/resource/findResourcesService.js +13 -16
- package/src/all/background_page/service/resource/findResourcesService.test.js +12 -11
- package/src/all/background_page/service/resource/getOrFindResourcesService.js +3 -3
- package/src/all/background_page/service/resource/import/ImportResourcesService.js +9 -9
- package/src/all/background_page/service/resource/import/ImportResourcesService.test.js +30 -15
- package/src/all/background_page/service/resource/update/resourceUpdateService.js +38 -19
- package/src/all/background_page/service/resource/update/resourceUpdateService.test.js +64 -31
- package/src/all/background_page/service/resourceType/getOrFindResourceTypesService.js +49 -0
- package/src/all/background_page/service/resourceType/getOrFindResourceTypesService.test.js +61 -0
- package/src/all/background_page/service/resourceType/getSecretSchemaResourceTypeService.js +44 -0
- package/src/all/background_page/service/resourceType/updateResourceTypesService.js +6 -4
- package/src/all/background_page/service/resourceType/updateResourceTypesService.test.js +19 -14
- package/src/all/background_page/service/secretRevisions/findAndDecryptSecretRevisionsService.js +5 -4
- package/src/all/background_page/service/secretRevisions/findAndDecryptSecretRevisionsService.test.js +16 -9
- package/src/all/background_page/service/share/shareResourceService.js +4 -4
- package/src/all/background_page/service/share/shareResourceService.test.js +6 -6
- package/src/all/background_page/service/siteSettings/findAndUpdateSiteSettingsLocalStorageService.js +77 -0
- package/src/all/background_page/service/siteSettings/findAndUpdateSiteSettingsLocalStorageService.test.js +130 -0
- package/src/all/background_page/service/siteSettings/findSiteSettingsService.js +47 -0
- package/src/all/background_page/service/siteSettings/findSiteSettingsService.test.js +68 -0
- package/src/all/background_page/service/siteSettings/getOrFindSiteSettingsService.js +84 -0
- package/src/all/background_page/service/siteSettings/getOrFindSiteSettingsService.test.js +175 -0
- package/src/all/background_page/service/siteSettings/siteSettingsRuntimeCache.js +70 -0
- package/src/all/background_page/service/sso/generateSsoKitService.test.js +2 -2
- package/src/all/background_page/service/toolbar/toolbarService.js +6 -4
- package/src/all/background_page/service/toolbar/toolbarService.test.js +14 -13
- package/src/all/background_page/service/user/findAndUpdateUsersLocalStorageService.js +4 -4
- package/src/all/background_page/service/user/findAndUpdateUsersLocalStorageService.test.js +3 -3
- package/src/all/background_page/service/user/getOrFindMeService.js +64 -0
- package/src/all/background_page/service/user/getOrFindMeService.test.js +65 -0
- package/src/all/background_page/service/userKeyPolicies/findUserKeyPoliciesSettingsService.js +5 -4
- package/src/all/background_page/service/userKeyPolicies/findUserKeyPoliciesSettingsService.test.js +12 -11
- package/src/all/background_page/service/validator/appEmailValidatorService.js +11 -6
- package/src/all/background_page/service/validator/appEmailValidatorService.test.js +11 -11
- package/src/all/common/polyfill/browserPolyfill.js +8 -8
- package/src/all/locales/cs-CZ/common.json +1 -4
- package/src/all/locales/de-DE/common.json +1 -4
- package/src/all/locales/en-UK/common.json +0 -3
- package/src/all/locales/es-ES/common.json +1 -4
- package/src/all/locales/fr-FR/common.json +1 -4
- package/src/all/locales/it-IT/common.json +1 -4
- package/src/all/locales/ja-JP/common.json +1 -4
- package/src/all/locales/ko-KR/common.json +0 -3
- package/src/all/locales/lt-LT/common.json +1 -4
- package/src/all/locales/nl-NL/common.json +1 -4
- package/src/all/locales/pl-PL/common.json +1 -4
- package/src/all/locales/pt-BR/common.json +1 -4
- package/src/all/locales/ro-RO/common.json +1 -4
- package/src/all/locales/ru-RU/common.json +1 -4
- package/src/all/locales/sl-SI/common.json +1 -4
- package/src/all/locales/sv-SE/common.json +1 -4
- package/src/all/locales/uk-UA/common.json +1 -4
- package/src/all/webAccessibleResources/js/app/QuickAccess.js +7 -0
- package/src/chrome/manifest.json +1 -1
- package/src/chrome-mv3/manifest.json +1 -1
- package/src/chrome-mv3/offscreens/service/group/addUsersToGroupOffscreenService.js +132 -0
- package/src/chrome-mv3/offscreens/service/group/addUsersToGroupOffscreenService.test.js +125 -0
- package/src/chrome-mv3/offscreens/service/offscreen/handleOffscreenRequestService.js +4 -0
- package/src/chrome-mv3/serviceWorker/service/addUsersToGroup/requestAddUsersToGroupOffscreenService.js +83 -0
- package/src/chrome-mv3/serviceWorker/service/addUsersToGroup/requestAddUsersToGroupOffscreenService.test.js +116 -0
- package/src/chrome-mv3/serviceWorker/service/addUsersToGroup/responseAddUsersToGroupOffscreenService.js +99 -0
- package/src/chrome-mv3/serviceWorker/service/addUsersToGroup/responseAddUsersToGroupOffscreenService.test.data.js +45 -0
- package/src/chrome-mv3/serviceWorker/service/addUsersToGroup/responseAddUsersToGroupOffscreenService.test.js +87 -0
- package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.js +0 -1
- package/src/chrome-mv3/serviceWorker/service/offscreen/handleOffscreenResponseService.js +15 -0
- package/src/firefox/manifest.json +1 -1
- package/src/safari/background_page/index.js +0 -37
- package/src/safari/common/polyfill/safariBrowserPolyfill.js +10 -10
- package/src/safari/manifest.json +1 -1
- package/test/jest.setup.js +4 -2
- package/src/all/background_page/controller/group/getOrFindGroupsUsersController.test.js +0 -69
- package/src/all/background_page/controller/organizationSettings/getOrganizationSettingsController.js +0 -55
- package/src/all/background_page/controller/organizationSettings/getOrganizationSettingsController.test.js +0 -45
- package/src/all/background_page/controller/resource/findAllByIdsForDisplayPermissionsController.js +0 -76
- package/src/all/background_page/controller/resource/findAllByIdsForDisplayPermissionsController.test.js +0 -102
- package/src/all/background_page/event/organizationSettingsEvents.js +0 -37
- package/src/all/background_page/model/organizationSettings/organizationSettingsModel.js +0 -93
- package/src/all/background_page/model/resourceType/resourceTypeModel.js +0 -72
- package/src/all/background_page/model/resourceType/resourceTypeModel.test.js +0 -101
- package/src/all/background_page/service/api/organizationSettings/organizationSettingsService.js +0 -58
- package/src/all/background_page/service/group/getOrFindGroupsService.js +0 -65
- package/src/all/background_page/service/group/getOrFindGroupsService.test.js +0 -168
- package/src/all/background_page/service/group/getOrFindGroupsUsersService.js +0 -51
- package/src/all/background_page/service/group/getOrFindGroupsUsersService.test.js +0 -94
- package/src/all/background_page/service/local_storage/authStatusLocalStorage.js +0 -70
- package/src/all/background_page/service/sessionStorage/userMeSessionStorageService.js +0 -94
|
@@ -19,6 +19,8 @@ import MfaAuthenticationRequiredError from "../../error/mfaAuthenticationRequire
|
|
|
19
19
|
import WebIntegrationPagemod from "../../pagemod/webIntegrationPagemod";
|
|
20
20
|
import WorkerService from "../worker/workerService";
|
|
21
21
|
import PublicWebsiteSignInPagemod from "../../pagemod/publicWebsiteSignInPagemod";
|
|
22
|
+
import getActiveAccountService from "../account/getActiveAccountService";
|
|
23
|
+
import BuildApiClientOptionsService from "../account/buildApiClientOptionsService";
|
|
22
24
|
|
|
23
25
|
const PASSBOLT_EXTENSION_UPDATE = "passboltExtensionUpdate";
|
|
24
26
|
|
|
@@ -59,14 +61,17 @@ class OnExtensionUpdateAvailableService {
|
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
63
|
* Check and process event if the user is authenticated
|
|
62
|
-
* @returns {Promise<
|
|
64
|
+
* @returns {Promise<boolean>}
|
|
63
65
|
*/
|
|
64
66
|
const isUserAuthenticated = async () => {
|
|
65
67
|
const user = User.getInstance();
|
|
66
68
|
// Check if user is valid
|
|
67
69
|
if (user.isValid()) {
|
|
68
70
|
try {
|
|
69
|
-
|
|
71
|
+
const account = await getActiveAccountService.get();
|
|
72
|
+
const apiClientOptions = BuildApiClientOptionsService.buildFromAccount(account);
|
|
73
|
+
const authenticationService = new AuthenticationStatusService(apiClientOptions);
|
|
74
|
+
return authenticationService.isAuthenticated();
|
|
70
75
|
} catch (error) {
|
|
71
76
|
if (error instanceof MfaAuthenticationRequiredError) {
|
|
72
77
|
/*
|
|
@@ -47,7 +47,7 @@ describe("OnExtensionUpdateAvailableService", () => {
|
|
|
47
47
|
const webIntegrationPortWrapper = new Port(webIntegrationPort);
|
|
48
48
|
PortManager.registerPort(webIntegrationPortWrapper);
|
|
49
49
|
// mock function
|
|
50
|
-
jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => false);
|
|
50
|
+
jest.spyOn(AuthenticationStatusService.prototype, "isAuthenticated").mockImplementation(() => false);
|
|
51
51
|
jest.spyOn(browser.runtime, "reload");
|
|
52
52
|
jest.spyOn(webIntegrationPortWrapper, "emit");
|
|
53
53
|
// process
|
|
@@ -73,7 +73,7 @@ describe("OnExtensionUpdateAvailableService", () => {
|
|
|
73
73
|
expect.assertions(2);
|
|
74
74
|
// mock function
|
|
75
75
|
await MockExtension.withConfiguredAccount();
|
|
76
|
-
jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => true);
|
|
76
|
+
jest.spyOn(AuthenticationStatusService.prototype, "isAuthenticated").mockImplementation(() => true);
|
|
77
77
|
jest.spyOn(browser.runtime, "reload");
|
|
78
78
|
// process
|
|
79
79
|
await OnExtensionUpdateAvailableService.exec();
|
|
@@ -109,7 +109,7 @@ describe("OnExtensionUpdateAvailableService", () => {
|
|
|
109
109
|
PortManager.registerPort(webIntegrationPortWrapper2);
|
|
110
110
|
PortManager.registerPort(publicWebsiteSignInPortWrapper);
|
|
111
111
|
// mock function
|
|
112
|
-
jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => true);
|
|
112
|
+
jest.spyOn(AuthenticationStatusService.prototype, "isAuthenticated").mockImplementation(() => true);
|
|
113
113
|
jest
|
|
114
114
|
.spyOn(BrowserTabService, "sendMessage")
|
|
115
115
|
.mockImplementation(() => PortManager.registerPort(webIntegrationPortWrapper));
|
|
@@ -144,7 +144,7 @@ describe("OnExtensionUpdateAvailableService", () => {
|
|
|
144
144
|
expect.assertions(1);
|
|
145
145
|
// mock function
|
|
146
146
|
await MockExtension.withConfiguredAccount();
|
|
147
|
-
jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => {
|
|
147
|
+
jest.spyOn(AuthenticationStatusService.prototype, "isAuthenticated").mockImplementation(() => {
|
|
148
148
|
throw new Error("Error");
|
|
149
149
|
});
|
|
150
150
|
jest.spyOn(browser.runtime, "reload");
|
|
@@ -158,7 +158,7 @@ describe("OnExtensionUpdateAvailableService", () => {
|
|
|
158
158
|
expect.assertions(2);
|
|
159
159
|
// mock function
|
|
160
160
|
await MockExtension.withConfiguredAccount();
|
|
161
|
-
jest.spyOn(AuthenticationStatusService, "isAuthenticated").mockImplementation(() => {
|
|
161
|
+
jest.spyOn(AuthenticationStatusService.prototype, "isAuthenticated").mockImplementation(() => {
|
|
162
162
|
throw new MfaAuthenticationRequiredError();
|
|
163
163
|
});
|
|
164
164
|
jest.spyOn(browser.runtime, "reload");
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import GroupsCollection from "passbolt-styleguide/src/shared/models/entity/group/groupsCollection";
|
|
16
|
-
import { assertBoolean, assertType } from "../../utils/assertions";
|
|
16
|
+
import { assertArrayUUID, assertBoolean, assertType } from "../../utils/assertions";
|
|
17
17
|
import GroupApiService from "../api/group/groupApiService";
|
|
18
18
|
import User from "../../model/user";
|
|
19
|
+
import ExecuteConcurrentlyService from "../execute/executeConcurrentlyService";
|
|
19
20
|
|
|
20
21
|
export default class FindGroupsService {
|
|
21
22
|
/**
|
|
@@ -70,4 +71,44 @@ export default class FindGroupsService {
|
|
|
70
71
|
const contains = { groups_users: true, my_group_user: true, modifier: false };
|
|
71
72
|
return await this.findAll(contains, null, true);
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Find all groups given their ids with the data tailored for the "Share" process.
|
|
77
|
+
* Each group embeds its members (groups_users with their associated user and user profile).
|
|
78
|
+
* @param {Array<string>} groupIds
|
|
79
|
+
*/
|
|
80
|
+
async findAllByIdsForShare(groupIds) {
|
|
81
|
+
assertArrayUUID(groupIds);
|
|
82
|
+
|
|
83
|
+
const contains = { "groups_users.user.profile": true };
|
|
84
|
+
return await this.findAllByIds(groupIds, contains);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Find all groups given their ids.
|
|
89
|
+
* @param {Array<string>} groupIds
|
|
90
|
+
* @param {Object|null} [contains] optional
|
|
91
|
+
*/
|
|
92
|
+
async findAllByIds(groupIds, contains) {
|
|
93
|
+
assertArrayUUID(groupIds);
|
|
94
|
+
|
|
95
|
+
// Short-circuit an empty id list: it produces no callbacks, and ExecuteConcurrentlyService
|
|
96
|
+
// never resolves when given none (its resolve() only fires from within the per-callback loop).
|
|
97
|
+
if (groupIds.length === 0) {
|
|
98
|
+
return new GroupsCollection([]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// @todo: update to check for server capability for supporting `has-id` filter and use it instead.
|
|
102
|
+
const callbacks = groupIds.map((groupId) => {
|
|
103
|
+
return async () => {
|
|
104
|
+
const response = await this.groupApiService.get(groupId, contains);
|
|
105
|
+
return response.body;
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// @todo Later (tm). The Collection should provide this capability, ensuring that validation build rules are executed and performance is guaranteed.
|
|
110
|
+
const executeConcurrentlyService = new ExecuteConcurrentlyService();
|
|
111
|
+
const groupsCollectionDto = await executeConcurrentlyService.execute(callbacks, 5);
|
|
112
|
+
return new GroupsCollection(groupsCollectionDto, { ignoreInvalidEntity: true });
|
|
113
|
+
}
|
|
73
114
|
}
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
* @since 5.7.0
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import { v4 as uuidv4 } from "uuid";
|
|
15
16
|
import User from "../../model/user";
|
|
16
17
|
import AccountEntity from "../../model/entity/account/accountEntity";
|
|
17
18
|
import { defaultAccountDto } from "../../model/entity/account/accountEntity.test.data";
|
|
18
19
|
import GroupsCollection from "passbolt-styleguide/src/shared/models/entity/group/groupsCollection";
|
|
20
|
+
import { defaultGroupDto } from "passbolt-styleguide/src/shared/models/entity/group/groupEntity.test.data";
|
|
19
21
|
import BuildApiClientOptionsService from "../account/buildApiClientOptionsService";
|
|
20
22
|
import FindGroupsService from "./findGroupsService";
|
|
21
23
|
import MockExtension from "../../../../../test/mocks/mockExtension";
|
|
@@ -125,4 +127,84 @@ describe("FindGroupsService", () => {
|
|
|
125
127
|
expect(result.id).toBe(groupsDtos.id);
|
|
126
128
|
});
|
|
127
129
|
});
|
|
130
|
+
|
|
131
|
+
describe("::findAllByIdsForShare", () => {
|
|
132
|
+
it("should delegate to findAllByIds with the contains tailored for the share process", async () => {
|
|
133
|
+
const groupIds = [uuidv4(), uuidv4()];
|
|
134
|
+
const expectedContains = { "groups_users.user.profile": true };
|
|
135
|
+
const expectedCollection = new GroupsCollection();
|
|
136
|
+
jest.spyOn(findGroupsService, "findAllByIds").mockResolvedValue(expectedCollection);
|
|
137
|
+
|
|
138
|
+
const result = await findGroupsService.findAllByIdsForShare(groupIds);
|
|
139
|
+
|
|
140
|
+
expect(findGroupsService.findAllByIds).toHaveBeenCalledWith(groupIds, expectedContains);
|
|
141
|
+
expect(result).toBe(expectedCollection);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("should throw if the given group ids are not an array of uuids", async () => {
|
|
145
|
+
expect.assertions(3);
|
|
146
|
+
const spy = jest.spyOn(findGroupsService, "findAllByIds");
|
|
147
|
+
|
|
148
|
+
await expect(findGroupsService.findAllByIdsForShare("not-an-array")).rejects.toThrow();
|
|
149
|
+
await expect(findGroupsService.findAllByIdsForShare(["not-a-uuid"])).rejects.toThrow();
|
|
150
|
+
expect(spy).not.toHaveBeenCalled();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("::findAllByIds", () => {
|
|
155
|
+
it("should find all the groups given their ids", async () => {
|
|
156
|
+
const groupDtos = [
|
|
157
|
+
defaultGroupDto({ name: "Group 0" }),
|
|
158
|
+
defaultGroupDto({ name: "Group 1" }),
|
|
159
|
+
defaultGroupDto({ name: "Group 2" }),
|
|
160
|
+
];
|
|
161
|
+
expect.assertions(4 + groupDtos.length);
|
|
162
|
+
const groupIds = groupDtos.map((groupDto) => groupDto.id);
|
|
163
|
+
const contains = { groups_users: true, my_group_user: false, modifier: false };
|
|
164
|
+
// The API returns a single group per id, wrapped in a PassboltResponseEntity like object.
|
|
165
|
+
const spy = jest
|
|
166
|
+
.spyOn(findGroupsService.groupApiService, "get")
|
|
167
|
+
.mockImplementation(async (id) => ({ body: groupDtos.find((groupDto) => groupDto.id === id) }));
|
|
168
|
+
|
|
169
|
+
const result = await findGroupsService.findAllByIds(groupIds, contains);
|
|
170
|
+
|
|
171
|
+
expect(spy).toHaveBeenCalledTimes(groupIds.length);
|
|
172
|
+
groupIds.forEach((groupId) => expect(spy).toHaveBeenCalledWith(groupId, contains));
|
|
173
|
+
expect(result).toBeInstanceOf(GroupsCollection);
|
|
174
|
+
expect(result).toHaveLength(groupIds.length);
|
|
175
|
+
expect(result.items.map((group) => group.id).sort()).toEqual([...groupIds].sort());
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should return an empty collection without calling the API when the given group ids are empty", async () => {
|
|
179
|
+
expect.assertions(3);
|
|
180
|
+
const spy = jest.spyOn(findGroupsService.groupApiService, "get");
|
|
181
|
+
|
|
182
|
+
const result = await findGroupsService.findAllByIds([]);
|
|
183
|
+
|
|
184
|
+
expect(result).toBeInstanceOf(GroupsCollection);
|
|
185
|
+
expect(result).toHaveLength(0);
|
|
186
|
+
expect(spy).not.toHaveBeenCalled();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should reject if the API returns an invalid group", async () => {
|
|
190
|
+
const validGroupDto = defaultGroupDto();
|
|
191
|
+
const invalidGroupDto = defaultGroupDto({ name: 42 });
|
|
192
|
+
const groupIds = [validGroupDto.id, invalidGroupDto.id];
|
|
193
|
+
jest
|
|
194
|
+
.spyOn(findGroupsService.groupApiService, "get")
|
|
195
|
+
.mockImplementation(async (id) => ({ body: id === validGroupDto.id ? validGroupDto : invalidGroupDto }));
|
|
196
|
+
|
|
197
|
+
const collection = await findGroupsService.findAllByIds(groupIds);
|
|
198
|
+
expect(collection).toHaveLength(1);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should throw if the given group ids are not an array of uuids", async () => {
|
|
202
|
+
expect.assertions(3);
|
|
203
|
+
const spy = jest.spyOn(findGroupsService.groupApiService, "get");
|
|
204
|
+
|
|
205
|
+
await expect(findGroupsService.findAllByIds("not-an-array")).rejects.toThrow();
|
|
206
|
+
await expect(findGroupsService.findAllByIds(["not-a-uuid"])).rejects.toThrow();
|
|
207
|
+
expect(spy).not.toHaveBeenCalled();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
128
210
|
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.13.0
|
|
13
|
+
*/
|
|
14
|
+
import { OpenpgpAssertion } from "../../utils/openpgp/openpgpAssertions";
|
|
15
|
+
import EncryptMessageService from "../crypto/encryptMessageService";
|
|
16
|
+
import DecryptMessageService from "../crypto/decryptMessageService";
|
|
17
|
+
import SecretEntity from "passbolt-styleguide/src/shared/models/entity/secret/secretEntity";
|
|
18
|
+
import GroupUpdateSecretsCollection from "../../model/entity/secret/groupUpdate/groupUpdateSecretsCollection";
|
|
19
|
+
import i18n from "../../sdk/i18n";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Shared decrypt/encrypt logic for the "add users to group" flow.
|
|
23
|
+
* It is called by both the MV2 path (GroupUpdateService, in the service worker / background page)
|
|
24
|
+
* and the MV3 path (AddUsersToGroupOffscreenService, in the offscreen document) so the
|
|
25
|
+
* cryptographic loop lives in a single place. The caller provides an `onProgress` callback to
|
|
26
|
+
* decide how progress is surfaced (direct progress service update vs throttled offscreen message).
|
|
27
|
+
*/
|
|
28
|
+
class GroupUpdateSecretsCryptoService {
|
|
29
|
+
/**
|
|
30
|
+
* Decrypt a collection of secrets.
|
|
31
|
+
* @param {openpgp.PrivateKey} privateKey The logged in user private key
|
|
32
|
+
* @param {GroupUpdateSecretsCollection} secretsCollection The collection of secrets to decrypt
|
|
33
|
+
* @param {function(string):void} [onProgress] Called after each secret with the progress message
|
|
34
|
+
* @returns {Promise<object>} Decrypted secrets organized as {[resourceId]: secretDecrypted, ...}
|
|
35
|
+
*/
|
|
36
|
+
static async decryptSecrets(privateKey, secretsCollection, onProgress) {
|
|
37
|
+
const result = [];
|
|
38
|
+
const collectionLength = secretsCollection.length;
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < collectionLength; i++) {
|
|
41
|
+
const secret = secretsCollection.items[i];
|
|
42
|
+
const secretMessage = await OpenpgpAssertion.readMessageOrFail(secret.data);
|
|
43
|
+
result[secret.resourceId] = await DecryptMessageService.decrypt(secretMessage, privateKey);
|
|
44
|
+
|
|
45
|
+
await onProgress?.(i18n.t("Decrypting {{counter}}/{{total}}", { counter: i + 1, total: collectionLength }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Encrypt a collection of needed secrets.
|
|
53
|
+
* @param {openpgp.PrivateKey} privateKey The logged in user private key
|
|
54
|
+
* @param {object} usersPublicKeys The users public armored keys organized as {[userId]: armoredKey, ...}
|
|
55
|
+
* @param {NeededSecretsCollection} neededSecretsCollection A collection of needed secret
|
|
56
|
+
* @param {object} decryptedSecrets The decrypted secrets organized as {[resourceId]: secretDecrypted, ...}
|
|
57
|
+
* @param {function(string):void} [onProgress] Called after each secret with the progress message
|
|
58
|
+
* @returns {Promise<GroupUpdateSecretsCollection>}
|
|
59
|
+
*/
|
|
60
|
+
static async encryptSecrets(privateKey, usersPublicKeys, neededSecretsCollection, decryptedSecrets, onProgress) {
|
|
61
|
+
const groupUpdateSecrets = new GroupUpdateSecretsCollection([]);
|
|
62
|
+
const collectionLength = neededSecretsCollection.length;
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < collectionLength; i++) {
|
|
65
|
+
const neededSecret = neededSecretsCollection.items[i];
|
|
66
|
+
const user_id = neededSecret.userId;
|
|
67
|
+
const resource_id = neededSecret.resourceId;
|
|
68
|
+
|
|
69
|
+
const encryptionKey = await OpenpgpAssertion.readKeyOrFail(usersPublicKeys[user_id]);
|
|
70
|
+
const data = await EncryptMessageService.encrypt(decryptedSecrets[resource_id], encryptionKey, [privateKey]);
|
|
71
|
+
|
|
72
|
+
const secret = new SecretEntity({ resource_id, user_id, data });
|
|
73
|
+
groupUpdateSecrets.push(secret);
|
|
74
|
+
|
|
75
|
+
onProgress?.(i18n.t("Encrypting {{counter}}/{{total}}", { counter: i + 1, total: collectionLength }));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return groupUpdateSecrets;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default GroupUpdateSecretsCryptoService;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passbolt ~ Open source password manager for teams
|
|
3
|
+
* Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
4
|
+
*
|
|
5
|
+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
|
6
|
+
* For full copyright and license information, please see the LICENSE.txt
|
|
7
|
+
* Redistributions of files must retain the above copyright notice.
|
|
8
|
+
*
|
|
9
|
+
* @copyright Copyright (c) Passbolt SA (https://www.passbolt.com)
|
|
10
|
+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
|
11
|
+
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
|
+
* @since 5.13.0
|
|
13
|
+
*/
|
|
14
|
+
import { v4 as uuidv4 } from "uuid";
|
|
15
|
+
import { pgpKeys } from "passbolt-styleguide/test/fixture/pgpKeys/keys";
|
|
16
|
+
import { plaintextSecretPasswordAndDescriptionDto } from "passbolt-styleguide/src/shared/models/entity/plaintextSecret/plaintextSecretEntity.test.data";
|
|
17
|
+
import GroupUpdateSecretsCryptoService from "./groupUpdateSecretsCryptoService";
|
|
18
|
+
import { OpenpgpAssertion } from "../../utils/openpgp/openpgpAssertions";
|
|
19
|
+
import EncryptMessageService from "../crypto/encryptMessageService";
|
|
20
|
+
import DecryptMessageService from "../crypto/decryptMessageService";
|
|
21
|
+
import GroupUpdateSecretsCollection from "../../model/entity/secret/groupUpdate/groupUpdateSecretsCollection";
|
|
22
|
+
import NeededSecretsCollection from "../../model/entity/secret/needed/neededSecretsCollection";
|
|
23
|
+
import { defaultResourcesSecretsDtos } from "../../model/entity/secret/groupUpdate/groupUpdateSecretsCollection.test.data";
|
|
24
|
+
|
|
25
|
+
describe("GroupUpdateSecretsCryptoService", () => {
|
|
26
|
+
describe("::decryptSecrets", () => {
|
|
27
|
+
it("should decrypt the secrets and report progress for each one", async () => {
|
|
28
|
+
expect.assertions(3);
|
|
29
|
+
|
|
30
|
+
const originalDecryptedSecret = plaintextSecretPasswordAndDescriptionDto();
|
|
31
|
+
const adaPublicKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.ada.public);
|
|
32
|
+
const secretsDtos = defaultResourcesSecretsDtos(1);
|
|
33
|
+
secretsDtos[0].data = await EncryptMessageService.encrypt(JSON.stringify(originalDecryptedSecret), adaPublicKey);
|
|
34
|
+
const secrets = new GroupUpdateSecretsCollection(secretsDtos, { validate: false });
|
|
35
|
+
const privateKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.ada.private_decrypted);
|
|
36
|
+
const onProgress = jest.fn();
|
|
37
|
+
|
|
38
|
+
const result = await GroupUpdateSecretsCryptoService.decryptSecrets(privateKey, secrets, onProgress);
|
|
39
|
+
|
|
40
|
+
expect(JSON.parse(result[secretsDtos[0].resource_id])).toStrictEqual(originalDecryptedSecret);
|
|
41
|
+
expect(onProgress).toHaveBeenCalledTimes(1);
|
|
42
|
+
expect(onProgress).toHaveBeenCalledWith("Decrypting 1/1");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("::encryptSecrets", () => {
|
|
47
|
+
it("should encrypt the needed secrets for the recipient and report progress for each one", async () => {
|
|
48
|
+
expect.assertions(4);
|
|
49
|
+
|
|
50
|
+
const originalDecryptedSecret = plaintextSecretPasswordAndDescriptionDto();
|
|
51
|
+
const userId = uuidv4();
|
|
52
|
+
const resourceId = uuidv4();
|
|
53
|
+
const decryptedSecrets = { [resourceId]: JSON.stringify(originalDecryptedSecret) };
|
|
54
|
+
const neededSecrets = new NeededSecretsCollection([{ user_id: userId, resource_id: resourceId }], {
|
|
55
|
+
validate: false,
|
|
56
|
+
});
|
|
57
|
+
const usersPublicKeys = { [userId]: pgpKeys.betty.public };
|
|
58
|
+
const privateKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.ada.private_decrypted);
|
|
59
|
+
const onProgress = jest.fn();
|
|
60
|
+
|
|
61
|
+
const result = await GroupUpdateSecretsCryptoService.encryptSecrets(
|
|
62
|
+
privateKey,
|
|
63
|
+
usersPublicKeys,
|
|
64
|
+
neededSecrets,
|
|
65
|
+
decryptedSecrets,
|
|
66
|
+
onProgress,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const decryptedData = await DecryptMessageService.decrypt(
|
|
70
|
+
await OpenpgpAssertion.readMessageOrFail(result.items[0].data),
|
|
71
|
+
await OpenpgpAssertion.readKeyOrFail(pgpKeys.betty.private_decrypted),
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
expect(JSON.parse(decryptedData)).toStrictEqual(originalDecryptedSecret);
|
|
75
|
+
expect(result.items[0].userId).toEqual(userId);
|
|
76
|
+
expect(onProgress).toHaveBeenCalledTimes(1);
|
|
77
|
+
expect(onProgress).toHaveBeenCalledWith("Encrypting 1/1");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should not throw when no progress callback is provided", async () => {
|
|
81
|
+
expect.assertions(1);
|
|
82
|
+
|
|
83
|
+
const userId = uuidv4();
|
|
84
|
+
const resourceId = uuidv4();
|
|
85
|
+
const decryptedSecrets = { [resourceId]: JSON.stringify(plaintextSecretPasswordAndDescriptionDto()) };
|
|
86
|
+
const neededSecrets = new NeededSecretsCollection([{ user_id: userId, resource_id: resourceId }], {
|
|
87
|
+
validate: false,
|
|
88
|
+
});
|
|
89
|
+
const usersPublicKeys = { [userId]: pgpKeys.betty.public };
|
|
90
|
+
const privateKey = await OpenpgpAssertion.readKeyOrFail(pgpKeys.ada.private_decrypted);
|
|
91
|
+
|
|
92
|
+
const result = await GroupUpdateSecretsCryptoService.encryptSecrets(
|
|
93
|
+
privateKey,
|
|
94
|
+
usersPublicKeys,
|
|
95
|
+
neededSecrets,
|
|
96
|
+
decryptedSecrets,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(result.items).toHaveLength(1);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -11,21 +11,19 @@
|
|
|
11
11
|
* @link https://www.passbolt.com Passbolt(tm)
|
|
12
12
|
* @since 4.10.1
|
|
13
13
|
*/
|
|
14
|
-
import { OpenpgpAssertion } from "../../utils/openpgp/openpgpAssertions";
|
|
15
14
|
import Keyring from "../../model/keyring";
|
|
16
|
-
import EncryptMessageService from "../../service/crypto/encryptMessageService";
|
|
17
|
-
import DecryptMessageService from "../../service/crypto/decryptMessageService";
|
|
18
15
|
import GroupModel from "../../model/group/groupModel";
|
|
19
16
|
import GroupEntity from "passbolt-styleguide/src/shared/models/entity/group/groupEntity";
|
|
20
17
|
import GroupUpdateEntity from "../../model/entity/group/update/groupUpdateEntity";
|
|
21
18
|
import i18n from "../../sdk/i18n";
|
|
22
|
-
import SecretEntity from "passbolt-styleguide/src/shared/models/entity/secret/secretEntity";
|
|
23
19
|
import GroupUpdateSecretsCollection from "../../model/entity/secret/groupUpdate/groupUpdateSecretsCollection";
|
|
24
20
|
import DecryptPrivateKeyService from "../crypto/decryptPrivateKeyService";
|
|
25
21
|
import { assertString, assertType } from "../../utils/assertions";
|
|
26
22
|
import GroupUpdatesCollection from "../../model/entity/group/update/groupUpdatesCollection";
|
|
27
23
|
import GroupLocalStorage from "../local_storage/groupLocalStorage";
|
|
28
24
|
import GroupApiService from "../api/group/groupApiService";
|
|
25
|
+
import GroupUpdateSecretsCryptoService from "./groupUpdateSecretsCryptoService";
|
|
26
|
+
import { RequestAddUsersToGroupOffscreenService } from "../../../../chrome-mv3/serviceWorker/service/addUsersToGroup/requestAddUsersToGroupOffscreenService";
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
29
|
* Progress goals are:
|
|
@@ -37,7 +35,6 @@ import GroupApiService from "../api/group/groupApiService";
|
|
|
37
35
|
* - Done
|
|
38
36
|
*/
|
|
39
37
|
const PROGRESS_GOAL = 6;
|
|
40
|
-
const YIELD_INTERVAL_MS = 8000; // PB-51648 yield reduce to 8s due to chrome 148 more strict to ping service worker
|
|
41
38
|
|
|
42
39
|
class GroupUpdateService {
|
|
43
40
|
/**
|
|
@@ -103,9 +100,18 @@ class GroupUpdateService {
|
|
|
103
100
|
*/
|
|
104
101
|
async encryptNeededSecrets(passphrase, groupUpdateDryRunResultEntity) {
|
|
105
102
|
this.progressService.finishStep(i18n.t("Encrypt required secrets for new users"), true);
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
103
|
+
|
|
104
|
+
const isMV3 = chrome.runtime.getManifest().manifest_version === 3;
|
|
105
|
+
if (isMV3) {
|
|
106
|
+
return await this.decryptAndEncryptSecretsFromOffscreen(passphrase, groupUpdateDryRunResultEntity);
|
|
107
|
+
} else {
|
|
108
|
+
const privateKey = await DecryptPrivateKeyService.decryptArmoredKey(
|
|
109
|
+
this.account.userPrivateArmoredKey,
|
|
110
|
+
passphrase,
|
|
111
|
+
);
|
|
112
|
+
const decryptedSecrets = await this.decryptSecrets(privateKey, groupUpdateDryRunResultEntity.secrets);
|
|
113
|
+
return await this.encryptSecrets(privateKey, groupUpdateDryRunResultEntity.neededSecrets, decryptedSecrets);
|
|
114
|
+
}
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
/**
|
|
@@ -117,44 +123,16 @@ class GroupUpdateService {
|
|
|
117
123
|
* @private
|
|
118
124
|
*/
|
|
119
125
|
async encryptSecrets(privateKey, neededSecretsCollection, decryptedSecrets) {
|
|
120
|
-
const groupUpdateSecrets = new GroupUpdateSecretsCollection([]);
|
|
121
|
-
let lastYield = Date.now();
|
|
122
126
|
this.progressService.finishStep(i18n.t("Synchronizing keyring"), true);
|
|
123
127
|
const usersPublicKeys = await this.retrieveAndReadUserPublicKeys(neededSecretsCollection);
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
i18n.t("Encrypting {{counter}}/{{total}}", { counter: i + 1, total: collectionLength }),
|
|
133
|
-
);
|
|
134
|
-
const data = await EncryptMessageService.encrypt(decryptedSecrets[resource_id], usersPublicKeys[user_id], [
|
|
135
|
-
privateKey,
|
|
136
|
-
]);
|
|
137
|
-
|
|
138
|
-
const secret = new SecretEntity({ resource_id, user_id, data });
|
|
139
|
-
groupUpdateSecrets.push(secret);
|
|
140
|
-
|
|
141
|
-
/*
|
|
142
|
-
* PB-51434
|
|
143
|
-
* Force a timeout promise to let the service worker respond to chromium
|
|
144
|
-
*
|
|
145
|
-
* Starting with Chrome 147, the regular check of the service worker on the event lop seems more strict
|
|
146
|
-
* If the service worker do a process that consume too much time and performance that it can not respond
|
|
147
|
-
* It will be killed by the browser as it is not responding at time.
|
|
148
|
-
*
|
|
149
|
-
* Using promise set timeout will solve the issue it releases the even loop enough time to respond
|
|
150
|
-
*/
|
|
151
|
-
if (Date.now() - lastYield >= YIELD_INTERVAL_MS) {
|
|
152
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
153
|
-
lastYield = Date.now();
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return groupUpdateSecrets;
|
|
129
|
+
return GroupUpdateSecretsCryptoService.encryptSecrets(
|
|
130
|
+
privateKey,
|
|
131
|
+
usersPublicKeys,
|
|
132
|
+
neededSecretsCollection,
|
|
133
|
+
decryptedSecrets,
|
|
134
|
+
(message) => this.progressService.updateStepMessage(message),
|
|
135
|
+
);
|
|
158
136
|
}
|
|
159
137
|
|
|
160
138
|
/**
|
|
@@ -165,36 +143,9 @@ class GroupUpdateService {
|
|
|
165
143
|
* @private
|
|
166
144
|
*/
|
|
167
145
|
async decryptSecrets(privateKey, secretsCollection) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const collectionLength = secretsCollection.length;
|
|
172
|
-
for (let i = 0; i < collectionLength; i++) {
|
|
173
|
-
const secret = secretsCollection.items[i];
|
|
174
|
-
const secretMessage = await OpenpgpAssertion.readMessageOrFail(secret.data);
|
|
175
|
-
|
|
176
|
-
this.progressService.updateStepMessage(
|
|
177
|
-
i18n.t("Decrypting {{counter}}/{{total}}", { counter: i + 1, total: collectionLength }),
|
|
178
|
-
);
|
|
179
|
-
result[secret.resourceId] = await DecryptMessageService.decrypt(secretMessage, privateKey);
|
|
180
|
-
|
|
181
|
-
/*
|
|
182
|
-
* PB-51434
|
|
183
|
-
* Force a timeout promise to let the service worker respond to chromium
|
|
184
|
-
*
|
|
185
|
-
* Starting with Chrome 147, the regular check of the service worker on the event lop seems more strict
|
|
186
|
-
* If the service worker do a process that consume too much time and performance that it can not respond
|
|
187
|
-
* It will be killed by the browser as it is not responding at time.
|
|
188
|
-
*
|
|
189
|
-
* Using promise set timeout will solve the issue it releases the even loop enough time to respond
|
|
190
|
-
*/
|
|
191
|
-
if (Date.now() - lastYield >= YIELD_INTERVAL_MS) {
|
|
192
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
193
|
-
lastYield = Date.now();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return result;
|
|
146
|
+
return GroupUpdateSecretsCryptoService.decryptSecrets(privateKey, secretsCollection, (message) =>
|
|
147
|
+
this.progressService.updateStepMessage(message),
|
|
148
|
+
);
|
|
198
149
|
}
|
|
199
150
|
|
|
200
151
|
/**
|
|
@@ -225,10 +176,10 @@ class GroupUpdateService {
|
|
|
225
176
|
}
|
|
226
177
|
|
|
227
178
|
/**
|
|
228
|
-
* Retrieve
|
|
179
|
+
* Retrieve the user public keys.
|
|
229
180
|
* @param {NeededSecretsCollection} neededSecrets The needed secrets
|
|
230
|
-
* @returns {Promise<object>} User public
|
|
231
|
-
* and the value contains the user
|
|
181
|
+
* @returns {Promise<object>} User public keys organized in an object where the property name represents the user id,
|
|
182
|
+
* and the value contains the user public armored key.
|
|
232
183
|
* @private
|
|
233
184
|
*/
|
|
234
185
|
async retrieveAndReadUserPublicKeys(neededSecrets) {
|
|
@@ -239,12 +190,32 @@ class GroupUpdateService {
|
|
|
239
190
|
await keyring.sync();
|
|
240
191
|
|
|
241
192
|
for (const userId of userIds) {
|
|
242
|
-
|
|
243
|
-
userOpenpgpPublicKeys[userId] =
|
|
193
|
+
// Keep armored key to send it through a message for offscreen
|
|
194
|
+
userOpenpgpPublicKeys[userId] = keyring.findPublic(userId).armoredKey;
|
|
244
195
|
}
|
|
245
196
|
|
|
246
197
|
return userOpenpgpPublicKeys;
|
|
247
198
|
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Delegate the decryption and encryption secrets to the offscreen
|
|
202
|
+
* @param {string} passphrase
|
|
203
|
+
* @param {GroupUpdateDryRunResultEntity} groupUpdateDryRunResultEntity
|
|
204
|
+
* @return {Promise<GroupUpdateSecretsCollection>}
|
|
205
|
+
*/
|
|
206
|
+
async decryptAndEncryptSecretsFromOffscreen(passphrase, groupUpdateDryRunResultEntity) {
|
|
207
|
+
this.progressService.finishStep(i18n.t("Synchronizing keyring"), true);
|
|
208
|
+
// User public key are stored in local storage that is not accessible from offscreen
|
|
209
|
+
const usersPublicKeys = await this.retrieveAndReadUserPublicKeys(groupUpdateDryRunResultEntity.neededSecrets);
|
|
210
|
+
const groupUpdateSecrets = await RequestAddUsersToGroupOffscreenService.decryptAndEncryptSecrets(
|
|
211
|
+
this.account,
|
|
212
|
+
passphrase,
|
|
213
|
+
groupUpdateDryRunResultEntity,
|
|
214
|
+
usersPublicKeys,
|
|
215
|
+
this.progressService,
|
|
216
|
+
);
|
|
217
|
+
return new GroupUpdateSecretsCollection(groupUpdateSecrets, { validate: false });
|
|
218
|
+
}
|
|
248
219
|
}
|
|
249
220
|
|
|
250
221
|
export default GroupUpdateService;
|