From c2877523c8f7a2935311b48f8ea0bbdb0667ad3b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:14:18 -0400 Subject: [PATCH 01/16] Bumped web version to 2023.9.1 (#6365) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/package.json | 2 +- package-lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 781d5d75265..ef42fafd79a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2023.9.0", + "version": "2023.9.1", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/package-lock.json b/package-lock.json index 96e308fb6ba..86ef7a3579a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -260,7 +260,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2023.9.0" + "version": "2023.9.1" }, "libs/angular": { "name": "@bitwarden/angular", From c02fc7abe1c372f15828e46f51ee308cfdf48bb6 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 21 Sep 2023 17:42:49 -0500 Subject: [PATCH 02/16] [PM-2481] [PM-3465] Integrate Shadow DOM Support Into Autofill v2 and Optimize Collection of Page Details (special thanks to @RafaelKr) (#6141) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * split up autofill.ts, first pass * remove modification tracking comments * lessen and localize eslint disables * additional typing and formatting * update autofill v2 with PR #5364 changes (update/i18n confirm dialogs) * update autofill v2 with PR #4155 changes (add autofill support for textarea) Co-Authored-By: Manuel * move commonly used string values to constants * ts cleanup * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Working through autofill collect method * [PM-2130] Marking Removal of documentUUID as dead code * [PM-2130] Refining the implementation of collect and moving broken out utils back into class implementation * [PM-2130] Applying small refactors to AutofillCollect * [PM-2130] Refining the implementation of getAutofillFieldLabelTag to help with readability of the method * [PM-2130] Implementing jest tests for AutofillCollect methods * [PM-2130] Refining implementation for AutofillCollect * [PM-2200] Unit tests for autofill content script utilities with slight refactors (#5544) * add unit tests for urlNotSecure * add test coverage command * add unit tests for canSeeElementToStyle * canSeeElementToStyle should not return true if `animateTheFilling` or `currentEl` is false * add tests for selectAllFromDoc and getElementByOpId * clean up getElementByOpId * address some typing issues * add tests for setValueForElementByEvent, setValueForElement, and doSimpleSetByQuery * clean up setValueForElement and setValueForElementByEvent * more typescript cleanup * add tests for doClickByOpId and touchAllPasswordFields * add tests for doFocusByOpId and doClickByQuery * misc fill cleanup * move functions between collect and fill utils and replace getElementForOPID for duplicate getElementByOpId * add tests for isKnownTag and isElementVisible * rename addProp and remove redundant focusElement in favor of doFocusElement * cleanup * fix checkNodeType * add tests for shiftForLeftLabel * clean up and rename checkNodeType, isKnownTag, and shiftForLeftLabel * add tests for getFormElements * clean up getFormElements * add tests for getElementAttrValue, getElementValue, getSelectElementOptions, getLabelTop, and queryDoc * clean up and rename queryDoc to queryDocument * misc cleanup and rename getElementAttrValue to getPropertyOrAttribute * rebase cleanup * prettier formatting * [PM-2130] Fixing linting issues * [PM-2130] Fixing linting issues * [PM-2130] Migrating implementation for collect methods and tests for those methods into AutofillCollect context * [PM-2130] Migrating getPropertyOrAttribute method from utils to AutofillCollect * [PM-2130] Continuing migration of methods from collect utils into AutofillCollect * [PM-2130] Rework of isViewable method to better handle behavior for how we identify if an element is currently within the viewport * [PM-2130] Filling out implementation of autofill-insert * [PM-2130] Refining AutofillInsert * [PM-2130] Implementing jest tests for AutofillCollect methods and breaking out visibility related logic to a separate service * [PM-2130] Fixing jest tests for AutofillCollect * [PM-2130] Fixing jest tests for AutofillInit * [PM-2130] Adjusting how the AutofillFieldVisibilityService class is used in AutofillCollect * [PM-2130] Working through AutofillInsert implementation * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Applying fix for IntersectionObserver when triggering behavior in Safari and fixing issue with how we trigger an input event shortly after filling in a field * [PM-2130] Refactoring AutofillCollect to service CollectAutofillContentService * [PM-2130] Refactoring AutofillInsert to service InsertAutofillContentService * [PM-2130] Further organization of implementation * [PM-2130] Filling out missing jest test for AutofillInit.fillForm method * [PM-2130] Migrating the last of the collect jest tests to InsertAutofillContentService * [PM-2130] Further refactoring of elements including typing information * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Organization and refactoring of methods within InsertAutofillContent * [PM-2130] Implementation of jest tests for InsertAutofillContentService * [PM-2130] Implementation of Jest Test for IntertAutofillContentService * [PM-2130] Finalizing migration of methods and jest tests from util files into Autofill serivces * [PM-2130] Cleaning up dead code comments * [PM-2130] Removing unnecessary constants * [PM-2130] Finalizing jest tests for InsertAutofillContentService * [PM-2130] Refactoring FieldVisibiltyService to DomElementVisibilityService to allow service to act in a more general manner * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Breaking out the callback method used to resolve the IntersectionObserver promise * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2747] Add Support for Feature Flag of Autofill Version * [PM-2747] Adding Support for Manifest v3 within the implementation * [PM-2747] Modifying how the feature flag for autofill is named * [PM-2747] Modifying main.background.ts to load the ConfigApiService correctly * [PM-2747] Refactoring trigger of autofill scripts to be a simple immediately invoked function * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2130] Applying changes required for PM-2762 to implementation, and ensuring jest tests exist to validate the behavior * [PM-2747] Modifying how we inject the autofill scripts to ensure we are injecting into all frames within a page * [PM-2130] Removing usage of IntersectionObserver when identifying element visibility due to broken interactions with React Components * [PM-2130] Fixing issue found when attempting to capture the elementAtCenterPoint in determining file visibility * Refining implementation for ShadowDOM fix * update tests * cleanup * [PM-3285] Autofill v2 Feature Branch * [PM-2100] Create Unit Test Suite for autofill.service.ts (#5371) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2747] Applying a fix for a race condition that can occur when loading the notification bar and autofiller script login * [PM-2747] Reverting removal of autofill npm action. Now this will force usage of autofill-v2 regardless of whether a feature flag is set or not * [PM-2747] Fixing logic error incorporated when merging in master * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing issue with autofill service unit tests * [PM-2747] Fixing issue present with notification bar merge * [PM-2130] Fixing test test for when we need to handle a password reprompt * [PM-3465] Optimization of CollectPageDetails Message within Autofill * [PM-3465] Implementing caching for CollectPage details call * [PM-3465] Implementing caching for CollectPage details call * [PM-3465] Implementing method for ensuring that getPageDetails is not called when no fields appear within a frame * [PM-3465] Implementing Mutation Observer to handle updating autofill fields when DOM updates * [PM-2747] Fixing wording for webpack script * [PM-2130] - Audit, Modularize, and Refactor Core autofill.js File (#5453) * split up autofill.ts, first pass * remove modification tracking comments * lessen and localize eslint disables * additional typing and formatting * update autofill v2 with PR #5364 changes (update/i18n confirm dialogs) * update autofill v2 with PR #4155 changes (add autofill support for textarea) Co-Authored-By: Manuel * move commonly used string values to constants * ts cleanup * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Working through autofill collect method * [PM-2130] Marking Removal of documentUUID as dead code * [PM-2130] Refining the implementation of collect and moving broken out utils back into class implementation * [PM-2130] Applying small refactors to AutofillCollect * [PM-2130] Refining the implementation of getAutofillFieldLabelTag to help with readability of the method * [PM-2130] Implementing jest tests for AutofillCollect methods * [PM-2130] Refining implementation for AutofillCollect * [PM-2200] Unit tests for autofill content script utilities with slight refactors (#5544) * add unit tests for urlNotSecure * add test coverage command * add unit tests for canSeeElementToStyle * canSeeElementToStyle should not return true if `animateTheFilling` or `currentEl` is false * add tests for selectAllFromDoc and getElementByOpId * clean up getElementByOpId * address some typing issues * add tests for setValueForElementByEvent, setValueForElement, and doSimpleSetByQuery * clean up setValueForElement and setValueForElementByEvent * more typescript cleanup * add tests for doClickByOpId and touchAllPasswordFields * add tests for doFocusByOpId and doClickByQuery * misc fill cleanup * move functions between collect and fill utils and replace getElementForOPID for duplicate getElementByOpId * add tests for isKnownTag and isElementVisible * rename addProp and remove redundant focusElement in favor of doFocusElement * cleanup * fix checkNodeType * add tests for shiftForLeftLabel * clean up and rename checkNodeType, isKnownTag, and shiftForLeftLabel * add tests for getFormElements * clean up getFormElements * add tests for getElementAttrValue, getElementValue, getSelectElementOptions, getLabelTop, and queryDoc * clean up and rename queryDoc to queryDocument * misc cleanup and rename getElementAttrValue to getPropertyOrAttribute * rebase cleanup * prettier formatting * [PM-2130] Fixing linting issues * [PM-2130] Fixing linting issues * [PM-2130] Migrating implementation for collect methods and tests for those methods into AutofillCollect context * [PM-2130] Migrating getPropertyOrAttribute method from utils to AutofillCollect * [PM-2130] Continuing migration of methods from collect utils into AutofillCollect * [PM-2130] Rework of isViewable method to better handle behavior for how we identify if an element is currently within the viewport * [PM-2130] Filling out implementation of autofill-insert * [PM-2130] Refining AutofillInsert * [PM-2130] Implementing jest tests for AutofillCollect methods and breaking out visibility related logic to a separate service * [PM-2130] Fixing jest tests for AutofillCollect * [PM-2130] Fixing jest tests for AutofillInit * [PM-2130] Adjusting how the AutofillFieldVisibilityService class is used in AutofillCollect * [PM-2130] Working through AutofillInsert implementation * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Applying fix for IntersectionObserver when triggering behavior in Safari and fixing issue with how we trigger an input event shortly after filling in a field * [PM-2130] Refactoring AutofillCollect to service CollectAutofillContentService * [PM-2130] Refactoring AutofillInsert to service InsertAutofillContentService * [PM-2130] Further organization of implementation * [PM-2130] Filling out missing jest test for AutofillInit.fillForm method * [PM-2130] Migrating the last of the collect jest tests to InsertAutofillContentService * [PM-2130] Further refactoring of elements including typing information * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Organization and refactoring of methods within InsertAutofillContent * [PM-2130] Implementation of jest tests for InsertAutofillContentService * [PM-2130] Implementation of Jest Test for IntertAutofillContentService * [PM-2130] Finalizing migration of methods and jest tests from util files into Autofill serivces * [PM-2130] Cleaning up dead code comments * [PM-2130] Removing unnecessary constants * [PM-2130] Finalizing jest tests for InsertAutofillContentService * [PM-2130] Refactoring FieldVisibiltyService to DomElementVisibilityService to allow service to act in a more general manner * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Breaking out the callback method used to resolve the IntersectionObserver promise * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Applying changes required for PM-2762 to implementation, and ensuring jest tests exist to validate the behavior * [PM-2130] Removing usage of IntersectionObserver when identifying element visibility due to broken interactions with React Components * [PM-2130] Fixing issue found when attempting to capture the elementAtCenterPoint in determining file visibility * [PM-2100] Create Unit Test Suite for autofill.service.ts (#5371) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing test test for when we need to handle a password reprompt --------- Co-authored-by: Manuel Co-authored-by: Cesar Gonzalez Co-authored-by: Cesar Gonzalez * [PM-2747] Finanlizing implementation of attribute updates on cached values * [PM-3465] Finalizing implementation of mutation observer behavior and CollectPageDetails optimization * [PM-3465] Adding jest tests for introduced functionality * [PM-3465] Finalizing jest tests and comments within implementation * [PM-3465] Removing a TODO by incorrporating a method for deep querying for a password field element * [PM-3465] Removing a TODO by incorrporating a method for deep querying for a password field element * [PM-3285] Migrating Changes from PM-1407 into autofill v2 refactor implementation * [PM-2747] Addressing stylistic changes requested from code review * [PM-2747] Add Support for Feature Flag of Autofill Version (#5695) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * split up autofill.ts, first pass * remove modification tracking comments * lessen and localize eslint disables * additional typing and formatting * update autofill v2 with PR #5364 changes (update/i18n confirm dialogs) * update autofill v2 with PR #4155 changes (add autofill support for textarea) Co-Authored-By: Manuel * move commonly used string values to constants * ts cleanup * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Starting work to re-architect autofillv2.ts * [PM-2130] Working through autofill collect method * [PM-2130] Marking Removal of documentUUID as dead code * [PM-2130] Refining the implementation of collect and moving broken out utils back into class implementation * [PM-2130] Applying small refactors to AutofillCollect * [PM-2130] Refining the implementation of getAutofillFieldLabelTag to help with readability of the method * [PM-2130] Implementing jest tests for AutofillCollect methods * [PM-2130] Refining implementation for AutofillCollect * [PM-2200] Unit tests for autofill content script utilities with slight refactors (#5544) * add unit tests for urlNotSecure * add test coverage command * add unit tests for canSeeElementToStyle * canSeeElementToStyle should not return true if `animateTheFilling` or `currentEl` is false * add tests for selectAllFromDoc and getElementByOpId * clean up getElementByOpId * address some typing issues * add tests for setValueForElementByEvent, setValueForElement, and doSimpleSetByQuery * clean up setValueForElement and setValueForElementByEvent * more typescript cleanup * add tests for doClickByOpId and touchAllPasswordFields * add tests for doFocusByOpId and doClickByQuery * misc fill cleanup * move functions between collect and fill utils and replace getElementForOPID for duplicate getElementByOpId * add tests for isKnownTag and isElementVisible * rename addProp and remove redundant focusElement in favor of doFocusElement * cleanup * fix checkNodeType * add tests for shiftForLeftLabel * clean up and rename checkNodeType, isKnownTag, and shiftForLeftLabel * add tests for getFormElements * clean up getFormElements * add tests for getElementAttrValue, getElementValue, getSelectElementOptions, getLabelTop, and queryDoc * clean up and rename queryDoc to queryDocument * misc cleanup and rename getElementAttrValue to getPropertyOrAttribute * rebase cleanup * prettier formatting * [PM-2130] Fixing linting issues * [PM-2130] Fixing linting issues * [PM-2130] Migrating implementation for collect methods and tests for those methods into AutofillCollect context * [PM-2130] Migrating getPropertyOrAttribute method from utils to AutofillCollect * [PM-2130] Continuing migration of methods from collect utils into AutofillCollect * [PM-2130] Rework of isViewable method to better handle behavior for how we identify if an element is currently within the viewport * [PM-2130] Filling out implementation of autofill-insert * [PM-2130] Refining AutofillInsert * [PM-2130] Implementing jest tests for AutofillCollect methods and breaking out visibility related logic to a separate service * [PM-2130] Fixing jest tests for AutofillCollect * [PM-2130] Fixing jest tests for AutofillInit * [PM-2130] Adjusting how the AutofillFieldVisibilityService class is used in AutofillCollect * [PM-2130] Working through AutofillInsert implementation * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Migrating methods from fill.ts to AutofillInsert * [PM-2130] Applying fix for IntersectionObserver when triggering behavior in Safari and fixing issue with how we trigger an input event shortly after filling in a field * [PM-2130] Refactoring AutofillCollect to service CollectAutofillContentService * [PM-2130] Refactoring AutofillInsert to service InsertAutofillContentService * [PM-2130] Further organization of implementation * [PM-2130] Filling out missing jest test for AutofillInit.fillForm method * [PM-2130] Migrating the last of the collect jest tests to InsertAutofillContentService * [PM-2130] Further refactoring of elements including typing information * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Implementing jest tests for InsertAutofillContentService * [PM-2130] Organization and refactoring of methods within InsertAutofillContent * [PM-2130] Implementation of jest tests for InsertAutofillContentService * [PM-2130] Implementation of Jest Test for IntertAutofillContentService * [PM-2130] Finalizing migration of methods and jest tests from util files into Autofill serivces * [PM-2130] Cleaning up dead code comments * [PM-2130] Removing unnecessary constants * [PM-2130] Finalizing jest tests for InsertAutofillContentService * [PM-2130] Refactoring FieldVisibiltyService to DomElementVisibilityService to allow service to act in a more general manner * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Implementing jest tests for DomElementVisibilityService * [PM-2130] Breaking out the callback method used to resolve the IntersectionObserver promise * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2130] Adding a comment explaining a fix for Safari * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2747] Add Support for Feature Flag of Autofill Version * [PM-2747] Adding Support for Manifest v3 within the implementation * [PM-2747] Modifying how the feature flag for autofill is named * [PM-2747] Modifying main.background.ts to load the ConfigApiService correctly * [PM-2747] Refactoring trigger of autofill scripts to be a simple immediately invoked function * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2130] Applying changes required for PM-2762 to implementation, and ensuring jest tests exist to validate the behavior * [PM-2747] Modifying how we inject the autofill scripts to ensure we are injecting into all frames within a page * [PM-2130] Removing usage of IntersectionObserver when identifying element visibility due to broken interactions with React Components * [PM-2130] Fixing issue found when attempting to capture the elementAtCenterPoint in determining file visibility * [PM-2100] Create Unit Test Suite for autofill.service.ts (#5371) * [PM-2100] Create Unit Test Suite for Autofill.service.ts * [PM-2100] Finishing out tests for the getFormsWithPasswordFields method * [PM-2100] Implementing tests for the doAutofill method within the autofill service * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Working through implementation of doAutofill method * [PM-2100] Finishing implementatino of isUntrustedIframe method within autofill service * [PM-2100] Finishing implementation of doAutoFill method within autofill service * [PM-2100] Finishing implementation of doAutoFillOnTab method within autofill service * [PM-2100] Working through tests for generateFillScript * [PM-2100] Finalizing generateFillScript method testing * [PM-2100] Starting implementation of generateLoginFillScript * [PM-2100] Working through tests for generateLoginFillScript * [PM-2100] Finalizing generateLoginFillScript method testing * [PM-2100] Removing unnecessary jest config file * [PM-2100] Fixing jest tests based on changes implemented within PM-2130 * [PM-2100] Fixing autofill mocks * [PM-2100] Fixing AutofillService jest tests * [PM-2100] Handling missing tests within coverage of AutofillService * [PM-2100] Handling missing tests within coverage of AutofillService.generateLoginFillScript * [PM-2100] Writing tests for AutofillService.generateCardFillScript * [PM-2100] Finalizing tests for AutofillService.generateCardFillScript * [PM-2100] Adding additional tests to cover changes introduced by TOTOP autofill PR * [PM-2100] Adding jest tests for Autofill.generateIdentityFillScript * [PM-2100] Finalizing tests for AutofillService.generateIdentityFillScript * [PM-2100] Implementing tests for AutofillService * [PM-2100] Implementing tests for AutofillService.loadPasswordFields * [PM-2100] Implementing tests for AutofillService.findUsernameField * [PM-2100] Implementing tests for AutofillService.findTotpField * [PM-2100] Implementing tests for AutofillService.fieldPropertyIsPrefixMatch * [PM-2100] Finalizing tests for AutofillService * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Modyfing placement of autofill-mocks * [PM-2100] Removal of jest transform declaration * [PM-2747] Applying a fix for a race condition that can occur when loading the notification bar and autofiller script login * [PM-2747] Reverting removal of autofill npm action. Now this will force usage of autofill-v2 regardless of whether a feature flag is set or not * [PM-2747] Fixing logic error incorporated when merging in master * [PM-2130] Fixing issue with autofill service unit tests * [PM-2130] Fixing issue with autofill service unit tests * [PM-2747] Fixing issue present with notification bar merge * [PM-2130] Fixing test test for when we need to handle a password reprompt * [PM-2747] Fixing wording for webpack script * [PM-2747] Addressing stylistic changes requested from code review * [PM-2747] Addressing stylistic changes requested from code review --------- Co-authored-by: Jonathan Prusik Co-authored-by: Manuel Co-authored-by: Jonathan Prusik * [PM-3285] Applying stylistic changes suggested by code review for the feature flag implementation * [PM-3285] Adding temporary console log to validate which version is being used * [PM-3465] Implementing a methodology for sorting the autofill field elements after awaiting the results of each element * [PM-3465] Implementing a methodology for sorting the autofill field elements after awaiting the results of each element * [PM-3465] Implementing a methodology for using cached field values when requerying DOM for elements * [PM-3285] Removing temporary console log indicating which version of autofill the user is currently loading * [PM-3465] Adding scripting api reference to the manifest v3 json file * [PM-3465] Updating implementation to deal with cache invalidation issues * [PM-3465] Implementing jest tests for added collect autofill content class elements * [PM-3465] Removing scripting API permissiong within manifest v3 json file * [PM-3465] Implementing method for removing cached page details if the window location has updated * [PM-3465] Fixing issue found with query selector generated while collecting page details * [PM-3465] Fixing jest tests * [PM-3465] Fixing jest tests --------- Co-authored-by: Jonathan Prusik Co-authored-by: Manuel Co-authored-by: Jonathan Prusik --- .../src/autofill/models/autofill-form.ts | 1 + .../collect-autofill-content.service.ts | 26 +- .../collect-autofill-content.service.spec.ts | 771 +++++++++++++++++- .../collect-autofill-content.service.ts | 737 +++++++++++++++-- .../dom-element-visibility.service.ts | 6 +- .../insert-autofill-content.service.ts | 18 +- 6 files changed, 1490 insertions(+), 69 deletions(-) diff --git a/apps/browser/src/autofill/models/autofill-form.ts b/apps/browser/src/autofill/models/autofill-form.ts index e23539bd303..3f06e28a912 100644 --- a/apps/browser/src/autofill/models/autofill-form.ts +++ b/apps/browser/src/autofill/models/autofill-form.ts @@ -2,6 +2,7 @@ * Represents an HTML form whose elements can be autofilled */ export default class AutofillForm { + [key: string]: any; /** * The unique identifier assigned to this field during collection of the page details */ diff --git a/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts index 7ff85a7e8e7..e4a409eb599 100644 --- a/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/abstractions/collect-autofill-content.service.ts @@ -1,8 +1,32 @@ +import AutofillField from "../../models/autofill-field"; +import AutofillForm from "../../models/autofill-form"; import AutofillPageDetails from "../../models/autofill-page-details"; +import { ElementWithOpId, FormFieldElement } from "../../types"; + +type AutofillFormElements = Map, AutofillForm>; + +type AutofillFieldElements = Map, AutofillField>; + +type UpdateAutofillDataAttributeParams = { + element: ElementWithOpId; + attributeName: string; + dataTarget?: AutofillForm | AutofillField; + dataTargetKey?: string; +}; interface CollectAutofillContentService { getPageDetails(): Promise; getAutofillFieldElementByOpid(opid: string): HTMLElement | null; + queryAllTreeWalkerNodes( + rootNode: Node, + filterCallback: CallableFunction, + isObservingShadowRoot?: boolean + ): Node[]; } -export { CollectAutofillContentService }; +export { + AutofillFormElements, + AutofillFieldElements, + UpdateAutofillDataAttributeParams, + CollectAutofillContentService, +}; diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts index 706ceb0fe0f..b21f530e572 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.spec.ts @@ -1,3 +1,7 @@ +import { mock } from "jest-mock-extended"; + +import AutofillField from "../models/autofill-field"; +import AutofillForm from "../models/autofill-form"; import { ElementWithOpId, FillableFormFieldElement, @@ -32,7 +36,128 @@ describe("CollectAutofillContentService", () => { }); describe("getPageDetails", () => { - it("returns an object containing information about the curren page as well as autofill data for the forms and fields of the page", async () => { + beforeEach(() => { + jest + .spyOn(collectAutofillContentService as any, "setupMutationObserver") + .mockImplementationOnce(() => { + collectAutofillContentService["mutationObserver"] = mock(); + }); + }); + + it("sets up the mutation observer the first time getPageDetails is called", async () => { + await collectAutofillContentService.getPageDetails(); + await collectAutofillContentService.getPageDetails(); + + expect(collectAutofillContentService["setupMutationObserver"]).toHaveBeenCalledTimes(1); + }); + + it("returns an object with empty forms and fields if no fields were found on a previous iteration", async () => { + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["noFieldsFound"] = true; + jest.spyOn(collectAutofillContentService as any, "getFormattedPageDetails"); + jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData"); + + await collectAutofillContentService.getPageDetails(); + + expect(collectAutofillContentService["getFormattedPageDetails"]).toHaveBeenCalledWith({}, []); + expect( + collectAutofillContentService["queryAutofillFormAndFieldElements"] + ).not.toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFormsData"]).not.toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFieldsData"]).not.toHaveBeenCalled(); + }); + + it("returns an object with cached form and field data values", async () => { + const formId = "validFormId"; + const formAction = "https://example.com/"; + const formMethod = "post"; + const formName = "validFormName"; + const usernameFieldId = "usernameField"; + const usernameFieldName = "username"; + const usernameFieldLabel = "User Name"; + const passwordFieldId = "passwordField"; + const passwordFieldName = "password"; + const passwordFieldLabel = "Password"; + document.body.innerHTML = ` +
+ + + + +
+ `; + const formElement = document.getElementById(formId) as ElementWithOpId; + const autofillForm: AutofillForm = { + opid: "__form__0", + htmlAction: formAction, + htmlName: formName, + htmlID: formId, + htmlMethod: formMethod, + }; + const fieldElement = document.getElementById( + usernameFieldId + ) as ElementWithOpId; + const autofillField: AutofillField = { + opid: "__0", + elementNumber: 0, + maxLength: 999, + viewable: true, + htmlID: usernameFieldId, + htmlName: usernameFieldName, + htmlClass: null, + tabindex: null, + title: "", + tagName: "input", + "label-tag": usernameFieldLabel, + "label-data": null, + "label-aria": null, + "label-top": null, + "label-right": passwordFieldLabel, + "label-left": usernameFieldLabel, + placeholder: "", + rel: null, + type: "text", + value: "", + checked: false, + autoCompleteType: "", + disabled: false, + readonly: false, + selectInfo: null, + form: "__form__0", + "aria-hidden": false, + "aria-disabled": false, + "aria-haspopup": false, + "data-stripe": null, + }; + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["autofillFormElements"] = new Map([ + [formElement, autofillForm], + ]); + collectAutofillContentService["autofillFieldElements"] = new Map([ + [fieldElement, autofillField], + ]); + jest.spyOn(collectAutofillContentService as any, "getFormattedPageDetails"); + jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFormsData"); + jest.spyOn(collectAutofillContentService as any, "getFormattedAutofillFieldsData"); + jest.spyOn(collectAutofillContentService as any, "queryAutofillFormAndFieldElements"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData"); + + await collectAutofillContentService.getPageDetails(); + + expect(collectAutofillContentService["getFormattedPageDetails"]).toHaveBeenCalled(); + expect(collectAutofillContentService["getFormattedAutofillFormsData"]).toHaveBeenCalled(); + expect(collectAutofillContentService["getFormattedAutofillFieldsData"]).toHaveBeenCalled(); + expect( + collectAutofillContentService["queryAutofillFormAndFieldElements"] + ).not.toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFormsData"]).not.toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFieldsData"]).not.toHaveBeenCalled(); + }); + + it("returns an object containing information about the current page as well as autofill data for the forms and fields of the page", async () => { const documentTitle = "Test Page"; const formId = "validFormId"; const formAction = "https://example.com/"; @@ -145,6 +270,19 @@ describe("CollectAutofillContentService", () => { collectedTimestamp: expect.any(Number), }); }); + + it("sets the noFieldsFond property to true if the page has no forms or fields", async function () { + document.body.innerHTML = ""; + collectAutofillContentService["noFieldsFound"] = false; + jest.spyOn(collectAutofillContentService as any, "buildAutofillFormsData"); + jest.spyOn(collectAutofillContentService as any, "buildAutofillFieldsData"); + + await collectAutofillContentService.getPageDetails(); + + expect(collectAutofillContentService["buildAutofillFormsData"]).toHaveBeenCalled(); + expect(collectAutofillContentService["buildAutofillFieldsData"]).toHaveBeenCalled(); + expect(collectAutofillContentService["noFieldsFound"]).toBe(true); + }); }); describe("getAutofillFieldElementByOpid", () => { @@ -213,6 +351,44 @@ describe("CollectAutofillContentService", () => { }); describe("buildAutofillFormsData", () => { + it("will not attempt to gather data from a cached form element", () => { + const documentTitle = "Test Page"; + const formId = "validFormId"; + const formAction = "https://example.com/"; + const formMethod = "post"; + const formName = "validFormName"; + document.title = documentTitle; + document.body.innerHTML = ` +
+ + + + +
+ + `; + const formElement = document.getElementById(formId) as ElementWithOpId; + const existingAutofillForm: AutofillForm = { + opid: "__form__0", + htmlAction: formAction, + htmlName: formName, + htmlID: formId, + htmlMethod: formMethod, + }; + collectAutofillContentService["autofillFormElements"] = new Map([ + [formElement, existingAutofillForm], + ]); + const formElements = Array.from(document.querySelectorAll("form")); + jest.spyOn(collectAutofillContentService as any, "getFormActionAttribute"); + + const autofillFormsData = collectAutofillContentService["buildAutofillFormsData"]( + formElements as Node[] + ); + + expect(collectAutofillContentService["getFormActionAttribute"]).not.toHaveBeenCalled(); + expect(autofillFormsData).toStrictEqual({ __form__0: existingAutofillForm }); + }); + it("returns an object of AutofillForm objects with the form id as a key", () => { const documentTitle = "Test Page"; const formId1 = "validFormId"; @@ -237,7 +413,9 @@ describe("CollectAutofillContentService", () => { `; - const autofillFormsData = collectAutofillContentService["buildAutofillFormsData"](); + const { formElements } = collectAutofillContentService["queryAutofillFormAndFieldElements"](); + const autofillFormsData = + collectAutofillContentService["buildAutofillFormsData"](formElements); expect(autofillFormsData).toStrictEqual({ __form__0: { @@ -266,10 +444,17 @@ describe("CollectAutofillContentService", () => { .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") .mockResolvedValue(true); - const autofillFieldsPromise = collectAutofillContentService["buildAutofillFieldsData"](); + const { formFieldElements } = + collectAutofillContentService["queryAutofillFormAndFieldElements"](); + const autofillFieldsPromise = collectAutofillContentService["buildAutofillFieldsData"]( + formFieldElements as FormFieldElement[] + ); const autofillFieldsData = await Promise.resolve(autofillFieldsPromise); - expect(collectAutofillContentService["getAutofillFieldElements"]).toHaveBeenCalledWith(50); + expect(collectAutofillContentService["getAutofillFieldElements"]).toHaveBeenCalledWith( + 100, + formFieldElements + ); expect(collectAutofillContentService["buildAutofillFieldItem"]).toHaveBeenCalledTimes(2); expect(autofillFieldsPromise).toBeInstanceOf(Promise); expect(autofillFieldsData).toStrictEqual([ @@ -372,9 +557,6 @@ describe("CollectAutofillContentService", () => { const formElements: FormFieldElement[] = collectAutofillContentService["getAutofillFieldElements"](); - expect(document.querySelectorAll).toHaveBeenCalledWith( - 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), textarea:not([data-bwignore]), select:not([data-bwignore]), span[data-bwautofill]' - ); expect(collectAutofillContentService["getPropertyOrAttribute"]).not.toHaveBeenCalled(); expect(formElements).toEqual([ usernameInput, @@ -538,6 +720,105 @@ describe("CollectAutofillContentService", () => { }); describe("buildAutofillFieldItem", () => { + it("returns an existing autofill field item if it exists", async () => { + const index = 0; + const usernameField = { + labelText: "Username", + id: "username-id", + classes: "username input classes", + name: "username", + type: "text", + maxLength: 42, + tabIndex: 0, + title: "Username Input Title", + autocomplete: "username-autocomplete", + dataLabel: "username-data-label", + ariaLabel: "username-aria-label", + placeholder: "username-placeholder", + rel: "username-rel", + value: "username-value", + dataStripe: "data-stripe", + }; + document.body.innerHTML = ` +
+ + +
+ `; + document.body.innerHTML = ` +
+ + +
+ `; + const existingFieldData: AutofillField = { + elementNumber: index, + htmlClass: usernameField.classes, + htmlID: usernameField.id, + htmlName: usernameField.name, + maxLength: usernameField.maxLength, + opid: `__${index}`, + tabindex: String(usernameField.tabIndex), + tagName: "input", + title: usernameField.title, + viewable: true, + }; + const usernameInput = document.getElementById( + usernameField.id + ) as ElementWithOpId; + usernameInput.opid = "__0"; + collectAutofillContentService["autofillFieldElements"].set(usernameInput, existingFieldData); + jest.spyOn(collectAutofillContentService as any, "getAutofillFieldMaxLength"); + jest + .spyOn(collectAutofillContentService["domElementVisibilityService"], "isFormFieldViewable") + .mockResolvedValue(true); + jest.spyOn(collectAutofillContentService as any, "getPropertyOrAttribute"); + jest.spyOn(collectAutofillContentService as any, "getElementValue"); + + const autofillFieldItem = await collectAutofillContentService["buildAutofillFieldItem"]( + usernameInput, + 0 + ); + + expect(collectAutofillContentService["getAutofillFieldMaxLength"]).not.toHaveBeenCalled(); + expect( + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + ).not.toHaveBeenCalled(); + expect(collectAutofillContentService["getPropertyOrAttribute"]).not.toHaveBeenCalled(); + expect(collectAutofillContentService["getElementValue"]).not.toHaveBeenCalled(); + expect(autofillFieldItem).toEqual(existingFieldData); + }); + it("returns the AutofillField base data values without the field labels or input values if the passed element is a span element", async () => { const index = 0; const spanElementId = "span-element"; @@ -958,6 +1239,20 @@ describe("CollectAutofillContentService", () => { expect(labels).toEqual(document.querySelectorAll("label[for='username']")); }); + + it("removes any new lines generated for the query selector", () => { + document.body.innerHTML = ` + + + `; + const element = document.querySelector("input") as FillableFormFieldElement; + + const labels = collectAutofillContentService["queryElementLabels"](element); + + expect(labels).toEqual(document.querySelectorAll("label[for='username-id']")); + }); }); describe("createLabelElementsTag", () => { @@ -1585,4 +1880,466 @@ describe("CollectAutofillContentService", () => { expect(selectWithoutOptionsOptions).toEqual({ options: [] }); }); }); + + describe("getShadowRoot", () => { + it("returns null if the passed node is not an HTMLElement instance", () => { + const textNode = document.createTextNode("Hello, world!"); + const shadowRoot = collectAutofillContentService["getShadowRoot"](textNode); + + expect(shadowRoot).toEqual(null); + }); + + it("returns a value provided by Chrome's openOrClosedShadowRoot API", () => { + // eslint-disable-next-line + // @ts-ignore + globalThis.chrome.dom = { + openOrClosedShadowRoot: jest.fn(), + }; + const element = document.createElement("div"); + collectAutofillContentService["getShadowRoot"](element); + + // eslint-disable-next-line + // @ts-ignore + expect(chrome.dom.openOrClosedShadowRoot).toBeCalled(); + }); + }); + + describe("buildTreeWalkerNodesQueryResults", () => { + it("will recursively call itself if a shadowDOM element is found and will observe the element for mutations", () => { + collectAutofillContentService["mutationObserver"] = mock({ + observe: jest.fn(), + }); + jest.spyOn(collectAutofillContentService as any, "buildTreeWalkerNodesQueryResults"); + const shadowRoot = document.createElement("div"); + jest + .spyOn(collectAutofillContentService as any, "getShadowRoot") + .mockReturnValueOnce(shadowRoot); + const callbackFilter = jest.fn(); + + collectAutofillContentService["buildTreeWalkerNodesQueryResults"]( + document.body, + [], + callbackFilter, + true + ); + + expect(collectAutofillContentService["buildTreeWalkerNodesQueryResults"]).toBeCalledTimes(2); + expect(collectAutofillContentService["mutationObserver"].observe).toBeCalled(); + }); + + it("will not observe the shadowDOM element if required to skip", () => { + collectAutofillContentService["mutationObserver"] = mock({ + observe: jest.fn(), + }); + const shadowRoot = document.createElement("div"); + jest + .spyOn(collectAutofillContentService as any, "getShadowRoot") + .mockReturnValueOnce(shadowRoot); + const callbackFilter = jest.fn(); + + collectAutofillContentService["buildTreeWalkerNodesQueryResults"]( + document.body, + [], + callbackFilter, + false + ); + + expect(collectAutofillContentService["mutationObserver"].observe).not.toBeCalled(); + }); + }); + + describe("setupMutationObserver", () => { + it("sets up a mutation observer and observes the document element", () => { + jest.spyOn(MutationObserver.prototype, "observe"); + + collectAutofillContentService["setupMutationObserver"](); + + expect(collectAutofillContentService["mutationObserver"]).toBeInstanceOf(MutationObserver); + expect(collectAutofillContentService["mutationObserver"].observe).toBeCalled(); + }); + }); + + describe("handleMutationObserverMutation", () => { + it("will set the domRecentlyMutated value to true and the noFieldsFound value to false if a form or field node has been added ", () => { + const form = document.createElement("form"); + document.body.appendChild(form); + const addedNodes = document.querySelectorAll("form"); + const removedNodes = document.querySelectorAll("li"); + + const mutationRecord: MutationRecord = { + type: "childList", + addedNodes: addedNodes, + attributeName: null, + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: removedNodes, + target: document.body, + }; + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["noFieldsFound"] = true; + collectAutofillContentService["currentLocationHref"] = window.location.href; + jest.spyOn(collectAutofillContentService as any, "isAutofillElementNodeMutated"); + + collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); + + expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(true); + expect(collectAutofillContentService["noFieldsFound"]).toEqual(false); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( + removedNodes, + true + ); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).toBeCalledWith( + addedNodes + ); + }); + + it("will handle updating the autofill element if any attribute mutations are encountered", () => { + const mutationRecord: MutationRecord = { + type: "attributes", + addedNodes: null, + attributeName: "value", + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: null, + target: document.body, + }; + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["noFieldsFound"] = true; + collectAutofillContentService["currentLocationHref"] = window.location.href; + jest.spyOn(collectAutofillContentService as any, "isAutofillElementNodeMutated"); + jest.spyOn(collectAutofillContentService as any, "handleAutofillElementAttributeMutation"); + + collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); + + expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(false); + expect(collectAutofillContentService["noFieldsFound"]).toEqual(true); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); + expect(collectAutofillContentService["handleAutofillElementAttributeMutation"]).toBeCalled(); + }); + + it("will handle window location mutations", () => { + const mutationRecord: MutationRecord = { + type: "attributes", + addedNodes: null, + attributeName: "value", + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: null, + target: document.body, + }; + collectAutofillContentService["currentLocationHref"] = "https://someotherurl.com"; + jest.spyOn(collectAutofillContentService as any, "handleWindowLocationMutation"); + jest.spyOn(collectAutofillContentService as any, "isAutofillElementNodeMutated"); + jest.spyOn(collectAutofillContentService as any, "handleAutofillElementAttributeMutation"); + + collectAutofillContentService["handleMutationObserverMutation"]([mutationRecord]); + + expect(collectAutofillContentService["handleWindowLocationMutation"]).toBeCalled(); + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); + expect( + collectAutofillContentService["handleAutofillElementAttributeMutation"] + ).not.toBeCalled(); + }); + }); + + describe("deleteCachedAutofillElement", () => { + it("removes the autofill form element from the map of elements", () => { + const formElement = document.createElement("form") as ElementWithOpId; + const autofillForm: AutofillForm = { + opid: "1234", + htmlName: "formEl", + htmlID: "formEl-id", + htmlAction: "https://example.com", + htmlMethod: "POST", + }; + collectAutofillContentService["autofillFormElements"] = new Map([ + [formElement, autofillForm], + ]); + + collectAutofillContentService["deleteCachedAutofillElement"](formElement); + + expect(collectAutofillContentService["autofillFormElements"].size).toEqual(0); + }); + + it("removes the autofill field element form the map of elements", () => { + const fieldElement = document.createElement("input") as ElementWithOpId; + const autofillField: AutofillField = { + elementNumber: 0, + htmlClass: "", + tabindex: "", + title: "", + viewable: false, + opid: "1234", + htmlName: "username", + htmlID: "username-id", + htmlType: "text", + htmlAutocomplete: "username", + htmlAutofocus: false, + htmlDisabled: false, + htmlMaxLength: 999, + htmlReadonly: false, + htmlRequired: false, + htmlValue: "jsmith", + }; + collectAutofillContentService["autofillFieldElements"] = new Map([ + [fieldElement, autofillField], + ]); + + collectAutofillContentService["deleteCachedAutofillElement"](fieldElement); + + expect(collectAutofillContentService["autofillFieldElements"].size).toEqual(0); + }); + }); + + describe("handleWindowLocationMutation", () => { + it("will set the current location to the global location href, set the dom recently mutated flag and the no fields found flag, clear out the autofill form and field maps, and update the autofill elements after mutation", () => { + collectAutofillContentService["currentLocationHref"] = "https://example.com/login"; + collectAutofillContentService["domRecentlyMutated"] = false; + collectAutofillContentService["noFieldsFound"] = true; + jest.spyOn(collectAutofillContentService as any, "updateAutofillElementsAfterMutation"); + + collectAutofillContentService["handleWindowLocationMutation"](); + + expect(collectAutofillContentService["currentLocationHref"]).toEqual(window.location.href); + expect(collectAutofillContentService["domRecentlyMutated"]).toEqual(true); + expect(collectAutofillContentService["noFieldsFound"]).toEqual(false); + expect(collectAutofillContentService["updateAutofillElementsAfterMutation"]).toBeCalled(); + expect(collectAutofillContentService["autofillFormElements"].size).toEqual(0); + expect(collectAutofillContentService["autofillFieldElements"].size).toEqual(0); + }); + }); + + describe("handleAutofillElementAttributeMutation", () => { + it("returns early if the target node is not an HTMLElement instance", () => { + const mutationRecord: MutationRecord = { + type: "attributes", + addedNodes: null, + attributeName: "value", + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: null, + target: document.createTextNode("Hello, world!"), + }; + jest.spyOn(collectAutofillContentService as any, "isAutofillElementNodeMutated"); + + collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); + + expect(collectAutofillContentService["isAutofillElementNodeMutated"]).not.toBeCalled(); + }); + + it("will update the autofill form element data if the target node can be found in the autofillFormElements map", () => { + const targetNode = document.createElement("form") as ElementWithOpId; + targetNode.setAttribute("name", "username"); + targetNode.setAttribute("value", "jsmith"); + const autofillForm: AutofillForm = { + opid: "1234", + htmlName: "formEl", + htmlID: "formEl-id", + htmlAction: "https://example.com", + htmlMethod: "POST", + }; + const mutationRecord: MutationRecord = { + type: "attributes", + addedNodes: null, + attributeName: "id", + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: null, + target: targetNode, + }; + collectAutofillContentService["autofillFormElements"] = new Map([[targetNode, autofillForm]]); + jest.spyOn(collectAutofillContentService as any, "updateAutofillFormElementData"); + + collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); + + expect(collectAutofillContentService["updateAutofillFormElementData"]).toBeCalledWith( + mutationRecord.attributeName, + mutationRecord.target, + autofillForm + ); + }); + + it("will update the autofill field element data if the target node can be found in the autofillFieldElements map", () => { + const targetNode = document.createElement("input") as ElementWithOpId; + targetNode.setAttribute("name", "username"); + targetNode.setAttribute("value", "jsmith"); + const autofillField: AutofillField = { + elementNumber: 0, + htmlClass: "", + tabindex: "", + title: "", + viewable: false, + opid: "1234", + htmlName: "username", + htmlID: "username-id", + htmlType: "text", + htmlAutocomplete: "username", + htmlAutofocus: false, + htmlDisabled: false, + htmlMaxLength: 999, + htmlReadonly: false, + htmlRequired: false, + htmlValue: "jsmith", + }; + const mutationRecord: MutationRecord = { + type: "attributes", + addedNodes: null, + attributeName: "id", + attributeNamespace: null, + nextSibling: null, + oldValue: null, + previousSibling: null, + removedNodes: null, + target: targetNode, + }; + collectAutofillContentService["autofillFieldElements"] = new Map([ + [targetNode, autofillField], + ]); + jest.spyOn(collectAutofillContentService as any, "updateAutofillFieldElementData"); + + collectAutofillContentService["handleAutofillElementAttributeMutation"](mutationRecord); + + expect(collectAutofillContentService["updateAutofillFieldElementData"]).toBeCalledWith( + mutationRecord.attributeName, + mutationRecord.target, + autofillField + ); + }); + }); + + describe("updateAutofillFormElementData", () => { + const formElement = document.createElement("form") as ElementWithOpId; + const autofillForm: AutofillForm = { + opid: "1234", + htmlName: "formEl", + htmlID: "formEl-id", + htmlAction: "https://example.com", + htmlMethod: "POST", + }; + const updatedAttributes = ["action", "name", "id", "method"]; + + updatedAttributes.forEach((attribute) => { + it(`will update the ${attribute} value for the form element`, () => { + jest.spyOn(collectAutofillContentService["autofillFormElements"], "set"); + + collectAutofillContentService["updateAutofillFormElementData"]( + attribute, + formElement, + autofillForm + ); + + expect(collectAutofillContentService["autofillFormElements"].set).toBeCalledWith( + formElement, + autofillForm + ); + }); + }); + + it("will not update an attribute value if it is not present in the updateActions object", () => { + jest.spyOn(collectAutofillContentService["autofillFormElements"], "set"); + + collectAutofillContentService["updateAutofillFormElementData"]( + "aria-label", + formElement, + autofillForm + ); + + expect(collectAutofillContentService["autofillFormElements"].set).not.toBeCalled(); + }); + }); + + describe("updateAutofillFieldElementData", () => { + const fieldElement = document.createElement("input") as ElementWithOpId; + const autofillField: AutofillField = { + htmlClass: "value", + htmlID: "", + htmlName: "", + opid: "", + tabindex: "", + title: "", + viewable: false, + elementNumber: 0, + }; + const updatedAttributes = [ + "maxlength", + "name", + "id", + "type", + "autocomplete", + "class", + "tabindex", + "title", + "value", + "rel", + "tagname", + "checked", + "disabled", + "readonly", + "data-label", + "aria-label", + "aria-hidden", + "aria-disabled", + "aria-haspopup", + "data-stripe", + ]; + + updatedAttributes.forEach((attribute) => { + it(`will update the ${attribute} value for the field element`, async () => { + jest.spyOn(collectAutofillContentService["autofillFieldElements"], "set"); + + await collectAutofillContentService["updateAutofillFieldElementData"]( + attribute, + fieldElement, + autofillField + ); + + expect(collectAutofillContentService["autofillFieldElements"].set).toBeCalledWith( + fieldElement, + autofillField + ); + }); + }); + + it("will check the dom element's visibility if the `style` or `class` attribute has updated ", async () => { + jest.spyOn( + collectAutofillContentService["domElementVisibilityService"], + "isFormFieldViewable" + ); + const attributes = ["class", "style"]; + + for (const attribute of attributes) { + await collectAutofillContentService["updateAutofillFieldElementData"]( + attribute, + fieldElement, + autofillField + ); + + expect( + collectAutofillContentService["domElementVisibilityService"].isFormFieldViewable + ).toBeCalledWith(fieldElement); + } + }); + + it("will not update an attribute value if it is not present in the updateActions object", async () => { + jest.spyOn(collectAutofillContentService["autofillFieldElements"], "set"); + + await collectAutofillContentService["updateAutofillFieldElementData"]( + "random-attribute", + fieldElement, + autofillField + ); + + expect(collectAutofillContentService["autofillFieldElements"].set).not.toBeCalled(); + }); + }); }); diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index ec7658c9863..4780c294ab1 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -8,34 +8,70 @@ import { FormElementWithAttribute, } from "../types"; -import { CollectAutofillContentService as CollectAutofillContentServiceInterface } from "./abstractions/collect-autofill-content.service"; +import { + UpdateAutofillDataAttributeParams, + AutofillFieldElements, + AutofillFormElements, + CollectAutofillContentService as CollectAutofillContentServiceInterface, +} from "./abstractions/collect-autofill-content.service"; import DomElementVisibilityService from "./dom-element-visibility.service"; class CollectAutofillContentService implements CollectAutofillContentServiceInterface { private readonly domElementVisibilityService: DomElementVisibilityService; + private noFieldsFound = false; + private domRecentlyMutated = true; + private autofillFormElements: AutofillFormElements = new Map(); + private autofillFieldElements: AutofillFieldElements = new Map(); + private currentLocationHref = ""; + private mutationObserver: MutationObserver; + private updateAutofillElementsAfterMutationTimeout: NodeJS.Timeout; + private readonly updateAfterMutationTimeoutDelay = 1000; constructor(domElementVisibilityService: DomElementVisibilityService) { this.domElementVisibilityService = domElementVisibilityService; } /** - * Builds the data for all the forms and fields - * that are found within the page DOM. + * Builds the data for all forms and fields found within the page DOM. + * Sets up a mutation observer to verify DOM changes and returns early + * with cached data if no changes are detected. * @returns {Promise} * @public */ async getPageDetails(): Promise { - const autofillFormsData: Record = this.buildAutofillFormsData(); - const autofillFieldsData: AutofillField[] = await this.buildAutofillFieldsData(); + if (!this.mutationObserver) { + this.setupMutationObserver(); + } - return { - title: document.title, - url: (document.defaultView || window).location.href, - documentUrl: document.location.href, - forms: autofillFormsData, - fields: autofillFieldsData, - collectedTimestamp: Date.now(), - }; + if (!this.domRecentlyMutated && this.noFieldsFound) { + return this.getFormattedPageDetails({}, []); + } + + if ( + !this.domRecentlyMutated && + this.autofillFormElements.size && + this.autofillFieldElements.size + ) { + return this.getFormattedPageDetails( + this.getFormattedAutofillFormsData(), + this.getFormattedAutofillFieldsData() + ); + } + + const { formElements, formFieldElements } = this.queryAutofillFormAndFieldElements(); + const autofillFormsData: Record = + this.buildAutofillFormsData(formElements); + const autofillFieldsData: AutofillField[] = await this.buildAutofillFieldsData( + formFieldElements as FormFieldElement[] + ); + this.sortAutofillFieldElementsMap(); + + if (!Object.values(autofillFormsData).length || !autofillFieldsData.length) { + this.noFieldsFound = true; + } + + this.domRecentlyMutated = false; + return this.getFormattedPageDetails(autofillFormsData, autofillFieldsData); } /** @@ -46,15 +82,18 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @returns {FormFieldElement | null} */ getAutofillFieldElementByOpid(opid: string): FormFieldElement | null { - const fieldElements = this.getAutofillFieldElements(); - const fieldElementsWithOpid = fieldElements.filter( + const cachedFormFieldElements = Array.from(this.autofillFieldElements.keys()); + const formFieldElements = cachedFormFieldElements?.length + ? cachedFormFieldElements + : this.getAutofillFieldElements(); + const fieldElementsWithOpid = formFieldElements.filter( (fieldElement) => (fieldElement as ElementWithOpId).opid === opid ) as ElementWithOpId[]; if (!fieldElementsWithOpid.length) { const elementIndex = parseInt(opid.split("__")[1], 10); - return fieldElements[elementIndex] || null; + return formFieldElements[elementIndex] || null; } if (fieldElementsWithOpid.length > 1) { @@ -65,30 +104,120 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte return fieldElementsWithOpid[0]; } + /** + * Queries the DOM for all the nodes that match the given filter callback + * and returns a collection of nodes. + * @param {Node} rootNode + * @param {Function} filterCallback + * @param {boolean} isObservingShadowRoot + * @returns {Node[]} + */ + queryAllTreeWalkerNodes( + rootNode: Node, + filterCallback: CallableFunction, + isObservingShadowRoot = true + ): Node[] { + const treeWalkerQueryResults: Node[] = []; + + this.buildTreeWalkerNodesQueryResults( + rootNode, + treeWalkerQueryResults, + filterCallback, + isObservingShadowRoot + ); + + return treeWalkerQueryResults; + } + + /** + * Sorts the AutofillFieldElements map by the elementNumber property. + * @private + */ + private sortAutofillFieldElementsMap() { + if (!this.autofillFieldElements.size) { + return; + } + + this.autofillFieldElements = new Map( + [...this.autofillFieldElements].sort((a, b) => a[1].elementNumber - b[1].elementNumber) + ); + } + + /** + * Formats and returns the AutofillPageDetails object + * @param {Record} autofillFormsData + * @param {AutofillField[]} autofillFieldsData + * @returns {AutofillPageDetails} + * @private + */ + private getFormattedPageDetails( + autofillFormsData: Record, + autofillFieldsData: AutofillField[] + ): AutofillPageDetails { + return { + title: document.title, + url: (document.defaultView || window).location.href, + documentUrl: document.location.href, + forms: autofillFormsData, + fields: autofillFieldsData, + collectedTimestamp: Date.now(), + }; + } + /** * Queries the DOM for all the forms elements and * returns a collection of AutofillForm objects. * @returns {Record} * @private */ - private buildAutofillFormsData(): Record { - const autofillForms: Record = {}; - const documentFormElements = document.querySelectorAll("form"); - - documentFormElements.forEach((formElement: HTMLFormElement, index: number) => { + private buildAutofillFormsData(formElements: Node[]): Record { + for (let index = 0; index < formElements.length; index++) { + const formElement = formElements[index] as ElementWithOpId; formElement.opid = `__form__${index}`; - autofillForms[formElement.opid] = { + const existingAutofillForm = this.autofillFormElements.get(formElement); + if (existingAutofillForm) { + existingAutofillForm.opid = formElement.opid; + this.autofillFormElements.set(formElement, existingAutofillForm); + continue; + } + + this.autofillFormElements.set(formElement, { opid: formElement.opid, - htmlAction: new URL( - this.getPropertyOrAttribute(formElement, "action"), - window.location.href - ).href, + htmlAction: this.getFormActionAttribute(formElement), htmlName: this.getPropertyOrAttribute(formElement, "name"), htmlID: this.getPropertyOrAttribute(formElement, "id"), htmlMethod: this.getPropertyOrAttribute(formElement, "method"), - }; - }); + }); + } + + return this.getFormattedAutofillFormsData(); + } + + /** + * Returns the action attribute of the form element. If the action attribute + * is a relative path, it will be converted to an absolute path. + * @param {ElementWithOpId} element + * @returns {string} + * @private + */ + private getFormActionAttribute(element: ElementWithOpId): string { + return new URL(this.getPropertyOrAttribute(element, "action"), window.location.href).href; + } + + /** + * Iterates over all known form elements and returns an AutofillForm object + * containing a key value pair of the form element's opid and the form data. + * @returns {Record} + * @private + */ + private getFormattedAutofillFormsData(): Record { + const autofillForms: Record = {}; + const autofillFormElements = Array.from(this.autofillFormElements); + for (let index = 0; index < autofillFormElements.length; index++) { + const [formElement, autofillForm] = autofillFormElements[index]; + autofillForms[formElement.opid] = autofillForm; + } return autofillForms; } @@ -99,8 +228,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @returns {Promise} * @private */ - private async buildAutofillFieldsData(): Promise { - const autofillFieldElements = this.getAutofillFieldElements(50); + private async buildAutofillFieldsData( + formFieldElements: FormFieldElement[] + ): Promise { + const autofillFieldElements = this.getAutofillFieldElements(100, formFieldElements); const autofillFieldDataPromises = autofillFieldElements.map(this.buildAutofillFieldItem); return Promise.all(autofillFieldDataPromises); @@ -111,18 +242,19 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * and returns a list limited to the given `fieldsLimit` number that * is ordered by priority. * @param {number} fieldsLimit - The maximum number of fields to return + * @param {FormFieldElement[]} previouslyFoundFormFieldElements - The list of all the field elements * @returns {FormFieldElement[]} * @private */ - private getAutofillFieldElements(fieldsLimit?: number): FormFieldElement[] { - const formFieldElements: FormFieldElement[] = [ - ...(document.querySelectorAll( - 'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), ' + - "textarea:not([data-bwignore]), " + - "select:not([data-bwignore]), " + - "span[data-bwautofill]" - ) as NodeListOf), - ]; + private getAutofillFieldElements( + fieldsLimit?: number, + previouslyFoundFormFieldElements?: FormFieldElement[] + ): FormFieldElement[] { + const formFieldElements = + previouslyFoundFormFieldElements || + (this.queryAllTreeWalkerNodes(document.documentElement, (node: Node) => + this.isNodeFormFieldElement(node) + ) as FormFieldElement[]); if (!fieldsLimit || formFieldElements.length <= fieldsLimit) { return formFieldElements; @@ -168,6 +300,15 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte ): Promise => { element.opid = `__${index}`; + const existingAutofillField = this.autofillFieldElements.get(element); + if (existingAutofillField) { + existingAutofillField.opid = element.opid; + existingAutofillField.elementNumber = index; + this.autofillFieldElements.set(element, existingAutofillField); + + return existingAutofillField; + } + const autofillFieldBase = { opid: element.opid, elementNumber: index, @@ -178,19 +319,16 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte htmlClass: this.getPropertyOrAttribute(element, "class"), tabindex: this.getPropertyOrAttribute(element, "tabindex"), title: this.getPropertyOrAttribute(element, "title"), - tagName: this.getPropertyOrAttribute(element, "tagName")?.toLowerCase(), + tagName: this.getAttributeLowerCase(element, "tagName"), }; if (element instanceof HTMLSpanElement) { + this.autofillFieldElements.set(element, autofillFieldBase); return autofillFieldBase; } let autofillFieldLabels = {}; - const autoCompleteType = - this.getPropertyOrAttribute(element, "x-autocompletetype") || - this.getPropertyOrAttribute(element, "autocompletetype") || - this.getPropertyOrAttribute(element, "autocomplete"); - const elementType = this.getPropertyOrAttribute(element, "type")?.toLowerCase(); + const elementType = this.getAttributeLowerCase(element, "type"); if (elementType !== "hidden") { autofillFieldLabels = { "label-tag": this.createAutofillFieldLabelTag(element), @@ -203,26 +341,87 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte }; } - return { + const autofillField = { ...autofillFieldBase, ...autofillFieldLabels, rel: this.getPropertyOrAttribute(element, "rel"), type: elementType, value: this.getElementValue(element), - checked: Boolean(this.getPropertyOrAttribute(element, "checked")), - autoCompleteType: autoCompleteType !== "off" ? autoCompleteType : null, - disabled: Boolean(this.getPropertyOrAttribute(element, "disabled")), - readonly: Boolean(this.getPropertyOrAttribute(element, "readOnly")), + checked: this.getAttributeBoolean(element, "checked"), + autoCompleteType: this.getAutoCompleteAttribute(element), + disabled: this.getAttributeBoolean(element, "disabled"), + readonly: this.getAttributeBoolean(element, "readonly"), selectInfo: element instanceof HTMLSelectElement ? this.getSelectElementOptions(element) : null, form: element.form ? this.getPropertyOrAttribute(element.form, "opid") : null, - "aria-hidden": this.getPropertyOrAttribute(element, "aria-hidden") === "true", - "aria-disabled": this.getPropertyOrAttribute(element, "aria-disabled") === "true", - "aria-haspopup": this.getPropertyOrAttribute(element, "aria-haspopup") === "true", + "aria-hidden": this.getAttributeBoolean(element, "aria-hidden", true), + "aria-disabled": this.getAttributeBoolean(element, "aria-disabled", true), + "aria-haspopup": this.getAttributeBoolean(element, "aria-haspopup", true), "data-stripe": this.getPropertyOrAttribute(element, "data-stripe"), }; + + this.autofillFieldElements.set(element, autofillField); + return autofillField; }; + /** + * Identifies the autocomplete attribute associated with an element and returns + * the value of the attribute if it is not set to "off". + * @param {ElementWithOpId} element + * @returns {string} + * @private + */ + private getAutoCompleteAttribute(element: ElementWithOpId): string { + const autoCompleteType = + this.getPropertyOrAttribute(element, "x-autocompletetype") || + this.getPropertyOrAttribute(element, "autocompletetype") || + this.getPropertyOrAttribute(element, "autocomplete"); + return autoCompleteType !== "off" ? autoCompleteType : null; + } + + /** + * Returns a boolean representing the attribute value of an element. + * @param {ElementWithOpId} element + * @param {string} attributeName + * @param {boolean} checkString + * @returns {boolean} + * @private + */ + private getAttributeBoolean( + element: ElementWithOpId, + attributeName: string, + checkString = false + ): boolean { + if (checkString) { + return this.getPropertyOrAttribute(element, attributeName) === "true"; + } + + return Boolean(this.getPropertyOrAttribute(element, attributeName)); + } + + /** + * Returns the attribute of an element as a lowercase value. + * @param {ElementWithOpId} element + * @param {string} attributeName + * @returns {string} + * @private + */ + private getAttributeLowerCase( + element: ElementWithOpId, + attributeName: string + ): string { + return this.getPropertyOrAttribute(element, attributeName)?.toLowerCase(); + } + + /** + * Returns the value of an element's property or attribute. + * @returns {AutofillField[]} + * @private + */ + private getFormattedAutofillFieldsData(): AutofillField[] { + return Array.from(this.autofillFieldElements.values()); + } + /** * Creates a label tag used to autofill the element pulled from a label * associated with the element's id, name, parent element or from an @@ -235,13 +434,14 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte */ private createAutofillFieldLabelTag(element: FillableFormFieldElement): string { const labelElementsSet: Set = new Set(element.labels); - if (labelElementsSet.size) { return this.createLabelElementsTag(labelElementsSet); } const labelElements: NodeListOf | null = this.queryElementLabels(element); - labelElements?.forEach((labelElement) => labelElementsSet.add(labelElement)); + for (let labelIndex = 0; labelIndex < labelElements?.length; labelIndex++) { + labelElementsSet.add(labelElements[labelIndex]); + } let currentElement: HTMLElement | null = element; while (currentElement && currentElement !== document.documentElement) { @@ -286,7 +486,9 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte return null; } - return document.querySelectorAll(labelQuerySelectors); + return (element.getRootNode() as Document | ShadowRoot).querySelectorAll( + labelQuerySelectors.replace(/\n/g, "") + ); } /** @@ -297,7 +499,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @private */ private createLabelElementsTag = (labelElementsSet: Set): string => { - return [...labelElementsSet] + return Array.from(labelElementsSet) .map((labelElement) => { const textContent: string | null = labelElement ? labelElement.textContent || labelElement.innerText @@ -561,7 +763,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte * @private */ private getSelectElementOptions(element: HTMLSelectElement): { options: (string | null)[][] } { - const options = [...element.options].map((option) => { + const options = Array.from(element.options).map((option) => { const optionText = option.text ? String(option.text) .toLowerCase() @@ -573,6 +775,425 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte return { options }; } + + /** + * Queries all potential form and field elements from the DOM and returns + * a collection of form and field elements. Leverages the TreeWalker API + * to deep query Shadow DOM elements. + * @returns {{formElements: Node[], formFieldElements: Node[]}} + * @private + */ + private queryAutofillFormAndFieldElements(): { + formElements: Node[]; + formFieldElements: Node[]; + } { + const formElements: Node[] = []; + const formFieldElements: Node[] = []; + this.queryAllTreeWalkerNodes(document.documentElement, (node: Node) => { + if (node instanceof HTMLFormElement) { + formElements.push(node); + return true; + } + + if (this.isNodeFormFieldElement(node)) { + formFieldElements.push(node); + return true; + } + + return false; + }); + + return { formElements, formFieldElements }; + } + + /** + * Checks if the passed node is a form field element. + * @param {Node} node + * @returns {boolean} + * @private + */ + private isNodeFormFieldElement(node: Node): boolean { + const nodeIsSpanElementWithAutofillAttribute = + node instanceof HTMLSpanElement && node.hasAttribute("data-bwautofill"); + + const ignoredInputTypes = new Set(["hidden", "submit", "reset", "button", "image", "file"]); + const nodeIsValidInputElement = + node instanceof HTMLInputElement && !ignoredInputTypes.has(node.type); + + const nodeIsTextAreaOrSelectElement = + node instanceof HTMLTextAreaElement || node instanceof HTMLSelectElement; + + const nodeIsNonIgnoredFillableControlElement = + (nodeIsTextAreaOrSelectElement || nodeIsValidInputElement) && + !node.hasAttribute("data-bwignore"); + + return nodeIsSpanElementWithAutofillAttribute || nodeIsNonIgnoredFillableControlElement; + } + + /** + * Attempts to get the ShadowRoot of the passed node. If support for the + * extension based openOrClosedShadowRoot API is available, it will be used. + * @param {Node} node + * @returns {ShadowRoot | null} + * @private + */ + private getShadowRoot(node: Node): ShadowRoot | null { + if (!(node instanceof HTMLElement)) { + return null; + } + + if ((chrome as any).dom?.openOrClosedShadowRoot) { + return (chrome as any).dom.openOrClosedShadowRoot(node); + } + + return (node as any).openOrClosedShadowRoot || node.shadowRoot; + } + + /** + * Recursively builds a collection of nodes that match the given filter callback. + * If a node has a ShadowRoot, it will be observed for mutations. + * @param {Node} rootNode + * @param {Node[]} treeWalkerQueryResults + * @param {Function} filterCallback + * @param {boolean} isObservingShadowRoot + * @private + */ + private buildTreeWalkerNodesQueryResults( + rootNode: Node, + treeWalkerQueryResults: Node[], + filterCallback: CallableFunction, + isObservingShadowRoot: boolean + ) { + const treeWalker = document?.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT); + let currentNode = treeWalker?.currentNode; + + while (currentNode) { + if (filterCallback(currentNode)) { + treeWalkerQueryResults.push(currentNode); + } + + const nodeShadowRoot = this.getShadowRoot(currentNode); + if (nodeShadowRoot) { + if (isObservingShadowRoot) { + this.mutationObserver.observe(nodeShadowRoot, { + attributes: true, + childList: true, + subtree: true, + }); + } + + this.buildTreeWalkerNodesQueryResults( + nodeShadowRoot, + treeWalkerQueryResults, + filterCallback, + isObservingShadowRoot + ); + } + + currentNode = treeWalker?.nextNode(); + } + } + + /** + * Sets up a mutation observer on the body of the document. Observes changes to + * DOM elements to ensure we have an updated set of autofill field data. + * @private + */ + private setupMutationObserver() { + this.currentLocationHref = globalThis.location.href; + this.mutationObserver = new MutationObserver(this.handleMutationObserverMutation); + this.mutationObserver.observe(document.documentElement, { + attributes: true, + childList: true, + subtree: true, + }); + } + + /** + * Handles observed DOM mutations and identifies if a mutation is related to + * an autofill element. If so, it will update the autofill element data. + * @param {MutationRecord[]} mutations + * @private + */ + private handleMutationObserverMutation = (mutations: MutationRecord[]) => { + if (this.currentLocationHref !== globalThis.location.href) { + this.handleWindowLocationMutation(); + + return; + } + + for (let mutationsIndex = 0; mutationsIndex < mutations.length; mutationsIndex++) { + const mutation = mutations[mutationsIndex]; + if ( + mutation.type === "childList" && + (this.isAutofillElementNodeMutated(mutation.removedNodes, true) || + this.isAutofillElementNodeMutated(mutation.addedNodes)) + ) { + this.domRecentlyMutated = true; + this.noFieldsFound = false; + continue; + } + + if (mutation.type === "attributes") { + this.handleAutofillElementAttributeMutation(mutation); + } + } + + if (this.domRecentlyMutated) { + this.updateAutofillElementsAfterMutation(); + } + }; + + /** + * Handles a mutation to the window location. Clears the autofill elements + * and updates the autofill elements after a timeout. + * @private + */ + private handleWindowLocationMutation() { + this.currentLocationHref = globalThis.location.href; + + this.domRecentlyMutated = true; + this.noFieldsFound = false; + + this.autofillFormElements.clear(); + this.autofillFieldElements.clear(); + + this.updateAutofillElementsAfterMutation(); + } + + /** + * Checks if the passed nodes either contain or are autofill elements. + * @param {NodeList} nodes + * @param {boolean} isRemovingNodes + * @returns {boolean} + * @private + */ + private isAutofillElementNodeMutated(nodes: NodeList, isRemovingNodes = false): boolean { + if (!nodes.length) { + return false; + } + + let isElementMutated = false; + const mutatedElements = []; + for (let index = 0; index < nodes.length; index++) { + const node = nodes[index]; + if (!(node instanceof HTMLElement)) { + continue; + } + + if (node instanceof HTMLFormElement || this.isNodeFormFieldElement(node)) { + isElementMutated = true; + mutatedElements.push(node); + continue; + } + + const childNodes = this.queryAllTreeWalkerNodes( + node, + (node: Node) => node instanceof HTMLFormElement || this.isNodeFormFieldElement(node) + ) as HTMLElement[]; + if (childNodes.length) { + isElementMutated = true; + mutatedElements.push(...childNodes); + } + } + + if (isRemovingNodes) { + for (let elementIndex = 0; elementIndex < mutatedElements.length; elementIndex++) { + this.deleteCachedAutofillElement( + mutatedElements[elementIndex] as + | ElementWithOpId + | ElementWithOpId + ); + } + } + + return isElementMutated; + } + + /** + * Deletes any cached autofill elements that have been + * removed from the DOM. + * @param {ElementWithOpId | ElementWithOpId} element + * @private + */ + private deleteCachedAutofillElement( + element: ElementWithOpId | ElementWithOpId + ) { + if (element instanceof HTMLFormElement && this.autofillFormElements.has(element)) { + this.autofillFormElements.delete(element); + return; + } + + if (this.autofillFieldElements.has(element)) { + this.autofillFieldElements.delete(element); + } + } + + /** + * Updates the autofill elements after a DOM mutation has occurred. + * Is debounced to prevent excessive updates. + * @private + */ + private updateAutofillElementsAfterMutation() { + if (this.updateAutofillElementsAfterMutationTimeout) { + clearTimeout(this.updateAutofillElementsAfterMutationTimeout); + } + + this.updateAutofillElementsAfterMutationTimeout = setTimeout( + this.getPageDetails.bind(this), + this.updateAfterMutationTimeoutDelay + ); + } + + /** + * Handles observed DOM mutations related to an autofill element attribute. + * @param {MutationRecord} mutation + * @private + */ + private handleAutofillElementAttributeMutation(mutation: MutationRecord) { + const targetElement = mutation.target; + if (!(targetElement instanceof HTMLElement)) { + return; + } + + const attributeName = mutation.attributeName?.toLowerCase(); + const autofillForm = this.autofillFormElements.get( + targetElement as ElementWithOpId + ); + + if (autofillForm) { + this.updateAutofillFormElementData( + attributeName, + targetElement as ElementWithOpId, + autofillForm + ); + + return; + } + + const autofillField = this.autofillFieldElements.get( + targetElement as ElementWithOpId + ); + if (!autofillField) { + return; + } + + this.updateAutofillFieldElementData( + attributeName, + targetElement as ElementWithOpId, + autofillField + ); + } + + /** + * Updates the autofill form element data based on the passed attribute name. + * @param {string} attributeName + * @param {ElementWithOpId} element + * @param {AutofillForm} dataTarget + * @private + */ + private updateAutofillFormElementData( + attributeName: string, + element: ElementWithOpId, + dataTarget: AutofillForm + ) { + const updateAttribute = (dataTargetKey: string) => { + this.updateAutofillDataAttribute({ element, attributeName, dataTarget, dataTargetKey }); + }; + const updateActions: Record = { + action: () => (dataTarget.htmlAction = this.getFormActionAttribute(element)), + name: () => updateAttribute("htmlName"), + id: () => updateAttribute("htmlID"), + method: () => updateAttribute("htmlMethod"), + }; + + if (!updateActions[attributeName]) { + return; + } + + updateActions[attributeName](); + this.autofillFormElements.set(element, dataTarget); + } + + /** + * Updates the autofill field element data based on the passed attribute name. + * @param {string} attributeName + * @param {ElementWithOpId} element + * @param {AutofillField} dataTarget + * @returns {Promise} + * @private + */ + private async updateAutofillFieldElementData( + attributeName: string, + element: ElementWithOpId, + dataTarget: AutofillField + ) { + const updateAttribute = (dataTargetKey: string) => { + this.updateAutofillDataAttribute({ element, attributeName, dataTarget, dataTargetKey }); + }; + const updateActions: Record = { + maxlength: () => (dataTarget.maxLength = this.getAutofillFieldMaxLength(element)), + id: () => updateAttribute("htmlID"), + name: () => updateAttribute("htmlName"), + class: () => updateAttribute("htmlClass"), + tabindex: () => updateAttribute("tabindex"), + title: () => updateAttribute("tabindex"), + rel: () => updateAttribute("rel"), + tagname: () => (dataTarget.tagName = this.getAttributeLowerCase(element, "tagName")), + type: () => (dataTarget.type = this.getAttributeLowerCase(element, "type")), + value: () => (dataTarget.value = this.getElementValue(element)), + checked: () => (dataTarget.checked = this.getAttributeBoolean(element, "checked")), + disabled: () => (dataTarget.disabled = this.getAttributeBoolean(element, "disabled")), + readonly: () => (dataTarget.readonly = this.getAttributeBoolean(element, "readonly")), + autocomplete: () => (dataTarget.autoCompleteType = this.getAutoCompleteAttribute(element)), + "data-label": () => updateAttribute("label-data"), + "aria-label": () => updateAttribute("label-aria"), + "aria-hidden": () => + (dataTarget["aria-hidden"] = this.getAttributeBoolean(element, "aria-hidden", true)), + "aria-disabled": () => + (dataTarget["aria-disabled"] = this.getAttributeBoolean(element, "aria-disabled", true)), + "aria-haspopup": () => + (dataTarget["aria-haspopup"] = this.getAttributeBoolean(element, "aria-haspopup", true)), + "data-stripe": () => updateAttribute("data-stripe"), + }; + + if (!updateActions[attributeName]) { + return; + } + + updateActions[attributeName](); + + const visibilityAttributesSet = new Set(["class", "style"]); + if ( + visibilityAttributesSet.has(attributeName) && + !dataTarget.htmlClass?.includes("com-bitwarden-browser-animated-fill") + ) { + dataTarget.viewable = await this.domElementVisibilityService.isFormFieldViewable(element); + } + + this.autofillFieldElements.set(element, dataTarget); + } + + /** + * Gets the attribute value for the passed element, and returns it. If the dataTarget + * and dataTargetKey are passed, it will set the value of the dataTarget[dataTargetKey]. + * @param UpdateAutofillDataAttributeParams + * @returns {string} + * @private + */ + private updateAutofillDataAttribute({ + element, + attributeName, + dataTarget, + dataTargetKey, + }: UpdateAutofillDataAttributeParams) { + const attributeValue = this.getPropertyOrAttribute(element, attributeName); + if (dataTarget && dataTargetKey) { + dataTarget[dataTargetKey] = attributeValue; + } + + return attributeValue; + } } export default CollectAutofillContentService; diff --git a/apps/browser/src/autofill/services/dom-element-visibility.service.ts b/apps/browser/src/autofill/services/dom-element-visibility.service.ts index 4be59d7f276..2797ee0eb3d 100644 --- a/apps/browser/src/autofill/services/dom-element-visibility.service.ts +++ b/apps/browser/src/autofill/services/dom-element-visibility.service.ts @@ -13,7 +13,6 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac */ async isFormFieldViewable(element: FormFieldElement): Promise { const elementBoundingClientRect = element.getBoundingClientRect(); - if ( this.isElementOutsideViewportBounds(element, elementBoundingClientRect) || this.isElementHiddenByCss(element) @@ -176,7 +175,10 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac ): boolean { const elementBoundingClientRect = targetElementBoundingClientRect || targetElement.getBoundingClientRect(); - const elementAtCenterPoint = targetElement.ownerDocument.elementFromPoint( + const elementRootNode = targetElement.getRootNode(); + const rootElement = + elementRootNode instanceof ShadowRoot ? elementRootNode : targetElement.ownerDocument; + const elementAtCenterPoint = rootElement.elementFromPoint( elementBoundingClientRect.left + elementBoundingClientRect.width / 2, elementBoundingClientRect.top + elementBoundingClientRect.height / 2 ); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index 4e47e73704b..ad40b76fbcd 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -82,7 +82,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf if ( !savedUrls?.some((url) => url.startsWith(`https://${window.location.hostname}`)) || window.location.protocol !== "http:" || - !document.querySelectorAll("input[type=password]")?.length + !this.isPasswordFieldWithinDocument() ) { return false; } @@ -95,6 +95,22 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf return !confirm(confirmationWarning); } + /** + * Checks if there is a password field within the current document. Includes + * password fields that are present within the shadow DOM. + * @returns {boolean} + * @private + */ + private isPasswordFieldWithinDocument(): boolean { + return Boolean( + this.collectAutofillContentService.queryAllTreeWalkerNodes( + document.documentElement, + (node: Node) => node instanceof HTMLInputElement && node.type === "password", + false + )?.length + ); + } + /** * Checking if the autofill is occurring within an untrusted iframe. If the page is within an untrusted iframe, * the user is prompted to confirm that they want to autofill on the page. If the user cancels the autofill, From e7a05e115bab42270ce48a28a9d1a2fcff339c8b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 07:55:31 +0000 Subject: [PATCH 03/16] Autosync the updated translations (#6369) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 13 ++--------- apps/web/src/locales/ar/messages.json | 19 ++++------------ apps/web/src/locales/az/messages.json | 29 ++++++++---------------- apps/web/src/locales/be/messages.json | 19 ++++------------ apps/web/src/locales/bg/messages.json | 13 ++--------- apps/web/src/locales/bn/messages.json | 13 ++--------- apps/web/src/locales/bs/messages.json | 13 ++--------- apps/web/src/locales/ca/messages.json | 13 ++--------- apps/web/src/locales/cs/messages.json | 13 ++--------- apps/web/src/locales/cy/messages.json | 13 ++--------- apps/web/src/locales/da/messages.json | 13 ++--------- apps/web/src/locales/de/messages.json | 13 ++--------- apps/web/src/locales/el/messages.json | 13 ++--------- apps/web/src/locales/en_GB/messages.json | 13 ++--------- apps/web/src/locales/en_IN/messages.json | 13 ++--------- apps/web/src/locales/eo/messages.json | 13 ++--------- apps/web/src/locales/es/messages.json | 13 ++--------- apps/web/src/locales/et/messages.json | 13 ++--------- apps/web/src/locales/eu/messages.json | 13 ++--------- apps/web/src/locales/fa/messages.json | 13 ++--------- apps/web/src/locales/fi/messages.json | 13 ++--------- apps/web/src/locales/fil/messages.json | 13 ++--------- apps/web/src/locales/fr/messages.json | 13 ++--------- apps/web/src/locales/gl/messages.json | 13 ++--------- apps/web/src/locales/he/messages.json | 13 ++--------- apps/web/src/locales/hi/messages.json | 13 ++--------- apps/web/src/locales/hr/messages.json | 13 ++--------- apps/web/src/locales/hu/messages.json | 13 ++--------- apps/web/src/locales/id/messages.json | 13 ++--------- apps/web/src/locales/it/messages.json | 13 ++--------- apps/web/src/locales/ja/messages.json | 13 ++--------- apps/web/src/locales/ka/messages.json | 13 ++--------- apps/web/src/locales/km/messages.json | 13 ++--------- apps/web/src/locales/kn/messages.json | 13 ++--------- apps/web/src/locales/ko/messages.json | 13 ++--------- apps/web/src/locales/lv/messages.json | 13 ++--------- apps/web/src/locales/ml/messages.json | 13 ++--------- apps/web/src/locales/mr/messages.json | 13 ++--------- apps/web/src/locales/my/messages.json | 13 ++--------- apps/web/src/locales/nb/messages.json | 13 ++--------- apps/web/src/locales/ne/messages.json | 13 ++--------- apps/web/src/locales/nl/messages.json | 13 ++--------- apps/web/src/locales/nn/messages.json | 13 ++--------- apps/web/src/locales/or/messages.json | 13 ++--------- apps/web/src/locales/pl/messages.json | 13 ++--------- apps/web/src/locales/pt_BR/messages.json | 13 ++--------- apps/web/src/locales/pt_PT/messages.json | 13 ++--------- apps/web/src/locales/ro/messages.json | 13 ++--------- apps/web/src/locales/ru/messages.json | 13 ++--------- apps/web/src/locales/si/messages.json | 13 ++--------- apps/web/src/locales/sk/messages.json | 13 ++--------- apps/web/src/locales/sl/messages.json | 13 ++--------- apps/web/src/locales/sr/messages.json | 13 ++--------- apps/web/src/locales/sr_CS/messages.json | 13 ++--------- apps/web/src/locales/sv/messages.json | 13 ++--------- apps/web/src/locales/te/messages.json | 13 ++--------- apps/web/src/locales/th/messages.json | 13 ++--------- apps/web/src/locales/tr/messages.json | 13 ++--------- apps/web/src/locales/uk/messages.json | 13 ++--------- apps/web/src/locales/vi/messages.json | 13 ++--------- apps/web/src/locales/zh_CN/messages.json | 13 ++--------- apps/web/src/locales/zh_TW/messages.json | 13 ++--------- 62 files changed, 138 insertions(+), 696 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 46c073483aa..8f7174fde00 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimum lêergrootte is 500 MB." }, - "updateKey": { - "message": "U kan eers hierdie funksie gebruik wanneer u u enkripsiesleutel bygewerk het." - }, "addedItem": { "message": "Toegevoegde item" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Sleutel bygewerk" }, - "updateKeyTitle": { - "message": "Werk sleutel by" - }, "updateEncryptionKey": { "message": "Werk enkripsiesleutel by" }, - "updateEncryptionKeyShortDesc": { - "message": "U gebruik tans ’n verouderde enkripsieskema." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index 8639957af05..6b474dce33d 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "الحجم الأقصى للملف هو 500 ميجابايت." }, - "updateKey": { - "message": "لا يمكنك استخدام هذه المِيزة حتى تحديث مفتاح التشفير الخاص بك." - }, "addedItem": { "message": "تمت إضافة العنصر" }, @@ -752,10 +749,10 @@ "message": "لا توجد أية عناصر في القائمة." }, "noPermissionToViewAllCollectionItems": { - "message": "You do not have permission to view all items in this collection." + "message": "ليس لديك الصلاحية لعرض جميع العناصر في هذه المجموعة." }, "noCollectionsInList": { - "message": "There are no collections to list." + "message": "لا توجد مجموعات لعرضها." }, "noGroupsInList": { "message": "لا توجد أية مجموعات لعرضها." @@ -764,7 +761,7 @@ "message": "ليس هناك مستخدمون لعرضهم." }, "noMembersInList": { - "message": "There are no members to list." + "message": "ليس هناك أعضاء لعرضهم." }, "noEventsInList": { "message": "لا توجد أية أحداث لعرضها." @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 5e601873e7d..8c302c9a483 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimal fayl həcmi 500 MB-dır." }, - "updateKey": { - "message": "Şifrələmə açarınızı güncəlləyənə qədər bu özəlliyi istifadə edə bilməzsiniz." - }, "addedItem": { "message": "Element əlavə edildi" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Açar güncəlləndi" }, - "updateKeyTitle": { - "message": "Açarı güncəllə" - }, "updateEncryptionKey": { "message": "Şifrələmə açarını güncəllə" }, - "updateEncryptionKeyShortDesc": { - "message": "Hal-hazırda köhnə bir şifrələmə sxemi istifadə edirsiniz." - }, - "updateEncryptionKeyDesc": { - "message": "Daha yaxşı güvənlik və daha yeni özəlliklərə müraciəti təmin edən daha böyük şifrələmə açarlarına keçdik. Şifrələmə açarınızı güncəlləmək sürətli və asandır. Sadəcə aşağıda ana parolunuzu yazın. Bu güncəlləmə, nəticədə məcburi olacaq." + "updateEncryptionSchemeDesc": { + "message": "Daha yaxşı güvənlik təqdim etmək üçün şifrələmə sxemini dəyişdirdik. Ana parolunuzu aşağıda daxil edərək şifrələmə açarınızı güncəlləyin." }, "updateEncryptionKeyWarning": { "message": "Şifrələmə açarını güncəllədikdən sonra, hazırda istifadə etdiyiniz (mobil tətbiq və ya brauzer genişləndirmələri kimi) bütün Bitwarden tətbiqlərində çıxış edib yenidən giriş etməlisiniz. Çıxış edib təkrar giriş etməmək (yeni şifrələmə açarının endirilməsi prosesi) verilənlərin zədələnməsi ilə nəticələnə bilər. Avtomatik olaraq çıxış etməyə çalışacağıq, bu gecikə bilər." @@ -3634,7 +3625,7 @@ "message": "Bu element, düzəldilməli köhnə fayl qoşmalarını ehtiva edir." }, "attachmentFixDescription": { - "message": "This attachment uses outdated encryption. Select 'Fix' to download, re-encrypt, and re-upload the attachment." + "message": "Bu qoşma güncəl olmayan şifrələmə istifadə edir. Qoşmanı endirmək, təkrar şifrələmək və təkrar yükləmək üçün \"Düzəlt\"i seçin." }, "fix": { "message": "Düzəlt", @@ -4622,10 +4613,10 @@ "message": "Şifrələmə üsuluna əsasən, ana parollar və güvənilən cihazlar unudulduqda və ya itirildikdə hesabları bərpa edin." }, "accountRecoveryPolicyWarning": { - "message": "Existing accounts with master passwords will require members to self-enroll before administrators can recover their accounts. Automatic enrollment will turn on account recovery for new members." + "message": "Ana parolları olan mövcud hesablar, administratorların öz hesablarını bərpa edə bilməsindən əvvəl üzvlərin öz-özünə yazılmalarını tələb edəcək. Avto-yazılma, yeni üzvlər üçün hesab bərpasını işə salacaq." }, "accountRecoverySingleOrgRequirementDesc": { - "message": "The single organization Enterprise policy must be turned on before activating this policy." + "message": "Bu siyasət aktivləşdirilməzdən əvvəl, \"Tək təşkilat\" Müəssisə siyasəti işə salınmalıdır." }, "resetPasswordPolicyAutoEnroll": { "message": "Avtomatik qeydiyyat" @@ -4730,7 +4721,7 @@ "message": "Xəta" }, "accountRecoveryManageUsers": { - "message": "Manage users must also be granted with the manage account recovery permission" + "message": "\"Hesab bərpasını idarə et\" icazəsi ilə birgə \"İstifadəçiləri idarə et\"ə icazə verilməlidir" }, "setupProvider": { "message": "Provayder quraşdırması" @@ -4942,7 +4933,7 @@ "message": "Fərdi anbarın ixracını sıradan çıxart" }, "disablePersonalVaultExportDescription": { - "message": "Do not allow members to export data from their individual vault." + "message": "Üzvlərin öz anbarlarından datanı xaricə köçürməsinə icazə verməyin." }, "vaultExportDisabled": { "message": "Anbar ixracı sıradan çıxarıldı" @@ -5246,7 +5237,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" }, "ssoPolicyHelpAnchor": { - "message": "require single sign-on authentication policy", + "message": "tək daxil olma kimlik təsdiqləmə siyasətini tələb et", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" }, "ssoPolicyHelpEnd": { @@ -5446,7 +5437,7 @@ "message": "Təşkilat anbarının ixracı" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Yalnız $EMAIL$ ilə əlaqələndirilmiş fərdi anbar elementləri xaricə köçürüləcək. Təşkilat anbar elementləri daxil edilməyəcək. Yalnız anbar element məlumatları xaricə köçürüləcək və əlaqələndirilmiş qoşmalar daxil edilməyəcək.", "placeholders": { "email": { "content": "$1", @@ -7179,6 +7170,6 @@ "message": "Beta" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "Artıq bir hesabınız var?" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 449b220591f..ca579026693 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Максімальны памер файла 500 МБ." }, - "updateKey": { - "message": "Вы не зможаце выкарыстоўваць гэту функцыю, пакуль не абнавіце свой ключ шыфравання." - }, "addedItem": { "message": "Элемент дададзены" }, @@ -1098,7 +1095,7 @@ "message": "Уліковы запіс абноўлены" }, "changeEmail": { - "message": "Змяніць электронную пошту" + "message": "Змяніць адрас электроннай пошты" }, "changeEmailTwoFactorWarning": { "message": "Працягваючы, вы зменіце адрас электроннай пошты вашага ўліковага запісу. Гэта не паўплывае на змяненне адраса электроннай пошты, які выкарыстоўваецца для двухфактарнай аўтэнтыфікацыі. Калі вы хочаце змяніць і яго, то вам неабходна гэта зрабіць у наладах двухфактарнай аўтэнтыфікацыі." @@ -1426,10 +1423,10 @@ "message": "Абараніце свой уліковы запіс з дапамогай дадатковага кроку праверкі падчас уваходу." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Уключыць двухэтапную аўтарызацыю для вашай арганізацыі." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Забяспечыць параметры двухэтапнага ўваходу ў Bitwarden для ўдзельнікаў, выкарыстоўваючы ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ключ абноўлены" }, - "updateKeyTitle": { - "message": "Абнавіць ключ" - }, "updateEncryptionKey": { "message": "Абнавіць ключ шыфравання" }, - "updateEncryptionKeyShortDesc": { - "message": "Зараз вы выкарыстоўваеце састарэлую схему шыфравання." - }, - "updateEncryptionKeyDesc": { - "message": "Мы перайшлі на больш складаныя ключы шыфравання, якія забяспечваюць лепшую бяспеку і доступ да самых новых функцый. Абнаўленне вашых ключоў шыфравання адбудзецца хутка і лёгка. Проста ўвядзіце свой асноўны пароль знізу. Гэта абнаўленне ўрэшце стане абавязковым." + "updateEncryptionSchemeDesc": { + "message": "Мы змянілі схему шыфравання каб надаць найлепшы ўзровень бяспекі. Каб абнавіць ваш ключ шыфравання, увядзіце ваш асноўны пароль ніжэй." }, "updateEncryptionKeyWarning": { "message": "Пасля абнаўлення вашага ключа шыфравання вам неабходна выйсці з сістэмы, а потым выканаць паўторны ўваход ва ўсе праграмы Bitwarden, якія вы зараз выкарыстоўваеце (напрыклад, мабільныя праграмы або пашырэнні для браўзераў). Збой пры выхадзе і паўторным уваходзе (пры гэтым спампоўваецца ваш новы ключ шыфравання) можа стаць прычынай пашкоджання даных. Мы паспрабуем аўтаматычна ажыццявіць завяршэнне ўсіх вашых сеансаў, але гэта можа адбывацца з затрымкай." diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 1813900cd1b..66faad556a6 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Големината на файла е най-много 500 MB." }, - "updateKey": { - "message": "Трябва да обновите шифриращия си ключ, за да използвате тази възможност." - }, "addedItem": { "message": "Елементът е добавен" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ключът е обновен" }, - "updateKeyTitle": { - "message": "Обновяване на ключа" - }, "updateEncryptionKey": { "message": "Обновяване на ключа за шифриране" }, - "updateEncryptionKeyShortDesc": { - "message": "Ползвате остаряла схема за шифриране." - }, - "updateEncryptionKeyDesc": { - "message": "Вече ползваме по-дълги ключове за по-добра сигурност и повече възможности. Обновяването на ключа е бързо и лесно. Просто въведете главната си парола по-долу. В някой бъдещ момент това обновяване ще бъде задължително." + "updateEncryptionSchemeDesc": { + "message": "Променихме схемата на шифроване, за да подобрим сигурността. Обновете шифриращия си ключ сега, като въведете главната си парола по-долу." }, "updateEncryptionKeyWarning": { "message": "След смяната на ключа за шифриране ще трябва да се отпишете и след това да се впишете в регистрацията си във всички приложения на Битуорден, които ползвате (като мобилното приложение и разширенията за браузъри). Ако не се отпишете и впишете повторно (за да получите достъп до новия ключ), рискувате да повредите записите си. Сега ще се пробва да бъдете отписани автоматично, това обаче може да се забави." diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 065e6482322..a333526490f 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "সর্বোচ্চ ফাইলের আকার ১০০ এমবি।" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index bff25d8f84c..3bd3b0d268a 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimalna veličina datoteke je 500 MB." }, - "updateKey": { - "message": "Ne možete koristiti ovu uslugu dok ne ažurirate Vaš enkripcioni ključ." - }, "addedItem": { "message": "Stavka Dodata" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index db32f7ca8d8..b2bb1ec1482 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "La mida màxima del fitxer és de 500 MB." }, - "updateKey": { - "message": "No podeu utilitzar aquesta funció fins que actualitzeu la vostra clau de xifratge." - }, "addedItem": { "message": "Element afegit" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Clau actualitzada" }, - "updateKeyTitle": { - "message": "Actualitza clau" - }, "updateEncryptionKey": { "message": "Actualitza la clau de xifratge" }, - "updateEncryptionKeyShortDesc": { - "message": "Esteu fent servir un esquema de xifrage antiquat." - }, - "updateEncryptionKeyDesc": { - "message": "Hem canviat a claus de xifratge més grans que proporcionen una millor seguretat i accés a característiques més noves. L'actualització de la vostra clau de xifratge és ràpida i senzilla. Només cal que introduïu la vostra contrasenya mestra a continuació. Aquesta actualització es convertirà en obligatòria en qualsevol moment." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Després d'actualitzar la vostra clau de xifratge, heu de tancar la sessió i tornar a entrar a totes les aplicacions de Bitwarden que esteu utilitzant actualment (com ara l'aplicació mòbil o les extensions del navegador). Si no es tanca i torna a iniciar la sessió (la qual descarrega la vostra nova clau de xifratge) pot provocar corrupció en les dades. Intentarem registrar-vos automàticament, però, es pot retardar." diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index b145c4d1950..f2854b51284 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximální velikost souboru je 500 MB." }, - "updateKey": { - "message": "Dokud neaktualizujete svůj šifrovací klíč, nemůžete tuto funkci použít." - }, "addedItem": { "message": "Položka byla přidána" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Klíč byl aktualizován" }, - "updateKeyTitle": { - "message": "Aktualizovat klíč" - }, "updateEncryptionKey": { "message": "Aktualizovat šifrovací klíč" }, - "updateEncryptionKeyShortDesc": { - "message": "Aktuálně používáte zastaralé šifrovací schéma." - }, - "updateEncryptionKeyDesc": { - "message": "Přešli jsme na delší šifrovací klíče, které poskytují vyšší úroveň zabezpečení a přístup k novým funkcím. Aktualizace Vašeho šifrovacího klíče je rychlá a snadná. Stačí níže zadat Vaše hlavní heslo. Provedení této aktualizace může být v budoucnu povinné." + "updateEncryptionSchemeDesc": { + "message": "Změnili jsme šifrovací schéma pro zajištění větší bezpečnosti. Aktualizujte Váš šifrovací klíč nyní zadáním hlavního hesla níže." }, "updateEncryptionKeyWarning": { "message": "Po aktualizace šifrovacího klíče dojde k odhlášení a budete se muset opětovně přihlásit do všech aplikací Bitwardenu, které aktuálně používáte (např. mobilní aplikace či rozšíření pro prohlížeč). Nezdaří-li se odhlášení a opětovné přihlášení (během něhož bude stažen nový šifrovací klíč), může dojít k poškození dat. Pokusíme se Vás automaticky odhlásit, nicméně, může to chvíli trvat." diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index e2e0db2e83e..f19f8e63d23 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maks. filstørrelse er 500 MB." }, - "updateKey": { - "message": "Denne funktion kan ikke bruges, før du opdaterer din krypteringsnøgle." - }, "addedItem": { "message": "Emne tilføjet" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Nøgle opdateret" }, - "updateKeyTitle": { - "message": "Opdatér nøgle" - }, "updateEncryptionKey": { "message": "Opdatér krypteringsnøgle" }, - "updateEncryptionKeyShortDesc": { - "message": "Du bruger i øjeblikket en forældet krypteringsmetode." - }, - "updateEncryptionKeyDesc": { - "message": "Vi har skiftet til større krypteringsnøgler, der giver bedre sikkerhed og adgang til nyere funktioner. Opdatering af din krypteringsnøgle er hurtig og nem. Indtast bare din hovedadgangskode nedenfor. Denne opdatering bliver obligatorisk på et senere tidspunkt." + "updateEncryptionSchemeDesc": { + "message": "Vi har ændret krypteringsmetoden mhp. bedre sikkerhed. Opdatér krypteringsnøglen nu ved at angive din hovedadgangskode nedenfor." }, "updateEncryptionKeyWarning": { "message": "Efter opdatering af din krypteringsnøgle skal du logge ud og ind igen i alle Bitwarden-programmer, du bruger i øjeblikket (f.eks. mobilapp eller browserudvidelser). Hvis du ikke logger ud og ind (som downloader din nye krypteringsnøgle), kan det resultere i data korruption. Vi vil forsøge at logge dig ud automatisk, men det kan blive forsinket." diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 5be4cb32039..d1638271f5d 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Die maximale Dateigröße beträgt 500 MB." }, - "updateKey": { - "message": "Sie können diese Funktion nicht nutzen, bevor Sie Ihren Verschlüsselungscode aktualisiert haben." - }, "addedItem": { "message": "Eintrag hinzugefügt" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Schlüssel aktualisiert" }, - "updateKeyTitle": { - "message": "Schlüssel aktualisieren" - }, "updateEncryptionKey": { "message": "Verschlüsselungsschlüssel aktualisieren" }, - "updateEncryptionKeyShortDesc": { - "message": "Sie verwenden derzeit ein veraltetes Verschlüsselungsschema." - }, - "updateEncryptionKeyDesc": { - "message": "Wir sind auf größere Verschlüsselungscodes umgestiegen, welche bessere Sicherheit und Zugang zu neuen Features bieten. Das Update Ihres Verschlüsselungscodes ist schnell und einfach. Geben Sie einfach hier Ihr Master-Passwort ein. Das Update wird irgendwann verpflichtend." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Nach der Aktualisierung Ihres Verschlüsselungsschlüssels musst du dich bei allen Bitwarden-Anwendungen, die du momentan benutzt, erneut anmelden (wie z.B. die mobile App oder die Browser-Erweiterungen). Fehler bei Ab- und Anmeldung (die deinen neuen Verschlüsselungsschlüssel herunterlädt) könnte zu einer Beschädigung der Daten führen. Wir werden versuchen dich automatisch abzumelden, was jedoch verzögert geschehen kann." diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index d2d87fb7180..f0f1ee2376f 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Το μέγιστο μέγεθος αρχείου είναι 500 MB." }, - "updateKey": { - "message": "Δεν μπορείτε να χρησιμοποιήσετε αυτήν τη δυνατότητα μέχρι να ενημερώσετε το κλειδί κρυπτογράφησης." - }, "addedItem": { "message": "Προστέθηκε στοιχείο" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Το Κλειδί Ενημερώθηκε" }, - "updateKeyTitle": { - "message": "Ενημέρωση Κλειδιού" - }, "updateEncryptionKey": { "message": "Ενημέρωση Κλειδιού Κρυπτογράφησης" }, - "updateEncryptionKeyShortDesc": { - "message": "Αυτήν τη στιγμή χρησιμοποιείτε ένα ξεπερασμένο σύστημα κρυπτογράφησης." - }, - "updateEncryptionKeyDesc": { - "message": "Έχουμε μετακινηθεί σε μεγαλύτερα κλειδιά κρυπτογράφησης που παρέχουν καλύτερη ασφάλεια και πρόσβαση σε νεότερες λειτουργίες. Η ενημέρωση του κλειδιού κρυπτογράφησης είναι γρήγορη και εύκολη. Απλά πληκτρολογήστε τον κύριο κωδικό σας παρακάτω. Αυτή η ενημέρωση τελικά θα καταστεί υποχρεωτική." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Μετά την ενημέρωση του κλειδιού κρυπτογράφησης, πρέπει να αποσυνδεθείτε και να επιστρέψετε σε όλες τις εφαρμογές Bitwarden που χρησιμοποιείτε αυτήν τη στιγμή (όπως η εφαρμογή για κινητά ή οι επεκτάσεις του προγράμματος περιήγησης). Η αποτυχία αποσύνδεσης και επαναφοράς (στην οποία γίνεται λήψη του νέου κλειδιού κρυπτογράφησης) ενδέχεται να προκαλέσει καταστροφή δεδομένων. Θα προσπαθήσουμε να αποσυνδεθείτε αυτόματα, ωστόσο αυτό μπορεί να καθυστερήσει." diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 65abe5b1f72..1b96758b956 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed." diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index dc120f301e9..7760e477043 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Added item" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, although this may be delayed." diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index e259d0f0732..91215eca960 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimuma dosiergrandeco estas 500 MB." }, - "updateKey": { - "message": "Vi ne povas uzi ĉi tiun funkcion antaŭ ol vi ĝisdatigos vian ĉifran ŝlosilon." - }, "addedItem": { "message": "Aldonita ero" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ŝlosilo ĝisdatigita" }, - "updateKeyTitle": { - "message": "Ĝisdatiga Ŝlosilo" - }, "updateEncryptionKey": { "message": "Ĝisdatigi Ĉifran Ŝlosilon" }, - "updateEncryptionKeyShortDesc": { - "message": "Vi nun uzas malmodernan ĉifran skemon." - }, - "updateEncryptionKeyDesc": { - "message": "Ni transloĝiĝis al pli grandaj ĉifraj ŝlosiloj, kiuj donas pli bonan sekurecon kaj aliron al pli novaj funkcioj. Ĝisdatigi vian ĉifran ŝlosilon estas rapide kaj facile. Simple tajpu vian ĉefan pasvorton sube. Ĉi tiu ĝisdatigo fine fariĝos deviga." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Post ĝisdatigi vian ĉifradan ŝlosilon, vi devas elsaluti kaj reeniri al ĉiuj Bitwarden-aplikaĵoj, kiujn vi nun uzas (kiel la poŝtelefona programo aŭ retumila etendaĵoj). Malsukceso elsaluti kaj reeniri (kiu elŝutas via nova ĉifra ŝlosilo) povas rezultigi korupton de datumoj. Ni provos elsaluti vin aŭtomate, tamen ĝi eble prokrastos. " diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 8f58624a6f7..c9770e20c55 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "El tamaño máximo de archivo es de 500MB." }, - "updateKey": { - "message": "No puedes usar esta característica hasta que actualices tu clave de cifrado." - }, "addedItem": { "message": "Elemento añadido" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Clave actualizada" }, - "updateKeyTitle": { - "message": "Actualizar clave" - }, "updateEncryptionKey": { "message": "Actualizar clave de cifrado" }, - "updateEncryptionKeyShortDesc": { - "message": "Actualmente utilizas un esquema de cifrado desactualizado." - }, - "updateEncryptionKeyDesc": { - "message": "Hemos cambiado a unas claves de cifrado más grandes para ofrecer una mayor seguridad y acceso a nuevas características. Actualizar tu clave de cifrado actual es fácil y rápido. Solo necesitas escribir tu contraseña maestra debajo. Esta actualización en algún momento se volverá obligatoria." + "updateEncryptionSchemeDesc": { + "message": "Hemos cambiado el esquema de cifrado para proporcionar una mayor seguridad. Actualice su clave de cifrado ahora introduciendo su contraseña maestra a continuación." }, "updateEncryptionKeyWarning": { "message": "Una vez actualices tu clave de cifrado, será necesario que cierres sesión y vuelvas a identificarte en todas las aplicaciones de Bitwarden que estés utilizando (como la aplicación móvil o la extensión de navegador). Si la reautenticación falla (la cual descargaría la nueva clave de cifrad) puede producirse corrupción de datos. Intentaremos cerrar tu sesión automáticamente, pero puede tardar un tiempo." diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 92ac930999f..8911118e418 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimaalne faili suurus on 500 MB." }, - "updateKey": { - "message": "Seda funktsiooni ei saa enne krüpteerimise võtme uuendamist kasutada." - }, "addedItem": { "message": "Kirje on lisatud" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Võti on uuendatud" }, - "updateKeyTitle": { - "message": "Uuenda võtit" - }, "updateEncryptionKey": { "message": "Uuenda krüpteerimisvõtit" }, - "updateEncryptionKeyShortDesc": { - "message": "Kasutad hetkeseisuga aegunud krüpteerimise skeemi." - }, - "updateEncryptionKeyDesc": { - "message": "Oleme kasutusele võtnud suuremad krüpteerimise võtmed, mis pakuvad paremat turvalisust ja uusi funktsioone. Krüpteerimisvõtmete uuendamine on lihtne ja kiire. Piisab ainult ülemparooli sisestamisest. See uuendus on möödapääsmatu ja tuleb ära teha." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Pärast krüpteerimisvõtme uuendamist pead kõikides seadmetes, kus Bitwardeni rakendust kasutad, oma kontosse uuesti sisse logima (nt nutitelefonis ja brauseris). Välja- ja sisselogimise (mis ühtlasi laadib ka uue krüpteerimisvõtme) nurjumine võib tingida andmete riknemise. Üritame sinu seadmetest ise välja logida, aga see võib võtta natukene aega." diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 55074ebd56f..edaa113e829 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Eranskinaren gehienezko tamaina 500MB." }, - "updateKey": { - "message": "Ezin duzu ezaugarri hau erabili zifratze-gakoa eguneratu arte." - }, "addedItem": { "message": "Gehitu elementua" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Gakoa eguneratua" }, - "updateKeyTitle": { - "message": "Eguneratu gakoa" - }, "updateEncryptionKey": { "message": "Eguneratu zifratze-gakoa" }, - "updateEncryptionKeyShortDesc": { - "message": "Une honetan, zifratze sistema zaharkitua erabiltzen ari zara." - }, - "updateEncryptionKeyDesc": { - "message": "Zifratze-gako handiagoetara aldatu gara, segurtasun hobea eta ezaugarri berrietarako sarbidea ematen baitute. Zifratze-gakoa azkar eta erraz eguneratzen da. Idatzi zure pasahitz nagusia behean. Eguneratze hau nahitaezkoa izango da uneren batean." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Zifratze-gakoa eguneratu ondoren, saioa hasi eta erabiltzen ari zaren Bitwarden aplikazio guztietara itzuli behar duzu (adibidez, aplikazio mugikorra edo nabigatzailearen gehigarriak). Saioa berriro hasteak huts egiten badu (zifratze-gako berria deskargatzea dakar) datuen korrupzioa ekar lezake. Automatikoki saioa ixten saiatuko gara, baina atzeratu egin daiteke." diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 80c93b7e597..f52fb5bd431 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "بیشترین حجم فایل ۵۰۰ مگابایت است." }, - "updateKey": { - "message": "تا زمانی که کد رمزنگاری را به‌روز نکنید نمی‌توانید از این قابلیت استفاده کنید." - }, "addedItem": { "message": "مورد اضافه شد" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "کلیدها به‌روز شد" }, - "updateKeyTitle": { - "message": "کلید به‌روزرسانی" - }, "updateEncryptionKey": { "message": "کلید رمزگذاری را به‌روز کنید" }, - "updateEncryptionKeyShortDesc": { - "message": "شما در حال حاضر از یک طرح رمزگذاری قدیمی استفاده می‌کنید." - }, - "updateEncryptionKeyDesc": { - "message": "ما به سمت کلیدهای رمزگذاری بزرگتر رفته ایم که امنیت بهتر و دسترسی به ویژگی های جدیدتر را فراهم می‌کند. به‌روزرسانی کلید رمزگذاری شما سریع و آسان است. فقط کلمه عبور اصلی خود را در زیر تایپ کنید. این به‌روزرسانی در نهایت اجباری خواهد شد." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "پس از به‌روزرسانی کلید رمزگذاری، باید از سیستم خارج شوید و دوباره به همه برنامه‌های Bitwarden که در حال حاضر استفاده می‌کنید (مانند برنامه تلفن همراه یا برنامه‌های افزودنی مرورگر) وارد شوید. عدم خروج و ورود مجدد (که کلید رمزگذاری جدید شما را دانلود می‌کند) ممکن است منجر به خراب شدن داده‌ها شود. ما سعی خواهیم کرد شما را به طور خودکار از سیستم خارج کنیم، اما ممکن است با تأخیر انجام شود." diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index d1699f72505..c957bac7549 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Tiedoston enimmäiskoko on 500 Mt." }, - "updateKey": { - "message": "Et voi käyttää tätä toimintoa ennen kuin päivität salausavaimesi." - }, "addedItem": { "message": "Kohde lisättiin" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Avain päivitettiin" }, - "updateKeyTitle": { - "message": "Päivitä avain" - }, "updateEncryptionKey": { "message": "Päivitä salausavain" }, - "updateEncryptionKeyShortDesc": { - "message": "Käytät tällä hetkellä vanhentunutta salausjärjestelmää." - }, - "updateEncryptionKeyDesc": { - "message": "Olemme siirtyneet suurempiin salausavaimiin, jotka tarjoavat parempaa suojausta sekä uudempia toimintoja. Salausavaimesi päivitys on helppoa: syötä vain pääsalasanasi alle. Tästä päivityksestä tulee myöhemmin pakollinen." + "updateEncryptionSchemeDesc": { + "message": "Olemme muuttaneet salausmallia tarjotaksemme paremman tietoturvan. Päivitä salausavaimesi syöttämällä alle pääsalasanasi." }, "updateEncryptionKeyWarning": { "message": "Salausavaimesi päivityksen jälkeen, sinun tulee kirjautua ulos ja sitten takaisin sisään kaikissa Bitwarden-sovelluksissa, jotka ovat käytössäsi (esim. mobiilisovellus ja selainlaajennukset). Uudelleenkirjautumisen (joka lataa uuden salausavaimen) suorittamatta jättäminen saattaa johtaa tietojen vaurioitumiseen. Yritämme kirjata sinut ulos autmaattisesti, mutta tämä voi tapahtua vasta jonkin ajan kuluttua." diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index d395fed70d8..45455f87882 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Hanggang sa 500 MB lang dapat ang mga file." }, - "updateKey": { - "message": "Hindi mo magagamit ang feature na ito hanggang sa i-update mo ang encryption key mo." - }, "addedItem": { "message": "Naidagdag ang item" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Na update ang Key" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Na update ang encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "Kasalukuyan kang gumagamit ng isang hindi napapanahong scheme ng pag encrypt." - }, - "updateEncryptionKeyDesc": { - "message": "Lumipat kami sa mas malaking mga key ng pag encrypt na nagbibigay ng mas mahusay na seguridad at pag access sa mas bagong mga tampok. Ang pag update ng iyong key ng pag encrypt ay mabilis at madali. I type lamang ang iyong master password sa ibaba. Ang update na ito ay sa huli ay magiging sapilitan." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Matapos i update ang iyong key sa pag encrypt, kinakailangan kang mag log out at bumalik sa lahat ng mga application ng Bitwarden na kasalukuyang ginagamit mo (tulad ng mobile app o mga extension ng browser). Ang kabiguan na mag log out at bumalik sa (na nag download ng iyong bagong key ng pag encrypt) ay maaaring magresulta sa pagkasira ng data. Susubukan naming awtomatikong mag log out sa iyo, gayunpaman, maaari itong maantala." diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index d71064bd50f..32a524ca6ab 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "La taille maximale du fichier est de 500 Mo." }, - "updateKey": { - "message": "Vous ne pouvez pas utiliser cette fonctionnalité avant de mettre à jour votre clé de chiffrement." - }, "addedItem": { "message": "Élément ajouté" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Clé mise à jour" }, - "updateKeyTitle": { - "message": "Mettre à jour la clé" - }, "updateEncryptionKey": { "message": "Mettre à jour la clé de chiffrement" }, - "updateEncryptionKeyShortDesc": { - "message": "Vous utilisez actuellement un moyen de chiffrement obsolète." - }, - "updateEncryptionKeyDesc": { - "message": "Nous sommes passés à des clés de chiffrement plus grandes qui offrent une meilleure sécurité et un accès à des fonctionnalités plus récentes. La mise à jour de votre clé de chiffrement est rapide et facile. Il suffit de saisir votre mot de passe principal ci-dessous. Cette mise à jour deviendra éventuellement obligatoire." + "updateEncryptionSchemeDesc": { + "message": "Nous avons modifié le schéma de chiffrement pour améliorer la sécurité. Mettez à jour votre clé de chiffrement maintenant en entrant votre mot de passe principal ci-dessous." }, "updateEncryptionKeyWarning": { "message": "Après avoir mis à jour votre clé de chiffrement, vous devez vous déconnecter et vous reconnecter à toutes les applications Bitwarden que vous utilisez actuellement (comme l'application mobile ou les extensions de navigateur). Si vous ne vous déconnectez pas et ne vous reconnectez pas (ce qui télécharge votre nouvelle clé de chiffrement), cela peut entraîner une corruption des données. Nous tenterons de vous déconnecter automatiquement, mais cela peut être retardé." diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index ee3e68708ec..d269cdbc582 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "גודל הקובץ המירבי הוא 500 מגה." }, - "updateKey": { - "message": "לא ניתן להשתמש ביכולת זו עד שתעדכן את מפתח ההצפנה שלך." - }, "addedItem": { "message": "פריט שהתווסף" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "המפתח עודכן" }, - "updateKeyTitle": { - "message": "עדכן מפתח" - }, "updateEncryptionKey": { "message": "עדכן מפתח הצפנה" }, - "updateEncryptionKeyShortDesc": { - "message": "אתה משתמש בסכמת-הצפנה לא מעודכנת." - }, - "updateEncryptionKeyDesc": { - "message": "התחלנו להשתמש במפתחות הצפנה גדולים יותר בכדי לספק אבטחה טובה יותר ולאפשר גישה ליכולות חדשות. תהליך עדכון מפתחות האבטחה שלך הוא קל ומהיר. פשוט הזן את הסיסמה הראשית שלך. שים לב שעדכון זה יהפוך להכרחי בסופו של דבר." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "לאחר עדכון מפתחות ההצפנה שלך, תתבקש לצאת ולהכנס שוב בכל אפליקציות Bitwarden שאתה משתמש בהן (האפליקציה לפלאפון או ההרחבה לדפדפן). אם לא תצא ותכנס שוב (פעולת הכניסה מורידה את המפתח החדש), יתכן שתתקל במידע שגוי. אנו ננסה לגרום ליציאה אוטומטית, אך יתכן שהדבר לא יקרה מיידית." diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index c10d8c37284..3322c4db0db 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "अधिकतम फाइल आकार 500 MB है।" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index 27f841761d7..8d90b1528dc 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Najveća veličina datoteke je 500 MB." }, - "updateKey": { - "message": "Ne možeš koristiti ovu značajku prije nego ažuriraš ključ za šifriranje." - }, "addedItem": { "message": "Stavka dodana" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ključ ažuriran" }, - "updateKeyTitle": { - "message": "Ažuriraj ključ" - }, "updateEncryptionKey": { "message": "Ažuriraj ključ za šifriranje" }, - "updateEncryptionKeyShortDesc": { - "message": "Trenutno koristiš zastarjeli način šifriranja." - }, - "updateEncryptionKeyDesc": { - "message": "Prešli smo na korištenje duljih ključeva za šifriranje koji pružaju bolju zaštitu kao i pristup novim uslugama. Ažuriranje ključa za šifriranje je brzo i jednostavno. Samo unesi svoju glavnu lozinku. Ova nadogradnja će uskoro postati obavezna." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Nakon ažuriranja svojeg ključa za šifriranje, obavezno se trebaš odjaviti i ponovno prijaviti u sve Bitwarden aplikacije koje trenutno koristiš (npr. mobilna aplikacija, proširenje preglednika, ...). Ako se ne odjaviš i ponovno prijaviš (čime se preuzima tvoj novi ključ za šifriranje) može doći do oštećenja spremljenih podataka. Pokušati ćemo te automatski odjaviti, no, to bi možda moglo potrajati." diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index bec2a62727b..3721ce50344 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "A maximális fájl méret 500 MB." }, - "updateKey": { - "message": "Ez a funkció nem használható a titkosítási kulcs frissítéséig." - }, "addedItem": { "message": "Az elem hozzáadásra került." }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "A kulcs frissítésre került." }, - "updateKeyTitle": { - "message": "Kulcs frissítés" - }, "updateEncryptionKey": { "message": "Titkosítási kulcs frissítés" }, - "updateEncryptionKeyShortDesc": { - "message": "Jelenleg elavult titkosítási séma van használatban." - }, - "updateEncryptionKeyDesc": { - "message": "Hosszabb titkosítókulcsok kerültek használatba, amelyek jobb biztonságot és hozzáférést biztosítanak új funkciókhoz. A titkosító kulcs frissítése könnyű és gyors. Csak meg kell adni a mesterjelszót. Ez a frissítés kötelezővé válik." + "updateEncryptionSchemeDesc": { + "message": "A titkosítási rendszer megváltozott, hogy nagyobb biztonságot nyújtson. Frissítsük a titkosítási kulcsot az mesterjelszó megadásával lentebb." }, "updateEncryptionKeyWarning": { "message": "A titkosítási kulcs frissítése után ki kell jelentkezni és vissza kell jelentkezni az összes jelenleg használt Bitwarden alkalmazásba (például a mobilalkalmazás vagy a böngésző bővítmények). A kijelentkezés és a bejelentkezés elmulasztása (amely letölti az új titkosítási kulcsot) adatvesztést okozhat. Megkíséreljük az automatikusan kijelentkeztetést, azonban ez késhet." diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index d3854f8885d..f77671762d9 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Ukuran berkas maksimal adalah 500 MB." }, - "updateKey": { - "message": "Anda tidak dapat menggunakan fitur ini sampai Anda memperbarui kunci enkripsi Anda." - }, "addedItem": { "message": "Item yang Ditambahkan" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Kunci Diperbarui" }, - "updateKeyTitle": { - "message": "Perbarui Kunci" - }, "updateEncryptionKey": { "message": "Perbarui Kunci Enkripsi" }, - "updateEncryptionKeyShortDesc": { - "message": "Anda saat ini menggunakan skema enkripsi yang sudah ketinggalan zaman." - }, - "updateEncryptionKeyDesc": { - "message": "Kami telah pindah ke kunci enkripsi yang lebih besar yang memberikan keamanan lebih baik dan akses ke fitur yang lebih baru. Memperbarui kunci enkripsi Anda dengan cepat dan mudah. Cukup ketikkan kata sandi utama Anda di bawah ini. Pembaruan ini pada akhirnya akan menjadi wajib." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Setelah memperbarui kunci enkripsi Anda, Anda diminta untuk keluar dan masuk kembali ke semua aplikasi Bitwarden yang saat ini Anda gunakan (seperti aplikasi seluler atau ekstensi browser). Kegagalan untuk keluar dan masuk kembali (yang mengunduh kunci enkripsi baru Anda) dapat menyebabkan kerusakan data. Kami akan mencoba mengeluarkan Anda secara otomatis, namun, hal itu mungkin tertunda." diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index acdfd13370e..038a54850cb 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "La dimensione massima del file è di 500 MB." }, - "updateKey": { - "message": "Non puoi usare questa funzionalità finché non aggiorni la tua chiave di criptografia." - }, "addedItem": { "message": "Elemento aggiunto" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Chiave aggiornata" }, - "updateKeyTitle": { - "message": "Aggiorna chiave" - }, "updateEncryptionKey": { "message": "Aggiorna chiave di criptografia" }, - "updateEncryptionKeyShortDesc": { - "message": "Stai utilizzando uno schema di criptografia obsoleto." - }, - "updateEncryptionKeyDesc": { - "message": "Siamo passati a chiavi di criptografia più grandi che forniscono più sicurezza e accesso alle funzionalità più recenti. Aggiornare la chiave di criptografia è semplice e veloce, basta inserire la password principale qui sotto. Questo aggiornamento diventerà obbligatorio in futuro." + "updateEncryptionSchemeDesc": { + "message": "Abbiamo modificato lo schema di criptografia per fornire una maggiore sicurezza. Aggiorna la tua chiave di criptografia inserendo la tua password principale." }, "updateEncryptionKeyWarning": { "message": "Dopo aver aggiornato la tua chiave di criptografia, devi uscire ed entrare di nuovo in tutte le app Bitwarden che stai usando (come l'app mobile o l'estensione del browser). Se non si esce e rientra (per scaricare la nuova chiave di criptografia) i dati della tua cassaforte potrebbero essere danneggiati. Cercheremo di farti uscire automaticamente, ma potrebbe esserci un ritardo." diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 9d2a222220d..f9faafa6174 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "最大ファイルサイズは500MBです。" }, - "updateKey": { - "message": "暗号キーを更新するまでこの機能は使用できません。" - }, "addedItem": { "message": "追加されたアイテム" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "キーが更新されました" }, - "updateKeyTitle": { - "message": "キーの更新" - }, "updateEncryptionKey": { "message": "暗号化キーを更新します。" }, - "updateEncryptionKeyShortDesc": { - "message": "古い暗号化方式が使用されています。" - }, - "updateEncryptionKeyDesc": { - "message": "より良いセキュリティと新しい機能が利用できる強力な暗号化方式へ移行することにしました。暗号化キーの更新は迅速かつ簡単です。以下にマスターパスワードを入力して下さい。この更新はやがて必須になります。" + "updateEncryptionSchemeDesc": { + "message": "より良いセキュリティを提供するために暗号化方式を変更しました。以下にマスターパスワードを入力して、今すぐ暗号化キーを更新してください。" }, "updateEncryptionKeyWarning": { "message": "暗号化キーの更新後は、モバイルアプリやブラウザ拡張機能など現在利用中のすべてのBitwardenアプリで再ログインが必要となります。再ログインしないと(新しい暗号化キーをダウンロードすると)データが破損する可能性があります。自動的にログアウトを試みますが、遅延することがあります。" diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 4f2b7cc968b..10506212765 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "მაქსიმალური ფაილის ზომაა 500 მბ." }, - "updateKey": { - "message": "თქვენ არ შეგიძლიათ ამ ფუნქციით სარგებლობაბ მანამ არ განაახლებთ დაშიფვრის გასაღებს." - }, "addedItem": { "message": "საგანი დამატებულია" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 910883c83b0..1bad259633a 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "ಗರಿಷ್ಠ ಫೈಲ್ ಗಾತ್ರ 500 ಎಂಬಿ." }, - "updateKey": { - "message": "ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸುವವರೆಗೆ ನೀವು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲಾಗುವುದಿಲ್ಲ." - }, "addedItem": { "message": "ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "ಕೀ ನವೀಕರಿಸಲಾಗಿದೆ" }, - "updateKeyTitle": { - "message": "ಕೀ ನವೀಕರಿಸಿ" - }, "updateEncryptionKey": { "message": "ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸಿ" }, - "updateEncryptionKeyShortDesc": { - "message": "ನೀವು ಪ್ರಸ್ತುತ ಹಳತಾದ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಯೋಜನೆಯನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ." - }, - "updateEncryptionKeyDesc": { - "message": "ಉತ್ತಮ ಸುರಕ್ಷತೆ ಮತ್ತು ಹೊಸ ವೈಶಿಷ್ಟ್ಯಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಒದಗಿಸುವ ದೊಡ್ಡ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಗಳಿಗೆ ನಾವು ಸರಿಸಿದ್ದೇವೆ. ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸುವುದು ತ್ವರಿತ ಮತ್ತು ಸುಲಭ. ನಿಮ್ಮ ಮಾಸ್ಟರ್ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಕೆಳಗೆ ಟೈಪ್ ಮಾಡಿ. ಈ ನವೀಕರಣವು ಅಂತಿಮವಾಗಿ ಕಡ್ಡಾಯವಾಗುತ್ತದೆ." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸಿದ ನಂತರ, ನೀವು ಪ್ರಸ್ತುತ ಬಳಸುತ್ತಿರುವ (ಮೊಬೈಲ್ ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಬ್ರೌಸರ್ ವಿಸ್ತರಣೆಗಳಂತಹ) ಎಲ್ಲಾ ಬಿಟ್‌ವಾರ್ಡೆನ್ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ನೀವು ಲಾಗ್ ಔಟ್ ಮತ್ತು ಬ್ಯಾಕ್ ಇನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ. ಲಾಗ್ and ಟ್ ಮಾಡಲು ಮತ್ತು ಹಿಂತಿರುಗಲು ವಿಫಲವಾದರೆ (ಅದು ನಿಮ್ಮ ಹೊಸ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡುತ್ತದೆ) ಡೇಟಾ ಭ್ರಷ್ಟಾಚಾರಕ್ಕೆ ಕಾರಣವಾಗಬಹುದು. ನಾವು ನಿಮ್ಮನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಲಾಗ್ ಔಟ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸುತ್ತೇವೆ, ಆದಾಗ್ಯೂ, ಇದು ವಿಳಂಬವಾಗಬಹುದು." diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 6d0cbc4bc45..04555754e95 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "최대 파일 크기는 500MB입니다." }, - "updateKey": { - "message": "이 기능을 사용하려면 암호화 키를 업데이트해야 합니다." - }, "addedItem": { "message": "항목 추가함" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "키 업데이트됨" }, - "updateKeyTitle": { - "message": "키 업데이트" - }, "updateEncryptionKey": { "message": "암호화 키 업데이트" }, - "updateEncryptionKeyShortDesc": { - "message": "현재 오래된 암호화 체계를 사용하고 있습니다." - }, - "updateEncryptionKeyDesc": { - "message": "우리는 더 나은 보안 및 새로운 기능에 대한 액세스를 제공하는 더 큰 암호화 키로 이동했습니다. 암호화 키를 업데이트하는 것은 빠르고 쉽습니다. 그저 마스터 비밀번호를 입력하면 됩니다. 이 업데이트는 결국 필수사항이 될 것입니다." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "암호화 키를 업데이트하고난 후 현재 사용 중인 모든 Bitwarden 애플리케이션(예. 모바일 앱 혹은 브라우저 확장 기능)에서 로그아웃 후 다시 로그인해야 합니다. 재로그인하지 않으면 (새 암호화 키를 다운로드받는 경우) 데이터 손실이 발생할 수 있습니다. 자동으로 로그아웃을 시도하지만 지연될 수 있습니다." diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 7acc4e3c2c4..2e64e2cc21e 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Lielākais pieļaujamais datnes izmērs ir 500 MB." }, - "updateKey": { - "message": "Šo iespēju nevar izmantot, kamēr nav atjaunināta šifrēšanas atslēga." - }, "addedItem": { "message": "Vienums pievienots" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Atslēga atjaunināta" }, - "updateKeyTitle": { - "message": "Atjaunināt atslēgu" - }, "updateEncryptionKey": { "message": "Atjaunināt šifrēšanas atslēgu" }, - "updateEncryptionKeyShortDesc": { - "message": "Pašreiz tiek izmantots novecojis šifrēšanas veids." - }, - "updateEncryptionKeyDesc": { - "message": "Tiek izmantotas garākas šifrēšanas atslēgas, kas nodrošina labāku drošību un piekļuvi jaunākām iespējām. Šifrēšanas atslēgas atjaunināšana ir ātra un vienkārša. Zemāk ir tikai jāievada galvenā parole. Ar laiku šis atjauninājums kļūs nepieciešams." + "updateEncryptionSchemeDesc": { + "message": "Mēs esam mainījuši šifrēšanas veidu, lai nodrošinātu labāku drošību. Savu šifrēšanas atslēgu var atjaunināt tagad, zemāk ievadot savu galveno paroli." }, "updateEncryptionKeyWarning": { "message": "Pēc šifrēšanas atslēgas atjaunināšanas ir nepieciešams atteikties un tad pieteikties visās Bitwarden lietotnēs, kas pašreiz tiek izmantotas (piemēram, tālruņa lietotnē vai pārlūku paplašinājumā). Ja tas netiks darīts (tā tiek lejupielādēta jaunā šifrēšanas atslēga), dati var tikt bojāti. Tiks veikts automātisks atteikšanās mēģinājums, tomēr tas var notikt ar aizkavi." diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index c5265c0ee03..7dc9d21bfe2 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "പരമാവധി ഫയൽ വലുപ്പം 500 MB ആണ്." }, - "updateKey": { - "message": "നിങ്ങളുടെ എൻ‌ക്രിപ്ഷൻ കീ അപ്‌ഡേറ്റ് ചെയ്യുന്നതുവരെ നിങ്ങൾക്ക് ഈ സവിശേഷത ഉപയോഗിക്കാൻ കഴിയില്ല." - }, "addedItem": { "message": "ചേർക്കപ്പെട്ട ഇനം" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "കീ അപ്‌ഡേറ്റുചെയ്‌തു" }, - "updateKeyTitle": { - "message": "കീ അപ്‌ഡേറ്റുചെയ്യുക" - }, "updateEncryptionKey": { "message": "എൻക്രിപ്ഷൻ കീ അപ്‌ഡേറ്റുചെയ്യുക" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index a41bd2cd34c..3e85d72ffda 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Den maksimale filstørrelsen er 500 MB." }, - "updateKey": { - "message": "Du kan ikke bruke denne funksjonen før du oppdaterer krypteringsnøkkelen din." - }, "addedItem": { "message": "La til element" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Nøkkelen ble oppdatert" }, - "updateKeyTitle": { - "message": "Oppdater nøkkelen" - }, "updateEncryptionKey": { "message": "Oppdater krypteringsnøkkelen" }, - "updateEncryptionKeyShortDesc": { - "message": "Du bruker for øyeblikket et utdatert krypteringsoppsett." - }, - "updateEncryptionKeyDesc": { - "message": "Vi har byttet over til lengre krypteringsnøkler som sørger for bedre sikkerhet og tilgang til nyere funksjoner. Å oppdatere krypteringsnøkkelen din er raskt og enkelt. Bare skriv inn hovedpassordet ditt nedenfor. Denne oppdateringen vil etter hvert bli påbudt." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Etter å ha oppdatert krypteringsnøkkelen din, er du påkrevd å logge av og på på alle Bitwarden-appene og -programmene som du bruker for øyeblikket (deriblant mobilappen og nettleserutvidelsene). Å ikke logge av og på igjen (noe som vil laste ned din nye krypteringsnøkkel) kan føre til datakorrumpering. Vi vil forsøke å logge deg av automatisk, men det kan kanskje bli forsinket." diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 9813040b78e..b725d8819e5 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 91387dde607..6e658711ffd 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximale bestandsgrootte is 500 MB." }, - "updateKey": { - "message": "Je kunt deze functie pas gebruiken als je je encryptiesleutel bijwerkt." - }, "addedItem": { "message": "Item is toegevoegd" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Sleutel bijgewerkt" }, - "updateKeyTitle": { - "message": "Sleutel bijwerken" - }, "updateEncryptionKey": { "message": "Encryptiesleutel bijwerken" }, - "updateEncryptionKeyShortDesc": { - "message": "Je gebruikt een oud versleutelingsschema." - }, - "updateEncryptionKeyDesc": { - "message": "We zijn overgeschakeld naar langere encryptiesleutels die beter beveiligen en nieuwe functionaliteiten mogelijk maken. Het bijwerken van je encryptiesleutel gaat snel en eenvoudig. Typ gewoon je hoofdwachtwoord hieronder in. Deze wijziging zal uiteindelijk verplicht worden." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Na het bijwerken van je encryptiesleutel moet je je afmelden en weer aanmelden bij alle Bitwarden-applicaties die je gebruikt (zoals de mobiele app of browserextensies). Als je niet opnieuw inlogt (wat je nieuwe encryptiesleutel downloadt), kan dit gegevensbeschadiging tot gevolg hebben. We proberen je automatisch uit te loggen, maar het kan zijn dat dit met enige vertraging gebeurt." diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 845c218f195..67e431446d7 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Den høgste tillatne filstorleiken er 100 MB." }, - "updateKey": { - "message": "Du kan ikkje bruka denne funksjonen før du rettar enkrypteringsnykelen din." - }, "addedItem": { "message": "Oppføringar du har lagt til" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 7b543a5f375..c17cf116e63 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksymalny rozmiar pliku to 500 MB." }, - "updateKey": { - "message": "Nie możesz używać tej funkcji, dopóki nie zaktualizujesz klucza szyfrowania." - }, "addedItem": { "message": "Element został dodany" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Klucz został zaktualizowany" }, - "updateKeyTitle": { - "message": "Zaktualizuj klucz" - }, "updateEncryptionKey": { "message": "Zaktualizuj klucz szyfrowania" }, - "updateEncryptionKeyShortDesc": { - "message": "Używasz przestarzałej metody szyfrowania." - }, - "updateEncryptionKeyDesc": { - "message": "Zdecydowaliśmy się na używanie silniejszych kluczy szyfrowania, które zapewniają lepsze bezpieczeństwo i dostęp do nowych funkcji. Zaktualizowanie klucza szyfrowania jest szybkie i łatwe. Wystarczy wpisać poniżej swoje hasło. Aktualizacja klucza stanie się wkrótce obowiązkowa." + "updateEncryptionSchemeDesc": { + "message": "Zmieniliśmy schemat szyfrowania, aby zapewnić lepsze bezpieczeństwo. Zaktualizuj swój klucz szyfrowania, wprowadzając hasło główne poniżej." }, "updateEncryptionKeyWarning": { "message": "Po zaktualizowaniu klucza szyfrowania, musisz ponownie zalogować się do wszystkich aplikacji Bitwarden, z których obecnie korzystasz (na przykład aplikacje mobilne lub rozszerzenia przeglądarki). Niepowodzenie logowania (podczas którego pobierany jest nowy klucz szyfrowania) może spowodować uszkodzenie danych. Postaramy się wylogować Ciebie automatycznie, jednak może to chwilę potrwać." diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index f69f75eb980..a06f8c9ac88 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "O tamanho máximo do arquivo é de 500 MB." }, - "updateKey": { - "message": "Você não pode usar este recurso, até você atualizar sua chave de criptografia." - }, "addedItem": { "message": "Item adicionado" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Chave Atualizada" }, - "updateKeyTitle": { - "message": "Atualizar Chave" - }, "updateEncryptionKey": { "message": "Atualizar Chave de Criptografia" }, - "updateEncryptionKeyShortDesc": { - "message": "Você está usando atualmente um esquema de criptografia desatualizado." - }, - "updateEncryptionKeyDesc": { - "message": "Passamos para chaves de criptografia maiores, que oferecem melhor segurança e acesso a recursos mais recentes. Atualizar sua chave de criptografia é rápido e fácil. Basta inserir sua senha mestra abaixo. Esta atualização eventualmente se tornará obrigatória." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Depois de atualizar sua chave de criptografia, é necessário encerrar e iniciar a sessão em todos os aplicativos do Bitwarden que você está usando atualmente (como o aplicativo móvel ou as extensões do navegador). Não encerrar e iniciar sessão (que baixa sua nova chave de criptografia) pode resultar em corrupção de dados. Nós tentaremos desconectá-lo automaticamente, mas isso pode demorar um pouco." diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 602ce5a524c..7dd0a06cd7c 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "O tamanho máximo do ficheiro é de 500 MB." }, - "updateKey": { - "message": "Não pode utilizar esta funcionalidade até atualizar a sua chave de encriptação." - }, "addedItem": { "message": "Item adicionado" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Chave atualizada" }, - "updateKeyTitle": { - "message": "Atualizar chave" - }, "updateEncryptionKey": { "message": "Atualizar chave de encriptação" }, - "updateEncryptionKeyShortDesc": { - "message": "Está atualmente a utilizar um esquema de encriptação desatualizado." - }, - "updateEncryptionKeyDesc": { - "message": "Passámos a utilizar chaves de encriptação maiores que proporcionam uma melhor segurança e acesso a novas funcionalidades. A atualização da sua chave de encriptação é rápida e fácil. Basta introduzir a sua palavra-passe mestra abaixo. Esta atualização acabará por se tornar obrigatória." + "updateEncryptionSchemeDesc": { + "message": "Alterámos o esquema de encriptação para proporcionar uma melhor segurança. Atualize a sua chave de encriptação agora, introduzindo a sua palavra-passe mestra abaixo." }, "updateEncryptionKeyWarning": { "message": "Depois de atualizar a sua chave de encriptação, é necessário terminar sessão e voltar a iniciar em todas as aplicações Bitwarden que está a utilizar atualmente (como a aplicação móvel ou as extensões do navegador). A falha em terminar sessão e voltar a iniciar (que descarrega a sua nova chave de encriptação) pode resultar em corrupção de dados. Tentaremos terminar a sua sessão automaticamente, no entanto, pode demorar." diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 73f07464a8b..b0d31350d11 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Mărimea maximă a fișierului este de 500 MB." }, - "updateKey": { - "message": "Veți putea utiliza această caracteristică după ce veți actualiza cheia de criptare." - }, "addedItem": { "message": "Articol adăugat" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Cheie actualizată" }, - "updateKeyTitle": { - "message": "Actualizare cheie" - }, "updateEncryptionKey": { "message": "Actualizare cheie de criptare" }, - "updateEncryptionKeyShortDesc": { - "message": "În prezent utilizați o schemă de criptare învechită." - }, - "updateEncryptionKeyDesc": { - "message": "Ne-am mutat la chei mai mari de criptare care oferă o mai bună securitate și acces la funcții mai noi. Actualizarea cheii de criptare este rapidă și ușoară. Doar tastați parola principală mai jos. Această actualizare va deveni în cele din urmă obligatorie." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "După actualizarea cheii de criptare, trebuie să vă reconectați în toate aplicațiile Bitwarden pe care le utilizați în prezent (cum ar fi aplicația mobilă sau extensiile browserului). Faptul de a nu vă deconecta și reconecta (care descarcă noua cheie de criptare) poate duce la corupția datelor. Vom încerca să vă deconectăm automat, însă ar putea fi întârziat." diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index f63c97f69f6..ada065395ee 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Максимальный размер файла 500 МБ." }, - "updateKey": { - "message": "Вы не можете использовать эту функцию, пока не обновите свой ключ шифрования." - }, "addedItem": { "message": "Элемент добавлен" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ключ обновлен" }, - "updateKeyTitle": { - "message": "Обновить ключ" - }, "updateEncryptionKey": { "message": "Обновить ключ шифрования" }, - "updateEncryptionKeyShortDesc": { - "message": "В настоящее время вы используете устаревшую схему шифрования." - }, - "updateEncryptionKeyDesc": { - "message": "Мы перешли на более сложные ключи шифрования, которые обеспечивают лучшую безопасность и доступ к новым возможностям. Обновление ключа шифрования происходит быстро и легко. Просто введите свой мастер-пароль ниже. Это обновление в конечном итоге станет обязательным." + "updateEncryptionSchemeDesc": { + "message": "Мы изменили схему шифрования, чтобы повысить безопасность. Обновите ключ шифрования, введя ниже мастер-пароль." }, "updateEncryptionKeyWarning": { "message": "После обновления ключа шифрования необходимо выйти из всех приложений Bitwarden, которые вы используете (например, из мобильного приложения или расширения браузера). Если этого не сделать, могут повредиться данные (так как при выходе и последующем входе загружается ваш новый ключ шифрования). Мы попытаемся автоматически осуществить завершение ваших сессий, однако это может произойти с задержкой." diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index cf61fe2da62..e6d1d044c61 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index f666bd0aa1f..afaaebfe192 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximálna veľkosť súboru je 500 MB." }, - "updateKey": { - "message": "Túto funkciu nemožno použiť, kým neaktualizujete svoj šifrovací kľúč." - }, "addedItem": { "message": "Položka pridaná" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Kľúč aktualizovaný" }, - "updateKeyTitle": { - "message": "Aktualizovať kľúč" - }, "updateEncryptionKey": { "message": "Aktualizovať šifrovací kľúč" }, - "updateEncryptionKeyShortDesc": { - "message": "Stále používate starý šifrovací systém." - }, - "updateEncryptionKeyDesc": { - "message": "Prešli sme na väčšie šifrovacie kľúče, ktoré poskytujú lepšiu bezpečnosť a prístup k novým funkciám. Aktualizácia šifrovacieho kľúča je rýchla a jednoduchá. Jednoducho zadajte vaše hlavné heslo nižšie. Táto aktualizácia bude po nejakom čase povinná." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Po aktualizácii šifrovacieho kľúča budete požiadaní o opätovné prihlásenie do všetkých Bitwarden aplikácii ktoré momentálne používate (napríklad mobilné aplikácie, alebo rozšírenia v prehliadači). Ak sa opätovne neprihlásite (touto operáciou sa stiahnu nové šifrovacie kľúče), mohlo by to viesť k poškodeniu uložených dát. Pokúsime sa odhlásiť vás automaticky, ale môže to chvíľu trvať." diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index a0f735bc2d3..bbeb7e485bb 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Največja velikost datoteke je 500 MB." }, - "updateKey": { - "message": "Te funkcije ne morete koristiti, dokler dokler ne posodobite vašega ključa za šifriranje." - }, "addedItem": { "message": "Dodaj element" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ključ posodobljen" }, - "updateKeyTitle": { - "message": "Posodobi ključ" - }, "updateEncryptionKey": { "message": "Posodobi šifrirni ključ" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/sr/messages.json b/apps/web/src/locales/sr/messages.json index 926c1650e7c..aa5b2cdf4dc 100644 --- a/apps/web/src/locales/sr/messages.json +++ b/apps/web/src/locales/sr/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Максимална величина је 500МБ." }, - "updateKey": { - "message": "Не можете да користите ову способност док не промените Ваш кључ за шифровање." - }, "addedItem": { "message": "Ставка додата" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Кључ је ажуриран" }, - "updateKeyTitle": { - "message": "Ажурирате кључ" - }, "updateEncryptionKey": { "message": "Ажурирајте кључ за шифровање" }, - "updateEncryptionKeyShortDesc": { - "message": "Тренутно користите застарелу шему шифровања." - }, - "updateEncryptionKeyDesc": { - "message": "Прешли смо на веће кључеве за шифровање који пружају бољу сигурност и приступ новијим функцијама. Ажурирање кључа за шифровање је брзо и једноставно. Само унесите главну лозинку испод. Ово ажурирање ће временом постати обавезно." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Након ажурирања кључа за шифровање, мораћете да се одјавите и вратите у све Bitwarden апликације које тренутно користите (као што су мобилна апликација или додаци прегледача). Ако се не одјавите и поново пријавите (чиме се преузима ваш нови кључ за шифровање), може доћи до оштећења података. Покушаћемо аутоматски да се одјавимо, али може доћи до одлагања." diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index b0e7500e183..7dbbeb9ea31 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimalna veličina fajla je 500 MB." }, - "updateKey": { - "message": "Ne možete da koristite ovu uslugu dok ne ažurirate Vaš enkripcioni ključ." - }, "addedItem": { "message": "Stavka je dodata" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index 5ca6ab8f146..3193b04dd1a 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximal filstorlek är 500 MB." }, - "updateKey": { - "message": "Du kan inte använda denna funktion förrän du uppdaterar din krypteringsnyckel." - }, "addedItem": { "message": "Nytt objekt skapat" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Nyckeln uppdaterades" }, - "updateKeyTitle": { - "message": "Uppdatera nyckel" - }, "updateEncryptionKey": { "message": "Uppdatera krypteringsnyckel" }, - "updateEncryptionKeyShortDesc": { - "message": "Du använder för närvarande ett föråldrat krypteringssystem." - }, - "updateEncryptionKeyDesc": { - "message": "Vi har övergått till större krypteringsnycklar som ger bättre säkerhet och tillgång till nyare funktioner. Att uppdatera din krypteringsnyckel är snabbt och smidigt. Skriv bara in ditt huvudlösenord nedan. Denna uppdatering kommer så småningom bli obligatorisk." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "Efter att ha uppdaterat din krypteringsnyckel, måste du logga ut och in igen i alla Bitwarden-program som du använder (t.ex. mobilappen och webbläsartillägget). Att inte logga ut och in igen (vilket hämtar din nya krypteringsnyckel) kan resultera i datakorruption. Vi kommer försöka logga ut dig automatiskt, men det kan vara fördröjt." diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 4ed205c1f73..134ebdad64a 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 7948cbeee12..8ae4aeb9a13 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "ขนาดไฟล์สูงสุด คือ 500 MB" }, - "updateKey": { - "message": "คุณไม่สามารถใช้คุณลักษณะนี้ได้จนกว่าคุณจะปรับปรุงคีย์การเข้ารหัสลับของคุณ" - }, "addedItem": { "message": "Item added" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index 6c4da856faa..7ab26bdd951 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Maksimum dosya boyutu 500 MB'dir." }, - "updateKey": { - "message": "Şifreleme anahtarınızı güncellemeden bu özelliği kullanamazsınız." - }, "addedItem": { "message": "Kayıt eklendi" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Anahtar güncellendi" }, - "updateKeyTitle": { - "message": "Anahtarı güncelle" - }, "updateEncryptionKey": { "message": "Şifreleme anahtarını güncelle" }, - "updateEncryptionKeyShortDesc": { - "message": "Şu anda eski bir şifreleme modeli kullanıyorsunuz." - }, - "updateEncryptionKeyDesc": { - "message": "Daha yüksek güvenlik ve daha yeni özelliklere erişim sağlayan daha büyük şifreleme anahtarlarına geçtik. Şifreleme anahtarınızı kolayca güncelleyebilirsiniz. Ana parolanızı aşağıya yazmanız yeterli. Bu güncelleme bir süre sonra zorunlu hale gelecektir." + "updateEncryptionSchemeDesc": { + "message": "Güvenliği daha da artırmak için şifreleme şemamızı değiştirdik. Aşağıya ana parolanızı yazarak şifreleme anahtarınızı güncelleyebilirsiniz." }, "updateEncryptionKeyWarning": { "message": "Şifreleme anahtarınızı güncelledikten sonra, şu anda kullanmakta olduğunuz tüm Bitwarden uygulamalarında (mobil uygulama veya tarayıcı uzantıları gibi) oturumunuzu kapatıp tekrar açmanız gerekir. Yeni şifreleme anahtarınızı indirme için oturumu kapatıp tekrar açmamamız verilerin bozulmasına neden olabilir. Oturumunuzu otomatik olarak kapatmaya çalışacağız, ancak bu gecikebilir." diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 32a8c3782bb..18425476ab5 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Максимальний розмір файлу 500 Мб." }, - "updateKey": { - "message": "Ви не можете використовувати цю функцію доки не оновите свій ключ шифрування." - }, "addedItem": { "message": "Запис додано" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Ключ оновлено" }, - "updateKeyTitle": { - "message": "Оновити ключ" - }, "updateEncryptionKey": { "message": "Оновити ключ шифрування" }, - "updateEncryptionKeyShortDesc": { - "message": "Зараз ви використовуєте застарілу схему шифрування." - }, - "updateEncryptionKeyDesc": { - "message": "Ми перейшли на більші ключі шифрування, що гарантує кращу безпеку і доступ до новіших функцій. Оновлення вашого ключа шифрування є швидким і простим процесом. Просто введіть свій головний пароль внизу. Це оновлення невдовзі стане обов'язковою вимогою." + "updateEncryptionSchemeDesc": { + "message": "Ми змінили схему шифрування для кращої безпеки. Введіть головний пароль нижче, щоб оновити свій ключ шифрування." }, "updateEncryptionKeyWarning": { "message": "Після оновлення вашого ключа шифрування вам необхідно вийти з системи і потім виконати повторний вхід у всіх програмах Bitwarden, які ви використовуєте. Збій при виході та повторному вході може призвести до пошкодження даних. Ми спробуємо завершити ваші сеанси автоматично, однак, цей процес може відбутися із затримкою." diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index f7aa1449d12..deb89b9bf2d 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "Kích thước tối đa của tệp tin là 500 MB." }, - "updateKey": { - "message": "Bạn không thể sử dụng tính năng này cho đến khi bạn cập nhật khoá mã hóa." - }, "addedItem": { "message": "Đã thêm mục" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "Key updated" }, - "updateKeyTitle": { - "message": "Update key" - }, "updateEncryptionKey": { "message": "Update encryption key" }, - "updateEncryptionKeyShortDesc": { - "message": "You are currently using an outdated encryption scheme." - }, - "updateEncryptionKeyDesc": { - "message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory." + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed." diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index 008ad22a980..d610b5bc3d7 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "文件最大为 500 MB。" }, - "updateKey": { - "message": "在您更新加密密钥前,您不能使用此功能。" - }, "addedItem": { "message": "项目已添加" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "密钥已更新" }, - "updateKeyTitle": { - "message": "更新密钥" - }, "updateEncryptionKey": { "message": "更新加密密钥" }, - "updateEncryptionKeyShortDesc": { - "message": "您目前使用的是过时的加密方案。" - }, - "updateEncryptionKeyDesc": { - "message": "我们已经采用更强大的加密密钥,提供了更强的安全性和更新的功能。更新您的加密密钥非常简单快捷,只需要输入主密码。此次更新最终将强制执行。" + "updateEncryptionSchemeDesc": { + "message": "我们更改了加密方案以提供更好的安全性。请在下方输入您的主密码以立即更新您的加密密钥。" }, "updateEncryptionKeyWarning": { "message": "更新加密密钥后,您需要注销所有正在使用的 Bitwarden 应用(比如手机版应用或者浏览器扩展)后重新登录。注销或者重新登录(这将下载新的加密密钥)失败可能会导致数据损坏。我们会尝试自动为您注销,但是,可能会有所延迟。" diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index dde9ea4e746..39628f0f551 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -508,9 +508,6 @@ "maxFileSize": { "message": "檔案最大為 500MB。" }, - "updateKey": { - "message": "更新加密金鑰前不能使用此功能。" - }, "addedItem": { "message": "項目已新增" }, @@ -3476,17 +3473,11 @@ "keyUpdated": { "message": "金鑰已更新" }, - "updateKeyTitle": { - "message": "更新金鑰" - }, "updateEncryptionKey": { "message": "更新加密金鑰" }, - "updateEncryptionKeyShortDesc": { - "message": "您目前使用的是過時的加密方式。" - }, - "updateEncryptionKeyDesc": { - "message": "我們已經採用更強大的加密金鑰,此金鑰提供了更強的安全性及更新功能。更新您的加密金鑰非常簡單快速,僅需輸入您的主密碼即可。此次更新最終將強制執行。" + "updateEncryptionSchemeDesc": { + "message": "We've changed the encryption scheme to provide better security. Update your encryption key now by entering your master password below." }, "updateEncryptionKeyWarning": { "message": "更新加密金鑰後,您需要登出並重新登入目前使用的所有 Bitwarden 應用程式(如行動應用程式或瀏覽器擴充套件)。登出和重新登入(這會下載新的加密金鑰)失敗可能會導致資料損毀。我們將嘗試自動登出,但可能會有所延遲。" From 165e622cdcae0234ad4e36afe049c872963142ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 07:57:58 +0000 Subject: [PATCH 04/16] Autosync the updated translations (#6368) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 4 +- apps/browser/src/_locales/az/messages.json | 4 +- apps/browser/src/_locales/be/messages.json | 4 +- apps/browser/src/_locales/bg/messages.json | 4 +- apps/browser/src/_locales/bn/messages.json | 4 +- apps/browser/src/_locales/bs/messages.json | 4 +- apps/browser/src/_locales/ca/messages.json | 4 +- apps/browser/src/_locales/cs/messages.json | 4 +- apps/browser/src/_locales/cy/messages.json | 4 +- apps/browser/src/_locales/da/messages.json | 4 +- apps/browser/src/_locales/de/messages.json | 4 +- apps/browser/src/_locales/el/messages.json | 4 +- apps/browser/src/_locales/en_GB/messages.json | 4 +- apps/browser/src/_locales/en_IN/messages.json | 4 +- apps/browser/src/_locales/es/messages.json | 4 +- apps/browser/src/_locales/et/messages.json | 4 +- apps/browser/src/_locales/eu/messages.json | 4 +- apps/browser/src/_locales/fa/messages.json | 4 +- apps/browser/src/_locales/fi/messages.json | 4 +- apps/browser/src/_locales/fil/messages.json | 4 +- apps/browser/src/_locales/fr/messages.json | 64 +++++++++---------- apps/browser/src/_locales/gl/messages.json | 4 +- apps/browser/src/_locales/he/messages.json | 4 +- apps/browser/src/_locales/hi/messages.json | 4 +- apps/browser/src/_locales/hr/messages.json | 4 +- apps/browser/src/_locales/hu/messages.json | 4 +- apps/browser/src/_locales/id/messages.json | 4 +- apps/browser/src/_locales/it/messages.json | 4 +- apps/browser/src/_locales/ja/messages.json | 4 +- apps/browser/src/_locales/ka/messages.json | 4 +- apps/browser/src/_locales/km/messages.json | 4 +- apps/browser/src/_locales/kn/messages.json | 4 +- apps/browser/src/_locales/ko/messages.json | 4 +- apps/browser/src/_locales/lt/messages.json | 4 +- apps/browser/src/_locales/lv/messages.json | 4 +- apps/browser/src/_locales/ml/messages.json | 4 +- apps/browser/src/_locales/mr/messages.json | 4 +- apps/browser/src/_locales/my/messages.json | 4 +- apps/browser/src/_locales/nb/messages.json | 4 +- apps/browser/src/_locales/ne/messages.json | 4 +- apps/browser/src/_locales/nl/messages.json | 4 +- apps/browser/src/_locales/nn/messages.json | 4 +- apps/browser/src/_locales/or/messages.json | 4 +- apps/browser/src/_locales/pl/messages.json | 4 +- apps/browser/src/_locales/pt_BR/messages.json | 4 +- apps/browser/src/_locales/pt_PT/messages.json | 4 +- apps/browser/src/_locales/ro/messages.json | 4 +- apps/browser/src/_locales/ru/messages.json | 4 +- apps/browser/src/_locales/si/messages.json | 4 +- apps/browser/src/_locales/sk/messages.json | 4 +- apps/browser/src/_locales/sl/messages.json | 4 +- apps/browser/src/_locales/sr/messages.json | 4 +- apps/browser/src/_locales/sv/messages.json | 4 +- apps/browser/src/_locales/te/messages.json | 4 +- apps/browser/src/_locales/th/messages.json | 4 +- apps/browser/src/_locales/tr/messages.json | 4 +- apps/browser/src/_locales/uk/messages.json | 4 +- apps/browser/src/_locales/vi/messages.json | 4 +- apps/browser/src/_locales/zh_CN/messages.json | 4 +- apps/browser/src/_locales/zh_TW/messages.json | 4 +- 60 files changed, 150 insertions(+), 150 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index d48c573d5b4..be4eaca46f7 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "الميزة غير متوفرة" }, - "updateKey": { - "message": "لا يمكنك استخدام هذه المِيزة حتى تحديث مفتاح التشفير الخاص بك." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "العضوية المميزة" diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index b83fb91390e..774e3fd64cd 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Özəllik əlçatmazdır" }, - "updateKey": { - "message": "Şifrələmə açarınızı güncəlləyənə qədər bu özəlliyi istifadə edə bilməzsiniz." + "encryptionKeyMigrationRequired": { + "message": "Şifrələmə açarının daşınması tələb olunur. Şifrələmə açarınızı güncəlləmək üçün zəhmət olmasa veb anbar üzərindən giriş edin." }, "premiumMembership": { "message": "Premium üzvlük" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index ba72d949b90..2fc14ecb1fc 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Функцыя недаступна" }, - "updateKey": { - "message": "Вы не зможаце выкарыстоўваць гэту функцыю, пакуль не абнавіце свой ключ шыфравання." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Прэміяльны статус" diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index ceab700e46a..0bd4ba3b7f9 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Функцията е недостъпна" }, - "updateKey": { - "message": "Трябва да обновите шифриращия си ключ, за да използвате тази възможност." + "encryptionKeyMigrationRequired": { + "message": "Необходима е промяна на шифриращия ключ. Впишете се в трезора си по уеб, за да обновите своя шифриращ ключ." }, "premiumMembership": { "message": "Платен абонамент" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index db189fbcf0b..f57c4687ea3 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "বৈশিষ্ট্য অনুপলব্ধ" }, - "updateKey": { - "message": "আপনি আপনার এনক্রিপশন কী হালনাগাদ না করা পর্যন্ত এই বৈশিষ্ট্যটি ব্যবহার করতে পারবেন না।" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "প্রিমিয়াম সদস্য" diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index e10d4d29a8c..1c930eeafc7 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 6bd4d028b41..41f022a4362 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Característica no disponible" }, - "updateKey": { - "message": "No podeu utilitzar aquesta característica fins que actualitzeu la vostra clau de xifratge." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Subscripció Premium" diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 10f97ce9311..27ddddb5124 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funkce je nedostupná" }, - "updateKey": { - "message": "Dokud neaktualizujete svůj šifrovací klíč, nemůžete tuto funkci použít." + "encryptionKeyMigrationRequired": { + "message": "Vyžaduje se migrace šifrovacího klíče. Pro aktualizaci šifrovacího klíče se přihlaste přes webový trezor." }, "premiumMembership": { "message": "Prémiové členství" diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 2ce58e170a2..c7817b42bce 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Aelodaeth uwch" diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index ab815e6f029..cce307701b2 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funktion ikke tilgængelig" }, - "updateKey": { - "message": "Du kan ikke bruge denne funktion, før du opdaterer din krypteringsnøgle." + "encryptionKeyMigrationRequired": { + "message": "Krypteringsnøglemigrering nødvendig. Log ind gennem web-boksen for at opdatere krypteringsnøglen." }, "premiumMembership": { "message": "Premium-medlemskab" diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 62db8c33582..1b3eb19ee8e 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funktion nicht verfügbar" }, - "updateKey": { - "message": "Du kannst diese Funktion nicht nutzen, solange du deinen Verschlüsselungsschlüssel nicht aktualisiert hast." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium-Mitgliedschaft" diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index a292bf175c3..552f0b14850 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Μη διαθέσιμο χαρακτηριστικό" }, - "updateKey": { - "message": "Δεν μπορείτε να χρησιμοποιήσετε αυτήν τη λειτουργία μέχρι να ενημερώσετε το κλειδί κρυπτογράφησης." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Συνδρομή Premium" diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 73bbaedf9ae..6307472164d 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 2da8f08deb2..8bdedacc45c 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index b428e097edd..5890a499518 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Característica no disponible" }, - "updateKey": { - "message": "No puedes usar esta característica hasta que actualices tu clave de cifrado." + "encryptionKeyMigrationRequired": { + "message": "Se requiere migración de la clave de cifrado. Por favor, inicie sesión a través de la caja fuerte para actualizar su clave de cifrado." }, "premiumMembership": { "message": "Membresía Premium" diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index a2f90818aba..c3d12a9b120 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funktsioon pole saadaval" }, - "updateKey": { - "message": "Seda funktsiooni ei saa enne krüpteerimise võtme uuendamist kasutada." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium versioon" diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index ed4caa5d495..a57b243a923 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Ezaugarria ez dago erabilgarri" }, - "updateKey": { - "message": "Ezin duzu ezaugarri hau erabili zifratze-gakoa eguneratu arte." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium bazkidea" diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 8570b9c1c33..a6c4f2652c0 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "ویژگی موجود نیست" }, - "updateKey": { - "message": "تا زمانی که کد رمزنگاری را به‌روز نکنید نمی‌توانید از این قابلیت استفاده کنید." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "عضویت پرمیوم" diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 8ab4261cc03..81bf6c64322 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Ominaisuus ei ole käytettävissä" }, - "updateKey": { - "message": "Et voi käyttää tätä toimintoa ennen kuin päivität salausavaimesi." + "encryptionKeyMigrationRequired": { + "message": "Salausavaimen siirto vaaditaan. Päivitä salausavaimesi kirjautumalla verkkoholviin." }, "premiumMembership": { "message": "Premium-jäsenyys" diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index cbdd895d03b..df4726ac9cb 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Hindi magagamit ang tampok" }, - "updateKey": { - "message": "Hindi mo maari gamitin ang tampok na ito hanggang hindi mo iupdate ang iyong encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Pagiging miyembro ng premium" diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 19a9adc5de4..d4b04dfa24f 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Fonctionnalité non disponible" }, - "updateKey": { - "message": "Vous ne pouvez pas utiliser cette fonctionnalité avant de mettre à jour votre clé de chiffrement." + "encryptionKeyMigrationRequired": { + "message": "Migration de la clé de chiffrement nécessaire. Veuillez vous connecter sur le coffre web pour mettre à jour votre clé de chiffrement." }, "premiumMembership": { "message": "Adhésion Premium" @@ -1606,10 +1606,10 @@ "message": "Le déverrouillage biométrique dans le navigateur n’est pas pris en charge sur cet appareil" }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "Le déverrouillage biométique a échoué\n" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "Impossible d'utiliser le déverrouillage biométrique, utilisez votre mot de passe principal ou déconnectez-vous. Si le problème persiste, veuillez contacter le support Bitwarden." }, "nativeMessaginPermissionErrorTitle": { "message": "Permission non accordée" @@ -1992,7 +1992,7 @@ "message": "Export du coffre personnel" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Seuls les éléments individuels du coffre associés à $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus. Seules les informations sur les éléments du coffre seront exportées et n'incluront pas les pièces jointes associées.", "placeholders": { "email": { "content": "$1", @@ -2153,7 +2153,7 @@ "message": "Une notification a été envoyée à votre appareil." }, "loginInitiated": { - "message": "Login initiated" + "message": "Connexion initiée" }, "exposedMasterPassword": { "message": "Mot de passe principal exposé" @@ -2240,28 +2240,28 @@ "message": "S'ouvre dans une nouvelle fenêtre" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "L'approbation de l'appareil est requise. Sélectionnez une option d'approbation ci-dessous :" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Se souvenir de cet appareil" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Décocher si vous utilisez un appareil public" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Approuver sur votre autre appareil" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Demander l'approbation de l'administrateur" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Approuver avec le mot de passe principal" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Identifiant SSO de l'organisation requis." }, "eu": { - "message": "EU", + "message": "UE", "description": "European Union" }, "usDomain": { @@ -2280,28 +2280,28 @@ "message": "Affichage" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Compte créé avec succès !" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Approbation de l'administrateur demandée" }, "adminApprovalRequestSentToAdmins": { "message": "Demande transmise à votre administrateur." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Vous serez notifié une fois approuvé." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Problème pour vous connecter ?" }, "loginApproved": { - "message": "Login approved" + "message": "Connexion approuvée" }, "userEmailMissing": { - "message": "User email missing" + "message": "Courriel de l'utilisateur manquant" }, "deviceTrusted": { - "message": "Device trusted" + "message": "Appareil de confiance" }, "inputRequired": { "message": "Saisie requise." @@ -2310,7 +2310,7 @@ "message": "requis" }, "search": { - "message": "Search" + "message": "Rechercher" }, "inputMinLength": { "message": "La saisie doit comporter au moins $COUNT$ caractères.", @@ -2340,7 +2340,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "La valeur d'entrée doit être au moins de $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2349,7 +2349,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "La valeur d'entrée ne doit pas excéder $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2368,7 +2368,7 @@ "message": "La saisie n'est pas une adresse e-mail." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ champ(s) ci-dessus nécessitent votre attention.", "placeholders": { "count": { "content": "$1", @@ -2377,22 +2377,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Sélectionner --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Saisir pour filtrer --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Récupération des options..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Aucun élément trouvé" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Effacer tout" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ de plus", "placeholders": { "quantity": { "content": "$1", @@ -2401,7 +2401,7 @@ } }, "submenu": { - "message": "Submenu" + "message": "Sous-menu" }, "toggleCollapse": { "message": "Toggle collapse", diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index c3cd77bc639..48d88f82d40 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "יכולת זו לא זמינה" }, - "updateKey": { - "message": "לא ניתן להשתמש ביכולת זו עד שתעדכן את מפתח ההצפנה שלך." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "חשבון פרימיום" diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 5fafd94ccf8..2619e217370 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature Unavailable" }, - "updateKey": { - "message": "जब तक आप अपनी एन्क्रिप्शन कुंजी को अपडेट नहीं करते, तब तक आप इस सुविधा का उपयोग नहीं कर सकते हैं।" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium Membership" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 1e81fd2d757..9113a3b2687 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Značajka nije dostupna" }, - "updateKey": { - "message": "Ne možeš koristiti ovu značajku prije nego ažuriraš ključ za šifriranje." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium članstvo" diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index b18a03ee769..3b1c44dcfbb 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Ez a funkció nem érhető el." }, - "updateKey": { - "message": "Ez a funkció nem használható, amíg nem frissíted a titkosítási kulcsod." + "encryptionKeyMigrationRequired": { + "message": "Titkosítási kulcs migráció szükséges. Jelentkezzünk be a webes széfen keresztül a titkosítási kulcs frissítéséhez." }, "premiumMembership": { "message": "Prémium tagság" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index e51078ece28..3891b1ddc37 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Fitur Tidak Tersedia" }, - "updateKey": { - "message": "Anda tidak dapat menggunakan fitur ini sampai Anda memperbarui kunci enkripsi Anda." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Keanggotaan Premium" diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index c68e7aea3d3..9af1f04b8e2 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funzionalità non disponibile" }, - "updateKey": { - "message": "Non puoi usare questa funzionalità finché non aggiorni la tua chiave di criptografia." + "encryptionKeyMigrationRequired": { + "message": "Migrazione della chiave di criptografia obbligatoria. Accedi tramite la cassaforte web per aggiornare la tua chiave di criptografia." }, "premiumMembership": { "message": "Abbonamento Premium" diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 746401d241c..e02207a0051 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "サービスが利用できません" }, - "updateKey": { - "message": "暗号キーを更新するまでこの機能は使用できません。" + "encryptionKeyMigrationRequired": { + "message": "暗号化キーの移行が必要です。暗号化キーを更新するには、ウェブ保管庫からログインしてください。" }, "premiumMembership": { "message": "プレミアム会員" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index ea38b1f9a47..c6503b69df3 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 7762aa74db5..c4f5aee4955 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "ವೈಶಿಷ್ಟ್ಯ ಲಭ್ಯವಿಲ್ಲ" }, - "updateKey": { - "message": "ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸುವವರೆಗೆ ನೀವು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲಾಗುವುದಿಲ್ಲ." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "ಪ್ರೀಮಿಯಂ ಸದಸ್ಯತ್ವ" diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 995c5a932dd..795b47eca83 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "기능 사용할 수 없음" }, - "updateKey": { - "message": "이 기능을 사용하려면 암호화 키를 업데이트해야 합니다." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "프리미엄 멤버십" diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index f66d8f3f642..8daba606fa6 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funkcija neprieinama" }, - "updateKey": { - "message": "Negali naudotis šia funkcija, kol neatnaujinsi šifravimo raktą." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium narystė" diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index b49cea5167e..f094022695c 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Iespēja nav pieejama" }, - "updateKey": { - "message": "Jūs nevarat izmantot šo funkciju līdz jūs atjaunojat savu šifrēšanas atslēgu." + "encryptionKeyMigrationRequired": { + "message": "Nepieciešama šifrēšanas atslēgas nomaiņa. Lūgums pieteikties tīmekļa glabātavā, lai atjauninātu savu šifrēšanas atslēgu." }, "premiumMembership": { "message": "Premium dalība" diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 8a377bb32a6..94e71f2d8f1 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "സവിശേഷത ലഭ്യമല്ല" }, - "updateKey": { - "message": "നിങ്ങളുടെ എൻ‌ക്രിപ്ഷൻ കീ അപ്‌ഡേറ്റ് ചെയ്യുന്നതുവരെ നിങ്ങൾക്ക് ഈ സവിശേഷത ഉപയോഗിക്കാൻ കഴിയില്ല." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "പ്രീമിയം അംഗത്വം" diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 9c83fe4e03c..00a2e755376 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 98715f38327..a69e43ddbd6 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Egenskapen er utilgjengelig" }, - "updateKey": { - "message": "Du kan ikke bruke denne funksjonen før du oppdaterer krypteringsnøkkelen din." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium-medlemskap" diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 623b16daf7f..d6dda3290e7 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Functionaliteit niet beschikbaar" }, - "updateKey": { - "message": "Je kunt deze functie pas gebruiken als je je encryptiesleutel bijwerkt." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium-abonnement" diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index dcc01d2284d..296c76ddbd1 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funkcja jest niedostępna" }, - "updateKey": { - "message": "Nie możesz używać tej funkcji, dopóki nie zaktualizujesz klucza szyfrowania." + "encryptionKeyMigrationRequired": { + "message": "Wymagana jest migracja klucza szyfrowania. Zaloguj się przez sejf internetowy, aby zaktualizować klucz szyfrowania." }, "premiumMembership": { "message": "Konto Premium" diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index f3a760a426f..89ba426c960 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funcionalidade Indisponível" }, - "updateKey": { - "message": "Você não pode usar este recurso, até você atualizar sua chave de criptografia." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Assinatura Premium" diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 5c80b8dfa12..496350bb995 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funcionalidade indisponível" }, - "updateKey": { - "message": "Não pode utilizar esta funcionalidade até atualizar a sua chave de encriptação." + "encryptionKeyMigrationRequired": { + "message": "É necessária a migração da chave de encriptação. Inicie sessão através do cofre Web para atualizar a sua chave de encriptação." }, "premiumMembership": { "message": "Subscrição Premium" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 1ee069177ee..cf8acef767b 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funcție indisponibilă" }, - "updateKey": { - "message": "Nu puteți utiliza această caracteristică înainte de a actualiza cheia de criptare." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Abonament Premium" diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 1b49f85e7a2..5dfbe64abc8 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Функция недоступна" }, - "updateKey": { - "message": "Вы не можете использовать эту функцию, пока не обновите свой ключ шифрования." + "encryptionKeyMigrationRequired": { + "message": "Требуется миграция ключа шифрования. Чтобы обновить ключ шифрования, войдите через веб-хранилище." }, "premiumMembership": { "message": "Премиум" diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index d42711812c8..2c9a44b2afe 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "විශේෂාංගය ලබාගත නොහැක" }, - "updateKey": { - "message": "ඔබ ඔබේ සංකේතාංකන යතුර යාවත්කාලීන කරන තුරු ඔබට මෙම අංගය භාවිතා කළ නොහැක." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "වාරික සාමාජිකත්වය" diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 267ec80df9e..405402ccac7 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funkcia nie je k dispozícii" }, - "updateKey": { - "message": "Túto funkciu nemožno použiť, pokým neaktualizujete svoj šifrovací kľúč." + "encryptionKeyMigrationRequired": { + "message": "Vyžaduje sa migrácia šifrovacieho kľúča. Na aktualizáciu šifrovacieho kľúča sa prihláste cez webový trezor." }, "premiumMembership": { "message": "Prémiové členstvo" diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 0e05815294d..ee581226b0c 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funkcija ni na voljo." }, - "updateKey": { - "message": "To funkcijo lahko uporabite šele, ko posodobite svoj šifrirni ključ." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium članstvo" diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index be561aad7c3..a75cd39ebca 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Функција је недоступна" }, - "updateKey": { - "message": "Не можете да користите ову способност док не промените Ваш кључ за шифровање." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Премијум чланство" diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index fb44f17c835..7a4816dfa96 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Funktion ej tillgänglig" }, - "updateKey": { - "message": "Du kan inte använda denna funktion förrän du uppdaterar din krypteringsnyckel." + "encryptionKeyMigrationRequired": { + "message": "Migrering av krypteringsnyckel krävs. Logga in på webbvalvet för att uppdatera din krypteringsnyckel." }, "premiumMembership": { "message": "Premium-medlemskap" diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 6e95df17b01..bf1eedd8261 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature unavailable" }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium membership" diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index d09c59940d1..20e234e4f8f 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Feature Unavailable" }, - "updateKey": { - "message": "คุณไม่สามารถใช้คุณลักษณะนี้ได้จนกว่าคุณจะปรับปรุงคีย์การเข้ารหัสลับของคุณ" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Premium Membership" diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 71080dd999c..f40c94f1222 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Özellik kullanılamıyor" }, - "updateKey": { - "message": "Şifreleme anahtarınızı güncellemeden bu özelliği kullanamazsınız." + "encryptionKeyMigrationRequired": { + "message": "Şifreleme anahtarınızın güncellenmesi gerekiyor. Şifreleme anahtarınızı güncellemek için lütfen web kasasına giriş yapın." }, "premiumMembership": { "message": "Premium üyelik" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 2934e17e328..88a9dd902d2 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Функція недоступна" }, - "updateKey": { - "message": "Ви не можете використовувати цю функцію доки не оновите свій ключ шифрування." + "encryptionKeyMigrationRequired": { + "message": "Потрібно перенести ключ шифрування. Увійдіть у вебсховище та оновіть свій ключ шифрування." }, "premiumMembership": { "message": "Преміум статус" diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 62f63995c7b..7f256311f16 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "Tính năng không có sẵn" }, - "updateKey": { - "message": "Bạn không thể sử dụng tính năng này cho đến khi bạn cập nhật khoá mã hóa." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "Thành viên Cao Cấp" diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 64df3ec8778..04c9d9484c6 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "功能不可用" }, - "updateKey": { - "message": "在您更新加密密钥前,您不能使用此功能。" + "encryptionKeyMigrationRequired": { + "message": "需要迁移加密密钥。请登录网页版密码库来更新您的加密密钥。" }, "premiumMembership": { "message": "高级会员" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index c436fd63a29..3612d93edb3 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -771,8 +771,8 @@ "featureUnavailable": { "message": "功能不可用" }, - "updateKey": { - "message": "更新加密金鑰前不能使用此功能。" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "premiumMembership": { "message": "進階會員" From 83ee81ee5c485278fa72fd4049bcb823f550174f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:06:58 +0000 Subject: [PATCH 05/16] Autosync the updated translations (#6367) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +- apps/desktop/src/locales/ar/messages.json | 86 ++++++++++---------- apps/desktop/src/locales/az/messages.json | 4 +- apps/desktop/src/locales/be/messages.json | 4 +- apps/desktop/src/locales/bg/messages.json | 4 +- apps/desktop/src/locales/bn/messages.json | 4 +- apps/desktop/src/locales/bs/messages.json | 4 +- apps/desktop/src/locales/ca/messages.json | 4 +- apps/desktop/src/locales/cs/messages.json | 4 +- apps/desktop/src/locales/cy/messages.json | 4 +- apps/desktop/src/locales/da/messages.json | 4 +- apps/desktop/src/locales/de/messages.json | 4 +- apps/desktop/src/locales/el/messages.json | 4 +- apps/desktop/src/locales/en_GB/messages.json | 4 +- apps/desktop/src/locales/en_IN/messages.json | 4 +- apps/desktop/src/locales/eo/messages.json | 4 +- apps/desktop/src/locales/es/messages.json | 4 +- apps/desktop/src/locales/et/messages.json | 4 +- apps/desktop/src/locales/eu/messages.json | 4 +- apps/desktop/src/locales/fa/messages.json | 4 +- apps/desktop/src/locales/fi/messages.json | 4 +- apps/desktop/src/locales/fil/messages.json | 4 +- apps/desktop/src/locales/fr/messages.json | 6 +- apps/desktop/src/locales/gl/messages.json | 4 +- apps/desktop/src/locales/he/messages.json | 4 +- apps/desktop/src/locales/hi/messages.json | 4 +- apps/desktop/src/locales/hr/messages.json | 4 +- apps/desktop/src/locales/hu/messages.json | 4 +- apps/desktop/src/locales/id/messages.json | 4 +- apps/desktop/src/locales/it/messages.json | 4 +- apps/desktop/src/locales/ja/messages.json | 4 +- apps/desktop/src/locales/ka/messages.json | 4 +- apps/desktop/src/locales/km/messages.json | 4 +- apps/desktop/src/locales/kn/messages.json | 4 +- apps/desktop/src/locales/ko/messages.json | 4 +- apps/desktop/src/locales/lt/messages.json | 4 +- apps/desktop/src/locales/lv/messages.json | 4 +- apps/desktop/src/locales/me/messages.json | 4 +- apps/desktop/src/locales/ml/messages.json | 4 +- apps/desktop/src/locales/mr/messages.json | 4 +- apps/desktop/src/locales/my/messages.json | 4 +- apps/desktop/src/locales/nb/messages.json | 4 +- apps/desktop/src/locales/ne/messages.json | 4 +- apps/desktop/src/locales/nl/messages.json | 4 +- apps/desktop/src/locales/nn/messages.json | 4 +- apps/desktop/src/locales/or/messages.json | 4 +- apps/desktop/src/locales/pl/messages.json | 4 +- apps/desktop/src/locales/pt_BR/messages.json | 4 +- apps/desktop/src/locales/pt_PT/messages.json | 4 +- apps/desktop/src/locales/ro/messages.json | 4 +- apps/desktop/src/locales/ru/messages.json | 4 +- apps/desktop/src/locales/si/messages.json | 4 +- apps/desktop/src/locales/sk/messages.json | 4 +- apps/desktop/src/locales/sl/messages.json | 4 +- apps/desktop/src/locales/sr/messages.json | 4 +- apps/desktop/src/locales/sv/messages.json | 4 +- apps/desktop/src/locales/te/messages.json | 4 +- apps/desktop/src/locales/th/messages.json | 4 +- apps/desktop/src/locales/tr/messages.json | 4 +- apps/desktop/src/locales/uk/messages.json | 4 +- apps/desktop/src/locales/vi/messages.json | 4 +- apps/desktop/src/locales/zh_CN/messages.json | 4 +- apps/desktop/src/locales/zh_TW/messages.json | 4 +- 63 files changed, 168 insertions(+), 168 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index dd0843dc188..e6548b533f7 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimumlêergrootte is 500 MB." }, - "updateKey": { - "message": "U kan nie hierdie funksie gebruik tot u u enkripsiesleutel bygewerk het nie." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Gewysigde vouer" diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 738634afb93..bd767139f71 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "الحجم الأقصى للملف هو 500 ميجابايت." }, - "updateKey": { - "message": "لا يمكنك استخدام هذه الميزة حتى تقوم بتحديث مفتاح التشفير الخاص بك." + "encryptionKeyMigrationRequired": { + "message": "مطلوب ترحيل مفتاح التشفير. الرجاء تسجيل الدخول بواسطة مخزن الويب لتحديث مفتاح التشفير الخاص بك." }, "editedFolder": { "message": "تم تعديل المجلد" @@ -1078,7 +1078,7 @@ "message": "1 جيغابايت وحدة تخزين مشفرة لمرفقات الملفات." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "خيارات تسجيل الدخول بخطوتين المملوكة مثل YubiKey و Duo." }, "premiumSignUpReports": { "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزنتك." @@ -1493,7 +1493,7 @@ "message": "تسجيل الخروج من الخزنة يتطلب إعادة المصادقة للوصول إليها مرة أخرى." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "أعدنّ طريقة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك." }, "lock": { "message": "قفل", @@ -1985,7 +1985,7 @@ "message": "تصدير الخزنة الشخصية" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "سيتم تصدير عناصر المخزن الفردية المرتبطة بـ $EMAIL$ فقط. لن يتم إدراج عناصر مخزن المؤسسة. سيتم تصدير معلومات المنتج فقط ولن تشمل المرفقات المرتبطة بها.", "placeholders": { "email": { "content": "$1", @@ -2110,7 +2110,7 @@ "message": "تسجيل الدخول باستخدام جهاز آخر" }, "loginInitiated": { - "message": "Login initiated" + "message": "بَدْء تسجيل الدخول" }, "notificationSentDevice": { "message": "تم إرسال إشعار إلى جهازك." @@ -2250,31 +2250,31 @@ "message": "تحديث الإعدادات الموصى بها" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "موافقة الجهاز مطلوبة. حدد خيار الموافقة أدناه:" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "تذكر هذا الجهاز" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "إلغاء تحديد عند استخدام جهاز عام" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "الموافقة من جهازك الآخر" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "طلب موافقة المشرف" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "الموافقة بواسطة كلمة المرور الرئيسية" }, "region": { - "message": "Region" + "message": "المنطقة" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "معرف الـSSO للمؤسسة مطلوب." }, "eu": { - "message": "EU", + "message": "الاتحاد الأوروبي", "description": "European Union" }, "loggingInOn": { @@ -2287,46 +2287,46 @@ "message": "bitwarden.eu" }, "selfHostedServer": { - "message": "self-hosted" + "message": "استضافة ذاتية" }, "accessDenied": { "message": "غير مسموح بالدخول. ليس لديك الصلاحية لعرض هذه الصفحة." }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "تم إنشاء الحساب بنجاح!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "طلبت موافقة المسؤول" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "تم إرسال طلبك إلى المشرف الخاص بك." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "سيتم إعلامك بمجرد الموافقة." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "مشكلة في تسجيل الدخول؟" }, "loginApproved": { - "message": "Login approved" + "message": "تمت الموافقة على تسجيل الدخول" }, "userEmailMissing": { - "message": "User email missing" + "message": "البريد الإلكتروني للمستخدم مفقود" }, "deviceTrusted": { - "message": "Device trusted" + "message": "الجهاز موثوق به" }, "inputRequired": { - "message": "Input is required." + "message": "الإدخال مطلوب." }, "required": { - "message": "required" + "message": "مطلوب" }, "search": { - "message": "Search" + "message": "بحث" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "الإدخال يجب أن يكون على الأقل $COUNT$ حرفاً.", "placeholders": { "count": { "content": "$1", @@ -2335,7 +2335,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "الإدخال يجب أن لا يتجاوز أحرف $COUNT$ في الطول.", "placeholders": { "count": { "content": "$1", @@ -2344,7 +2344,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "الأحرف التالية غير مسموح بها: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -2353,7 +2353,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "يجب أن تكون قيمة الإدخال على الأقل $MIN$.", "placeholders": { "min": { "content": "$1", @@ -2362,7 +2362,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "يجب ألا تتجاوز قيمة الإدخال $MAX$.", "placeholders": { "max": { "content": "$1", @@ -2371,17 +2371,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 أو أكثر من رسائل البريد الإلكتروني غير صالحة" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "الإدخال يجب أن لا يحتوي على مساحة بيضاء فقط.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "الإدخال ليس عنوان بريد إلكتروني." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "الحقل (الحقول) $COUNT$ أعلاه بحاجة إلى اهتمامك.", "placeholders": { "count": { "content": "$1", @@ -2390,22 +2390,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- اختيار --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- اكتب للنصفية --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "استرداد الخيارات..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "لم يتم العثور على عناصر" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "امسح الكل" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ المزيد", "placeholders": { "quantity": { "content": "$1", @@ -2414,6 +2414,6 @@ } }, "submenu": { - "message": "Submenu" + "message": "قائمة فرعية" } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index d14a5e369d9..41c35e45820 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimal fayl həcmi 500 MB-dır." }, - "updateKey": { - "message": "Şifrələmə açarınızı güncəlləyənə qədər bu özəlliyi istifadə edə bilməzsiniz." + "encryptionKeyMigrationRequired": { + "message": "Şifrələmə açarının daşınması tələb olunur. Şifrələmə açarınızı güncəlləmək üçün zəhmət olmasa veb anbar üzərindən giriş edin." }, "editedFolder": { "message": "Qovluğa düzəliş edildi" diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 3148dcb2b42..d8302dec530 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Максімальны памер файла 500 МБ." }, - "updateKey": { - "message": "Вы не зможаце выкарыстоўваць гэту функцыю, пакуль не абнавіце свой ключ шыфравання." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Папка адрэдагавана" diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 6266883d29b..2315ae8519e 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Големината на файла е най-много 500 MB." }, - "updateKey": { - "message": "Трябва да обновите шифриращия си ключ, за да използвате тази възможност." + "encryptionKeyMigrationRequired": { + "message": "Необходима е промяна на шифриращия ключ. Впишете се в трезора си по уеб, за да обновите своя шифриращ ключ." }, "editedFolder": { "message": "Редактирана папка" diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index d94904b486e..4271873c462 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "সর্বোচ্চ ফাইলের আকার ১০০ এমবি।" }, - "updateKey": { - "message": "আপনি আপনার এনক্রিপশন কী হালনাগাদ না করা পর্যন্ত এই বৈশিষ্ট্যটি ব্যবহার করতে পারবেন না।" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "ফোল্ডার সম্পাদিত" diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index dbe4af97e66..17ebcf15e8f 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimalna veličina datoteke je 500 MB." }, - "updateKey": { - "message": "Ovu funkciju ne možete koristiti dok ne ažurirate ključ za šifrovanje." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Uređen folder" diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 3cb44b3ecbb..f7b980b791e 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "La mida màxima del fitxer és de 500 MB." }, - "updateKey": { - "message": "No podeu utilitzar aquesta característica fins que no actualitzeu la vostra clau de xifratge." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Carpeta guardada" diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 875b1f85831..c66c3dc9246 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximální velikost souboru je 500 MB." }, - "updateKey": { - "message": "Dokud neaktualizujete svůj šifrovací klíč, nemůžete tuto funkci použít." + "encryptionKeyMigrationRequired": { + "message": "Vyžaduje se migrace šifrovacího klíče. Pro aktualizaci šifrovacího klíče se přihlaste přes webový trezor." }, "editedFolder": { "message": "Složka byla uložena" diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 16c619b8caf..2d545648f0a 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimum filstørrelse er 500 MB." }, - "updateKey": { - "message": "Denne funktion kan ikke bruges, før din krypteringsnøgle opdateres." + "encryptionKeyMigrationRequired": { + "message": "Krypteringsnøglemigrering nødvendig. Log ind gennem web-boksen for at opdatere krypteringsnøglen." }, "editedFolder": { "message": "Mappe gemt" diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index ddb23c5aa33..e9d1e5de6ed 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Die maximale Dateigröße beträgt 500 MB." }, - "updateKey": { - "message": "Du kannst diese Funktion nicht nutzen, bevor du deinen Verschlüsselungsschlüssel aktualisiert hast." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Ordner gespeichert" diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 4bb7f624193..11d2aba2f74 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Το μέγιστο μέγεθος αρχείου είναι 500 MB." }, - "updateKey": { - "message": "Δεν μπορείτε να χρησιμοποιήσετε αυτήν τη λειτουργία μέχρι να ενημερώσετε το κλειδί κρυπτογράφησης." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Ο φάκελος αποθηκεύτηκε" diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index d2b0cc4dae2..77bb93b520a 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index a641a0a8b60..faa3a67731b 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Edited folder" diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 77199adbb81..ae7fc279de4 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index ca03ed49e98..e534989c461 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "El tamaño máximo de archivo es de 500MB." }, - "updateKey": { - "message": "No puedes usar esta característica hasta que actualices tu clave de cifrado." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Carpeta editada" diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 0811a107390..aa92702b8e3 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimaalne faili suurus on 500 MB." }, - "updateKey": { - "message": "Seda funktsiooni ei saa enne krüpteerimise võtme uuendamist kasutada." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Kaust on muudetud" diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 7cea45c7e4e..99cd64ea7b3 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Eranskinaren gehienezko tamaina 500MB." }, - "updateKey": { - "message": "Ezin duzu ezaugarri hau erabili zifratze-gakoa eguneratu arte." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Karpeta editatuta" diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index bb429f07742..e01c6ffda49 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "بیشترین حجم پرونده ۵۰۰ مگابایت است." }, - "updateKey": { - "message": "تا زمانی که کد رمزنگاری را به‌روز نکنید نمی‌توانید از این قابلیت استفاده کنید." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "پوشه ذخیره شد" diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 64161295558..90a378996fc 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Tiedoston enimmäiskoko on 500 Mt." }, - "updateKey": { - "message": "Et voi käyttää tätä toimintoa ennen kuin päivität salausavaimesi." + "encryptionKeyMigrationRequired": { + "message": "Salausavaimen siirto vaaditaan. Päivitä salausavaimesi kirjautumalla verkkoholviin." }, "editedFolder": { "message": "Kansio tallennettiin" diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 69a63c46c1a..2a1866896f7 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum na laki ng file ay 500 MB." }, - "updateKey": { - "message": "Hindi mo maari gamitin ang tampok na ito hanggang hindi mo iupdate ang iyong encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Nai-save na folder" diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 0e817f63285..1a7f86e744a 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "La taille maximale du fichier est de 500 Mo." }, - "updateKey": { - "message": "Vous ne pouvez pas utiliser cette fonctionnalité tant que vous ne mettez pas à jour votre clé de chiffrement." + "encryptionKeyMigrationRequired": { + "message": "Migration de la clé de chiffrement nécessaire. Veuillez vous connecter sur le coffre web pour mettre à jour votre clé de chiffrement." }, "editedFolder": { "message": "Dossier enregistré" @@ -1985,7 +1985,7 @@ "message": "Export du coffre personnel" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Seuls les éléments individuels du coffre associés à $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus. Seules les informations sur les éléments du coffre seront exportées et n'incluront pas les pièces jointes associées.", "placeholders": { "email": { "content": "$1", diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 37d13549d77..b809355385d 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "גודל הקובץ המירבי הוא 500 מגה." }, - "updateKey": { - "message": "לא ניתן להשתמש ביכולת זו עד שתעדכן את מפתח ההצפנה שלך." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "תיקייה שנשמרה" diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index d3c58ec6e8f..d88afc01d54 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index e30758aaa13..83c2d06105a 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Najveća veličina datoteke je 500 MB." }, - "updateKey": { - "message": "Ne možeš koristiti ovu značajku prije nego ažuriraš ključ za šifriranje." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Mapa spremljena" diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 8987df7e283..d9b383710ec 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximális fájl méret 500 MB." }, - "updateKey": { - "message": "Ez a funkció nem használható a titkosítási kulcs frissítéséig." + "encryptionKeyMigrationRequired": { + "message": "Titkosítási kulcs migráció szükséges. Jelentkezzünk be a webes széfen keresztül a titkosítási kulcs frissítéséhez." }, "editedFolder": { "message": "A mappa mentésre került." diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 728e5581722..d4f87cee3a8 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Ukuran berkas maksimal adalah 500 MB." }, - "updateKey": { - "message": "Anda tidak dapat menggunakan fitur ini sampai Anda memperbarui kunci enkripsi Anda." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder yang di Edit" diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 7bff6e03ab7..387dbf647c1 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "La dimensione massima del file è 500 MB." }, - "updateKey": { - "message": "Non puoi usare questa funzionalità finché non aggiorni la tua chiave di criptografia." + "encryptionKeyMigrationRequired": { + "message": "Migrazione della chiave di criptografia obbligatoria. Accedi tramite la cassaforte web per aggiornare la tua chiave di criptografia." }, "editedFolder": { "message": "Cartella salvata" diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 4972332cd7a..ba26101b223 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "最大ファイルサイズは500MBです。" }, - "updateKey": { - "message": "暗号キーを更新するまでこの機能は使用できません。" + "encryptionKeyMigrationRequired": { + "message": "暗号化キーの移行が必要です。暗号化キーを更新するには、ウェブ保管庫からログインしてください。" }, "editedFolder": { "message": "フォルダーを編集しました" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index cc595c6efeb..ca8961b2e28 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "ಗರಿಷ್ಠ ಫೈಲ್ ಗಾತ್ರ 500 ಎಂಬಿ." }, - "updateKey": { - "message": "ನಿಮ್ಮ ಎನ್‌ಕ್ರಿಪ್ಶನ್ ಕೀಲಿಯನ್ನು ನವೀಕರಿಸುವವರೆಗೆ ನೀವು ಈ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬಳಸಲಾಗುವುದಿಲ್ಲ." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "ಫೋಲ್ಡರ್ ತಿದ್ದಲಾಗಿದೆ" diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 6433157727d..fc90ddd3054 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "최대 파일 크기는 500MB입니다." }, - "updateKey": { - "message": "이 기능을 사용하려면 암호화 키를 업데이트해야 합니다." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "폴더 편집함" diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 033ba1910fe..e501ed2dd58 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Didžiausias failo dydis – 500 MB." }, - "updateKey": { - "message": "Negalite naudoti šios funkcijos, kol neatnaujinsite šifravimo rakto." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Aplankas išsaugotas" diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 63179371b82..4146ccc59cb 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Lielākais pieļaujamais datnes izmērs ir 500 MB." }, - "updateKey": { - "message": "Šo iespēju nevar izmantot, kamēr nav atjaunināta šifrēšanas atslēga." + "encryptionKeyMigrationRequired": { + "message": "Nepieciešama šifrēšanas atslēgas nomaiņa. Lūgums pieteikties tīmekļa glabātavā, lai atjauninātu savu šifrēšanas atslēgu." }, "editedFolder": { "message": "Mape labota" diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 375a8b2e9aa..66f90e8c989 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximalna veličina datoteke je 500 MB." }, - "updateKey": { - "message": "Ovu funkciju ne možete da koristite dok ne ažurirate ključ za šifrovanje." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Izmijenjena fascikla" diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index eb7ca9d8d29..d7a6814e8a9 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "പരമാവധി ഫയൽ വലുപ്പം 500 MB ആണ്." }, - "updateKey": { - "message": "നിങ്ങളുടെ എൻ‌ക്രിപ്ഷൻ കീ അപ്‌ഡേറ്റ് ചെയ്യുന്നതുവരെ നിങ്ങൾക്ക് ഈ സവിശേഷത ഉപയോഗിക്കാൻ കഴിയില്ല." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "ഫോൾഡർ തിരുത്തി" diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index d047602948c..bc52b76f5db 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index cb92f718040..55262558144 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Den maksimale filstørrelsen er 500 MB." }, - "updateKey": { - "message": "Du kan ikke bruke denne funksjonen før du oppdaterer krypteringsnøkkelen din." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Redigerte mappen" diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 2c81328ec6e..2db4d1eb3e6 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 8c7058c2cca..d0c7991b2b2 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximale bestandsgrootte is 500 MB." }, - "updateKey": { - "message": "Je kunt deze functie pas gebruiken als je je encryptiesleutel bijwerkt." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Map is bewerkt" diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 4a5dc712873..9a235d5853a 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Den høgaste tillatne filstorleiken er 500 MB." }, - "updateKey": { - "message": "Du kan ikkje bruke denne funksjonen, før du har oppdatert krypteringsnøkkelen din." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Mappe lagra" diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index bdaf9990917..9581b28d06a 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index d730a0e9902..ca4027153f8 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksymalny rozmiar pliku to 500 MB." }, - "updateKey": { - "message": "Nie możesz używać tej funkcji, dopóki nie zaktualizujesz klucza szyfrowania." + "encryptionKeyMigrationRequired": { + "message": "Wymagana jest migracja klucza szyfrowania. Zaloguj się przez sejf internetowy, aby zaktualizować klucz szyfrowania." }, "editedFolder": { "message": "Folder został zapisany" diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index eaf3663b8f0..7381ffeea1b 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "O tamanho máximo do arquivo é de 500 MB." }, - "updateKey": { - "message": "Você não pode usar este recurso até você atualizar sua chave de criptografia." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Pasta editada" diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 78298e1c536..2f1ed01ed20 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "O tamanho máximo do ficheiro é de 500 MB." }, - "updateKey": { - "message": "Não pode utilizar esta funcionalidade até atualizar a sua chave de encriptação." + "encryptionKeyMigrationRequired": { + "message": "É necessária a migração da chave de encriptação. Inicie sessão através do cofre Web para atualizar a sua chave de encriptação." }, "editedFolder": { "message": "Pasta guardada" diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 28cc9783f70..1fa5762094e 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Mărimea maximă a fișierului este de 500 MB." }, - "updateKey": { - "message": "Nu puteți utiliza această caracteristică înainte de a actualiza cheia de criptare." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Dosar salvat" diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index e51a39bfc81..b158ec1d3bc 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Максимальный размер файла 500 МБ." }, - "updateKey": { - "message": "Вы не можете использовать эту функцию, пока не обновите свой ключ шифрования." + "encryptionKeyMigrationRequired": { + "message": "Требуется миграция ключа шифрования. Чтобы обновить ключ шифрования, войдите через веб-хранилище." }, "editedFolder": { "message": "Папка сохранена" diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 8f50283ba62..af0ce89aff7 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 87fdb105639..01ee45312bb 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximálna veľkosť súboru je 500 MB." }, - "updateKey": { - "message": "Túto funkciu je možné použiť, až keď si aktualizujete svoj šifrovací kľúč." + "encryptionKeyMigrationRequired": { + "message": "Vyžaduje sa migrácia šifrovacieho kľúča. Na aktualizáciu šifrovacieho kľúča sa prihláste cez webový trezor." }, "editedFolder": { "message": "Priečinok upravený" diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 3926e5a7aca..db640080c7e 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Največja velikost datoteke je 500 MB." }, - "updateKey": { - "message": "Te funkcije ne morete koristiti, dokler dokler ne posodobite vašega ključa za šifriranje." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Mapa je bila urejena" diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 29b9ebd2a49..5c08226ed5f 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Максимална величина је 500МБ." }, - "updateKey": { - "message": "Не можете да користите ову опцију док не промените Ваш кључ за шифровање." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Фасцикла измењена" diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 70146e2f149..f998e30c731 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximal filstorlek är 500 MB." }, - "updateKey": { - "message": "Du kan inte använda denna funktion förrän du uppdaterar din krypteringsnyckel." + "encryptionKeyMigrationRequired": { + "message": "Migrering av krypteringsnyckel krävs. Logga in på webbvalvet för att uppdatera din krypteringsnyckel." }, "editedFolder": { "message": "Mapp sparad" diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 1eaa44b7109..fb6177cfb4a 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maximum file size is 500 MB." }, - "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Folder saved" diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index e9a98e21258..fd41e44c78e 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "ขนาดไฟล์สูงสุดคือ 500 MB" }, - "updateKey": { - "message": "คุณไม่สามารถใช้คุณลักษณะนี้ได้จนกว่าคุณปรับปรุงคีย์การเข้ารหัสลับของคุณ" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "แก้​ไข​โฟลเดอร์แล้ว" diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 3eaf0630e49..d1b51a01d46 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Maksimum dosya boyutu 500 MB'dir." }, - "updateKey": { - "message": "Şifreleme anahtarınızı güncelleştirene kadar bu özelliği kullanamazsınız." + "encryptionKeyMigrationRequired": { + "message": "Şifreleme anahtarınızın güncellenmesi gerekiyor. Şifreleme anahtarınızı güncellemek için lütfen web kasasına giriş yapın." }, "editedFolder": { "message": "Klasör kaydedildi" diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index bba71ea6f5a..84923d2d9a9 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Максимальний розмір файлу 500 Мб." }, - "updateKey": { - "message": "Ви не можете використовувати цю функцію доки не оновите свій ключ шифрування." + "encryptionKeyMigrationRequired": { + "message": "Потрібно перенести ключ шифрування. Увійдіть у вебсховище та оновіть свій ключ шифрування." }, "editedFolder": { "message": "Теку збережено" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 96498d741ae..d8fc0ff1462 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "Kích thước tối đa của tệp tin là 500MB." }, - "updateKey": { - "message": "Bạn không thể sử dụng tính năng này cho đến khi bạn cập nhật khoá mã hóa." + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "Đã chỉnh sửa thư mục" diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 81b08101eb9..8f778415b9c 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "文件最大为 500 MB。" }, - "updateKey": { - "message": "在您更新加密密钥前,您不能使用此功能。" + "encryptionKeyMigrationRequired": { + "message": "需要迁移加密密钥。请登录网页版密码库来更新您的加密密钥。" }, "editedFolder": { "message": "文件夹已保存" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 50ab2e3a3d3..5660ec8ec09 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -475,8 +475,8 @@ "maxFileSize": { "message": "檔案最大為 500MB。" }, - "updateKey": { - "message": "更新加密金鑰前不能使用此功能。" + "encryptionKeyMigrationRequired": { + "message": "Encryption key migration required. Please login through the web vault to update your encryption key." }, "editedFolder": { "message": "資料夾已儲存" From a9c25e83106491bb94bedf4c5d99d08fc35d045d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 22 Sep 2023 12:33:46 +0200 Subject: [PATCH 06/16] [BEEEP] [PM-4043] Remove date-input-polyfill (#6372) --- apps/browser/src/popup/polyfills.ts | 1 - apps/browser/src/popup/scss/plugins.scss | 6 ----- apps/web/src/polyfills.ts | 3 --- apps/web/src/scss/plugins.scss | 6 ----- package-lock.json | 30 ------------------------ package.json | 1 - 6 files changed, 47 deletions(-) diff --git a/apps/browser/src/popup/polyfills.ts b/apps/browser/src/popup/polyfills.ts index e41e960a8d4..14c0f595a39 100644 --- a/apps/browser/src/popup/polyfills.ts +++ b/apps/browser/src/popup/polyfills.ts @@ -1,5 +1,4 @@ import "core-js/stable"; -import "date-input-polyfill"; import "zone.js"; import "../platform/polyfills/zone-patch-chrome-runtime"; diff --git a/apps/browser/src/popup/scss/plugins.scss b/apps/browser/src/popup/scss/plugins.scss index 104927d73bb..e1e386d62d4 100644 --- a/apps/browser/src/popup/scss/plugins.scss +++ b/apps/browser/src/popup/scss/plugins.scss @@ -96,9 +96,3 @@ } } } - -date-input-polyfill { - &[data-open="true"] { - z-index: 10000 !important; - } -} diff --git a/apps/web/src/polyfills.ts b/apps/web/src/polyfills.ts index e6de94a5639..cc26cd13ab7 100644 --- a/apps/web/src/polyfills.ts +++ b/apps/web/src/polyfills.ts @@ -8,6 +8,3 @@ if (process.env.NODE_ENV === "production") { Error["stackTraceLimit"] = Infinity; require("zone.js/dist/long-stack-trace-zone"); } - -// Other polyfills -require("date-input-polyfill"); diff --git a/apps/web/src/scss/plugins.scss b/apps/web/src/scss/plugins.scss index 8d3b685b031..2f466484765 100644 --- a/apps/web/src/scss/plugins.scss +++ b/apps/web/src/scss/plugins.scss @@ -129,9 +129,3 @@ color: themed("textColor"); } } - -date-input-polyfill { - &[data-open="true"] { - z-index: 10000 !important; - } -} diff --git a/package-lock.json b/package-lock.json index 86ef7a3579a..7371e5602e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,6 @@ "chalk": "4.1.2", "commander": "7.2.0", "core-js": "3.32.0", - "date-input-polyfill": "2.14.0", "duo_web_sdk": "github:duosecurity/duo_web_sdk", "form-data": "4.0.0", "https-proxy-agent": "5.0.1", @@ -16725,27 +16724,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", - "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", - "hasInstallScript": true - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, "node_modules/bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -19241,14 +19219,6 @@ "node": ">=6.9.0" } }, - "node_modules/date-input-polyfill": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/date-input-polyfill/-/date-input-polyfill-2.14.0.tgz", - "integrity": "sha512-LUfuBYYlayDyBbQCIMN1RyrDaTmy5pa3u3jIDoWTXk/7tPgOajZczjWZA2ITd/+lbhtUBM6fhT+Grxs1yYATVA==", - "dependencies": { - "babel-runtime": "^6.11.6" - } - }, "node_modules/debounce-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", diff --git a/package.json b/package.json index c7c0a64360b..8c9f8e9b14c 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,6 @@ "chalk": "4.1.2", "commander": "7.2.0", "core-js": "3.32.0", - "date-input-polyfill": "2.14.0", "duo_web_sdk": "github:duosecurity/duo_web_sdk", "form-data": "4.0.0", "https-proxy-agent": "5.0.1", From c75f6c28eb1440d8a25e3629024b6bd6fee905cc Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Fri, 22 Sep 2023 10:03:50 -0500 Subject: [PATCH 07/16] [PM-3730] Master Password Re-prompt Enabled Items Invoke Popup Window Automatically When Autofill on Page Load is Enabled (#6189) * [PM-3730] MPR-enabled items invoke the pop-out window automatically when autofill on-page-load is enabled * [PM-3730] Updating vault item views to ensure that autofill on page load setting is not confusing to users * [PM-3730] Setting up toast message to appear when user opts to turn on master password reprompt * [PM-3730] Ensuring that toast messages do not show when the autofill on page load value is not on * [PM-3730] Updating copy and adding help text below the autofill on page load field within vault items in the extension --- apps/browser/src/_locales/en/messages.json | 12 ++++++++++ .../src/autofill/services/autofill.service.ts | 4 ++-- .../components/vault/add-edit.component.html | 4 ++++ .../components/vault/add-edit.component.ts | 23 +++++++++++++++++++ .../vault/components/add-edit.component.ts | 5 ++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index bf1eedd8261..fe5400c6a90 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2406,5 +2406,17 @@ "toggleCollapse": { "message": "Toggle collapse", "description": "Toggling an expand/collapse state." + }, + "passwordRepromptDisabledAutofillOnPageLoad": { + "message": "Items with master password re-prompt cannot be auto-filled on page load. Auto-fill on page load turned off.", + "description": "Toast message for describing that master password re-prompt cannot be auto-filled on page load." + }, + "autofillOnPageLoadSetToDefault": { + "message": "Auto-fill on page load set to use default setting.", + "description": "Toast message for informing the user that auto-fill on page load has been set to the default setting." + }, + "turnOffMasterPasswordPromptToEditField": { + "message": "Turn off master password re-prompt to edit this field", + "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." } } diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index fcf4dffd4aa..c494b8ece0f 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -19,9 +19,9 @@ import AutofillScript from "../models/autofill-script"; import { AutoFillOptions, AutofillService as AutofillServiceInterface, - PageDetail, FormData, GenerateFillScriptOptions, + PageDetail, } from "./abstractions/autofill.service"; import { AutoFillConstants, @@ -260,7 +260,7 @@ export default class AutofillService implements AutofillServiceInterface { } } - if (cipher == null) { + if (cipher == null || (cipher.reprompt === CipherRepromptType.Password && !fromCommand)) { return null; } diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.html b/apps/browser/src/vault/popup/components/vault/add-edit.component.html index 1373669ca17..dda71cb0d6e 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.html +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.html @@ -564,6 +564,7 @@ +
diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts index 6148e31dd6d..54cb08ae893 100644 --- a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts +++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts @@ -264,4 +264,27 @@ export class AddEditComponent extends BaseAddEditComponent { } }, 200); } + + repromptChanged() { + super.repromptChanged(); + + if (!this.showAutoFillOnPageLoadOptions) { + return; + } + + if (this.reprompt) { + this.platformUtilsService.showToast( + "info", + null, + this.i18nService.t("passwordRepromptDisabledAutofillOnPageLoad") + ); + return; + } + + this.platformUtilsService.showToast( + "info", + null, + this.i18nService.t("autofillOnPageLoadSetToDefault") + ); + } } diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 6bcd72082b1..377fe88b63f 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -272,6 +272,9 @@ export class AddEditComponent implements OnInit, OnDestroy { } this.previousCipherId = this.cipherId; this.reprompt = this.cipher.reprompt !== CipherRepromptType.None; + if (this.reprompt) { + this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[2].value; + } } async submit(): Promise { @@ -570,8 +573,10 @@ export class AddEditComponent implements OnInit, OnDestroy { this.reprompt = !this.reprompt; if (this.reprompt) { this.cipher.reprompt = CipherRepromptType.Password; + this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[2].value; } else { this.cipher.reprompt = CipherRepromptType.None; + this.cipher.login.autofillOnPageLoad = this.autofillOnPageLoadOptions[0].value; } } From ca65548b3ac221781b527c5d318ef3c0ea67da62 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 08:33:02 -0400 Subject: [PATCH 08/16] Update bitwarden/gh-actions digest to 62d1bf7 (#6385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/brew-bump-cli.yml | 2 +- .github/workflows/brew-bump-desktop.yml | 2 +- .github/workflows/build-browser.yml | 4 ++-- .github/workflows/build-cli.yml | 2 +- .github/workflows/build-desktop.yml | 8 +++---- .github/workflows/build-web.yml | 6 ++--- .github/workflows/crowdin-pull.yml | 4 ++-- .github/workflows/deploy-eu-prod-web.yml | 4 ++-- .github/workflows/deploy-eu-qa-web.yml | 4 ++-- .github/workflows/deploy-non-prod-web.yml | 2 +- .github/workflows/release-browser.yml | 6 ++--- .github/workflows/release-cli.yml | 24 ++++++++++---------- .github/workflows/release-desktop-beta.yml | 8 +++---- .github/workflows/release-desktop.yml | 22 +++++++++--------- .github/workflows/release-web.yml | 12 +++++----- .github/workflows/staged-rollout-desktop.yml | 2 +- .github/workflows/version-bump.yml | 6 ++--- .github/workflows/workflow-linter.yml | 2 +- 18 files changed, 60 insertions(+), 60 deletions(-) diff --git a/.github/workflows/brew-bump-cli.yml b/.github/workflows/brew-bump-cli.yml index 477c9ace582..d2e998eacb0 100644 --- a/.github/workflows/brew-bump-cli.yml +++ b/.github/workflows/brew-bump-cli.yml @@ -23,7 +23,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "brew-bump-workflow-pat" diff --git a/.github/workflows/brew-bump-desktop.yml b/.github/workflows/brew-bump-desktop.yml index 0a5c3947161..1856cc5fb82 100644 --- a/.github/workflows/brew-bump-desktop.yml +++ b/.github/workflows/brew-bump-desktop.yml @@ -23,7 +23,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "brew-bump-workflow-pat" diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 5e64dc35875..57c2fdcef07 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -361,7 +361,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@37ffa14164a7308bc273829edfe75c97cd562375 + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" @@ -423,7 +423,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@37ffa14164a7308bc273829edfe75c97cd562375 + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 4d19c7e7cce..9fa71cb24fe 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -404,7 +404,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 8dfb88163a6..6db1e59a3fa 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -277,7 +277,7 @@ jobs: node-gyp install $(node -v) - name: Install AST - uses: bitwarden/gh-actions/install-ast@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/install-ast@62d1bf7c3e31c458cc7236b1e69a475d235cd78f - name: Set up environmentF run: choco install checksum --no-progress @@ -302,7 +302,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "code-signing-vault-url, @@ -1190,7 +1190,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" @@ -1269,7 +1269,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 72117bde1f8..f5458448465 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -188,7 +188,7 @@ jobs: - name: Retrieve github PAT secrets id: retrieve-secret-pat - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -264,7 +264,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" @@ -325,7 +325,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index fed766e71d8..d14938cc468 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -32,13 +32,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - name: Download translations - uses: bitwarden/gh-actions/crowdin@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/crowdin@62d1bf7c3e31c458cc7236b1e69a475d235cd78f env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/deploy-eu-prod-web.yml b/.github/workflows/deploy-eu-prod-web.yml index f051207680e..523e0f44de7 100644 --- a/.github/workflows/deploy-eu-prod-web.yml +++ b/.github/workflows/deploy-eu-prod-web.yml @@ -24,13 +24,13 @@ jobs: - name: Retrieve Storage Account connection string id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: webvault-westeurope-prod secrets: "sa-bitwarden-web-vault-dev-key-temp" - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: apps/web diff --git a/.github/workflows/deploy-eu-qa-web.yml b/.github/workflows/deploy-eu-qa-web.yml index 34525eaa5f7..2a1e271f18b 100644 --- a/.github/workflows/deploy-eu-qa-web.yml +++ b/.github/workflows/deploy-eu-qa-web.yml @@ -24,13 +24,13 @@ jobs: - name: Retrieve Storage Account connection string id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: webvaulteu-westeurope-qa secrets: "sa-bitwarden-web-vault-dev-key-temp" - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: apps/web diff --git a/.github/workflows/deploy-non-prod-web.yml b/.github/workflows/deploy-non-prod-web.yml index f7411f5432d..78e0f12b7e9 100644 --- a/.github/workflows/deploy-non-prod-web.yml +++ b/.github/workflows/deploy-non-prod-web.yml @@ -67,7 +67,7 @@ jobs: uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: apps/web diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index 407f81deb60..5fd9441f14a 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -41,7 +41,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@58a2fdfbd3f1fc7e6727bc5dc51d159f4df07072 + uses: bitwarden/gh-actions/release-version-check@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -103,7 +103,7 @@ jobs: - name: Download latest Release build artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-browser.yml workflow_conclusion: success @@ -116,7 +116,7 @@ jobs: - name: Dry Run - Download latest master build artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-browser.yml workflow_conclusion: success diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 9ff812bf305..7c21cafcfd1 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -57,7 +57,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/release-version-check@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -78,7 +78,7 @@ jobs: - name: Download all Release artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli @@ -87,7 +87,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli @@ -150,7 +150,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "snapcraft-store-token" @@ -162,7 +162,7 @@ jobs: - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli @@ -172,7 +172,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli @@ -206,7 +206,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "cli-choco-api-key" @@ -222,7 +222,7 @@ jobs: - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli/dist @@ -232,7 +232,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli/dist @@ -265,14 +265,14 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "npm-api-key" - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli/build @@ -282,7 +282,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-cli.yml path: apps/cli/build diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index 335d705d2d9..ce09a7d80ad 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -47,7 +47,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/release-version-check@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: release-type: 'Initial Release' project-type: ts @@ -231,7 +231,7 @@ jobs: node-gyp install $(node -v) - name: Install AST - uses: bitwarden/gh-actions/install-ast@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/install-ast@62d1bf7c3e31c458cc7236b1e69a475d235cd78f - name: Set up environment run: choco install checksum --no-progress @@ -249,7 +249,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "code-signing-vault-url, @@ -932,7 +932,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 3dbc08f9859..f60020c7327 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -67,7 +67,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/release-version-check@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: release-type: ${{ inputs.release_type }} project-type: ts @@ -110,7 +110,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, @@ -123,7 +123,7 @@ jobs: - name: Download all artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success @@ -132,7 +132,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success @@ -185,7 +185,7 @@ jobs: --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com - name: Get checksum files - uses: bitwarden/gh-actions/get-checksum@82cfceb235b308c2eb63923824e61d8350d280db + uses: bitwarden/gh-actions/get-checksum@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: packages_dir: "apps/desktop/artifacts" file_path: "apps/desktop/artifacts/sha256-checksums.txt" @@ -263,7 +263,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "snapcraft-store-token" @@ -279,7 +279,7 @@ jobs: - name: Download Snap artifact if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success @@ -289,7 +289,7 @@ jobs: - name: Dry Run - Download Snap artifact if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success @@ -329,7 +329,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "cli-choco-api-key" @@ -347,7 +347,7 @@ jobs: - name: Download choco artifact if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success @@ -357,7 +357,7 @@ jobs: - name: Dry Run - Download choco artifact if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-desktop.yml workflow_conclusion: success diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 5b4b7195aea..46113b94b40 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -41,7 +41,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/release-version-check@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -130,7 +130,7 @@ jobs: - name: Retrieve bot secrets id: retrieve-bot-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: bitwarden-ci secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -144,7 +144,7 @@ jobs: - name: Download latest cloud asset if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: assets @@ -154,7 +154,7 @@ jobs: - name: Dry Run - Download latest cloud asset if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: assets @@ -227,7 +227,7 @@ jobs: - name: Download latest build artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: apps/web/artifacts @@ -238,7 +238,7 @@ jobs: - name: Dry Run - Download latest build artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/download-artifacts@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: workflow: build-web.yml path: apps/web/artifacts diff --git a/.github/workflows/staged-rollout-desktop.yml b/.github/workflows/staged-rollout-desktop.yml index ce56cc205dd..b0c57e1a1be 100644 --- a/.github/workflows/staged-rollout-desktop.yml +++ b/.github/workflows/staged-rollout-desktop.yml @@ -26,7 +26,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index c650e42ecf4..f50a6fe6cd5 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -54,7 +54,7 @@ jobs: - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/get-keyvault-secrets@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: keyvault: "bitwarden-ci" secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" @@ -125,14 +125,14 @@ jobs: - name: Bump Browser Version - Manifest if: ${{ inputs.bump_browser == true }} - uses: bitwarden/gh-actions/version-bump@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/version-bump@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: version: ${{ inputs.version_number }} file_path: "apps/browser/src/manifest.json" - name: Bump Browser Version - Manifest v3 if: ${{ inputs.bump_browser == true }} - uses: bitwarden/gh-actions/version-bump@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/version-bump@62d1bf7c3e31c458cc7236b1e69a475d235cd78f with: version: ${{ inputs.version_number }} file_path: "apps/browser/src/manifest.v3.json" diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index aef3077eb35..f38d228dda6 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -8,4 +8,4 @@ on: jobs: call-workflow: - uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@67ab95d7a466bcefdedf3f93cbc10bcff436edfe + uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@62d1bf7c3e31c458cc7236b1e69a475d235cd78f From d0037bb257abd06725bac186c4164f9417b87832 Mon Sep 17 00:00:00 2001 From: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:53:50 -0400 Subject: [PATCH 09/16] Add Braintree sandbox to permitted sources in CSP (#6381) --- apps/web/webpack.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/webpack.config.js b/apps/web/webpack.config.js index c7d2bcb5e01..96deb2f80e8 100644 --- a/apps/web/webpack.config.js +++ b/apps/web/webpack.config.js @@ -272,6 +272,7 @@ const devServer = https://api.2fa.directory/v3/totp.json https://api.stripe.com https://www.paypal.com + https://api.sandbox.braintreegateway.com https://api.braintreegateway.com https://client-analytics.braintreegateway.com https://*.braintree-api.com From e4fee0c7663ce455ca75b60e853c26635ab26b13 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 26 Sep 2023 03:49:59 +0900 Subject: [PATCH 10/16] fix typo in base.scss (#6399) appropiate -> appropriate --- apps/desktop/src/scss/base.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/scss/base.scss b/apps/desktop/src/scss/base.scss index 31be3f1bccc..3912d6cbf1d 100644 --- a/apps/desktop/src/scss/base.scss +++ b/apps/desktop/src/scss/base.scss @@ -17,7 +17,7 @@ body { body { color: $text-color; - // We initially rely on electron to provide the appropiate background color. + // We initially rely on electron to provide the appropriate background color. // This ensures the background color while reloading is correct to avoid a jarring missmatch. background-color: transparent; From 5616e69e1020f904e018bf6e8e1eb963f47156f2 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Mon, 25 Sep 2023 22:09:12 +0200 Subject: [PATCH 11/16] Removed unused references to ModalService (#6371) Co-authored-by: Daniel James Smith --- .../app/admin-console/organizations/manage/groups.component.ts | 2 -- .../organizations/tools/import-export/org-import.component.ts | 3 --- apps/web/src/app/tools/import-export/import.component.ts | 2 -- apps/web/src/app/tools/send/send.component.ts | 2 -- .../service-accounts/access/access-tokens.component.ts | 2 -- 5 files changed, 11 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts index f2167c0e5f3..5473f2eff68 100644 --- a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts @@ -15,7 +15,6 @@ import { import { first } from "rxjs/operators"; import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; @@ -126,7 +125,6 @@ export class GroupsComponent implements OnInit, OnDestroy { private groupService: GroupService, private route: ActivatedRoute, private i18nService: I18nService, - private modalService: ModalService, private dialogService: DialogService, private platformUtilsService: PlatformUtilsService, private searchService: SearchService, diff --git a/apps/web/src/app/admin-console/organizations/tools/import-export/org-import.component.ts b/apps/web/src/app/admin-console/organizations/tools/import-export/org-import.component.ts index 3c9b5858e04..e8dad8baf41 100644 --- a/apps/web/src/app/admin-console/organizations/tools/import-export/org-import.component.ts +++ b/apps/web/src/app/admin-console/organizations/tools/import-export/org-import.component.ts @@ -3,7 +3,6 @@ import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { switchMap, takeUntil } from "rxjs/operators"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { canAccessVaultTab, OrganizationService, @@ -42,7 +41,6 @@ export class OrganizationImportComponent extends ImportComponent { policyService: PolicyService, organizationService: OrganizationService, logService: LogService, - modalService: ModalService, syncService: SyncService, dialogService: DialogService, folderService: FolderService, @@ -56,7 +54,6 @@ export class OrganizationImportComponent extends ImportComponent { platformUtilsService, policyService, logService, - modalService, syncService, dialogService, folderService, diff --git a/apps/web/src/app/tools/import-export/import.component.ts b/apps/web/src/app/tools/import-export/import.component.ts index de2de35777e..1f71b2fd775 100644 --- a/apps/web/src/app/tools/import-export/import.component.ts +++ b/apps/web/src/app/tools/import-export/import.component.ts @@ -5,7 +5,6 @@ import * as JSZip from "jszip"; import { concat, Observable, Subject, lastValueFrom, combineLatest } from "rxjs"; import { map, takeUntil } from "rxjs/operators"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { canAccessImportExport, OrganizationService, @@ -76,7 +75,6 @@ export class ImportComponent implements OnInit, OnDestroy { protected platformUtilsService: PlatformUtilsService, protected policyService: PolicyService, private logService: LogService, - protected modalService: ModalService, protected syncService: SyncService, protected dialogService: DialogService, protected folderService: FolderService, diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 7301c3bcac3..6b5f10dd3a8 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -1,7 +1,6 @@ import { Component, NgZone, ViewChild, ViewContainerRef } from "@angular/core"; import { lastValueFrom } from "rxjs"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -52,7 +51,6 @@ export class SendComponent extends BaseSendComponent { ngZone: NgZone, searchService: SearchService, policyService: PolicyService, - private modalService: ModalService, private broadcasterService: BroadcasterService, logService: LogService, sendApiService: SendApiService, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts index 24080d3e7da..ea264e8aa4a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts @@ -10,7 +10,6 @@ import { takeUntil, } from "rxjs"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; @@ -37,7 +36,6 @@ export class AccessTokenComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private accessService: AccessService, private dialogService: DialogService, - private modalService: ModalService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private serviceAccountService: ServiceAccountService From abd3e66e20f765c306764dd8f3aed5d0bc260456 Mon Sep 17 00:00:00 2001 From: David Claybourne Date: Tue, 26 Sep 2023 15:34:34 +0200 Subject: [PATCH 12/16] [PS-1878] Feature/selfhost anonaddy (#4056) * Add selfhosted anonaddy url * Remove newlines * Add serverurl field to web * Show anonaddy api errors * Add forwardedAnonAddyBaseUrl prop to UsernameGeneratorOptions type * Use Alias Domain instead of just Domain name This is easier to distinguish between domain and server url * Only show Server Url on web if on selfhosted bitwarden --------- Co-authored-by: Daniel James Smith Co-authored-by: Daniel James Smith --- apps/browser/src/_locales/en/messages.json | 3 +++ .../popup/generator/generator.component.html | 14 ++++++++++++-- .../src/app/tools/generator.component.html | 14 ++++++++++++-- apps/desktop/src/locales/en/messages.json | 3 +++ apps/web/src/app/tools/generator.component.html | 15 +++++++++++++-- apps/web/src/app/tools/generator.component.ts | 4 ++++ apps/web/src/locales/en/messages.json | 6 ++++++ apps/web/webpack.config.js | 2 +- .../email-forwarders/anon-addy-forwarder.ts | 9 ++++++++- .../email-forwarders/forwarder-options.ts | 1 + .../username/username-generation-options.ts | 1 + .../username/username-generation.service.ts | 2 ++ 12 files changed, 66 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index fe5400c6a90..f8ffeff747e 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2407,6 +2407,9 @@ "message": "Toggle collapse", "description": "Toggling an expand/collapse state." }, + "aliasDomain": { + "message": "Alias domain" + }, "passwordRepromptDisabledAutofillOnPageLoad": { "message": "Items with master password re-prompt cannot be auto-filled on page load. Auto-fill on page load turned off.", "description": "Toast message for describing that master password re-prompt cannot be auto-filled on page load." diff --git a/apps/browser/src/tools/popup/generator/generator.component.html b/apps/browser/src/tools/popup/generator/generator.component.html index 13d35b6cd7a..cf7a2949f24 100644 --- a/apps/browser/src/tools/popup/generator/generator.component.html +++ b/apps/browser/src/tools/popup/generator/generator.component.html @@ -378,7 +378,7 @@ />
- +
+
+ + +
@@ -424,7 +434,7 @@ />
- +
- +
+
+ + +
@@ -451,7 +461,7 @@ />
- +
- +
+
+ + +
@@ -355,7 +366,7 @@ />
- + Date: Tue, 26 Sep 2023 23:07:22 +0200 Subject: [PATCH 13/16] Forbid promise misuse (#6360) --- .eslintrc.json | 16 +++++----------- .../src/autofill/services/autofill.service.ts | 1 + .../src/services/native-messaging.service.ts | 1 + .../settings/user-subscription.component.ts | 1 + .../src/app/vault/settings/premium.component.ts | 1 + .../clients/add-organization.component.ts | 1 + 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 4fba3dd6128..d6c329a94dc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -34,20 +34,14 @@ } }, "rules": { - "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled - "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], "@typescript-eslint/explicit-member-accessibility": [ "error", - { - "accessibility": "no-public" - } - ], - "@typescript-eslint/no-this-alias": [ - "error", - { - "allowedNames": ["self"] - } + { "accessibility": "no-public" } ], + "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled + "@typescript-eslint/no-misused-promises": ["error", { "checksVoidReturn": false }], + "@typescript-eslint/no-this-alias": ["error", { "allowedNames": ["self"] }], + "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], "no-console": "error", "import/no-unresolved": "off", // TODO: Look into turning off once each package is an actual package. "import/order": [ diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index c494b8ece0f..81bf107650d 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -205,6 +205,7 @@ export default class AutofillService implements AutofillServiceInterface { if ( options.cipher.type !== CipherType.Login || + // eslint-disable-next-line @typescript-eslint/no-misused-promises totpPromise || !options.cipher.login.totp || (!canAccessPremium && !options.cipher.organizationUseTotp) diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index c18a67e6b43..bb92beca782 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -116,6 +116,7 @@ export class NativeMessagingService { switch (message.command) { case "biometricUnlock": { + // eslint-disable-next-line @typescript-eslint/no-misused-promises if (!this.platformUtilService.supportsBiometric()) { return this.send({ command: "biometricUnlock", response: "not supported" }, appId); } diff --git a/apps/web/src/app/billing/settings/user-subscription.component.ts b/apps/web/src/app/billing/settings/user-subscription.component.ts index d058f7f1051..2871a964ba1 100644 --- a/apps/web/src/app/billing/settings/user-subscription.component.ts +++ b/apps/web/src/app/billing/settings/user-subscription.component.ts @@ -53,6 +53,7 @@ export class UserSubscriptionComponent implements OnInit { return; } + // eslint-disable-next-line @typescript-eslint/no-misused-promises if (this.stateService.getHasPremiumPersonally()) { this.loading = true; this.sub = await this.apiService.getUserSubscription(); diff --git a/apps/web/src/app/vault/settings/premium.component.ts b/apps/web/src/app/vault/settings/premium.component.ts index 7fb1d169758..445925e9be6 100644 --- a/apps/web/src/app/vault/settings/premium.component.ts +++ b/apps/web/src/app/vault/settings/premium.component.ts @@ -74,6 +74,7 @@ export class PremiumComponent implements OnInit { try { if (this.selfHosted) { + // eslint-disable-next-line @typescript-eslint/no-misused-promises if (!this.tokenService.getEmailVerified()) { this.platformUtilsService.showToast( "error", diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts index 2f0d3076745..d3eff4bc53c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts @@ -47,6 +47,7 @@ export class AddOrganizationComponent implements OnInit { } async add(organization: Organization) { + // eslint-disable-next-line @typescript-eslint/no-misused-promises if (this.formPromise) { return; } From fe0ef5aad74a6f64309aadb1d22b5d46388d98fe Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Wed, 27 Sep 2023 10:57:50 -0400 Subject: [PATCH 14/16] Clear VAT/GST info when box is unchecked (#6338) --- .../billing/settings/tax-info.component.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/billing/settings/tax-info.component.ts b/apps/web/src/app/billing/settings/tax-info.component.ts index df5ddfa0a03..af47a6821e2 100644 --- a/apps/web/src/app/billing/settings/tax-info.component.ts +++ b/apps/web/src/app/billing/settings/tax-info.component.ts @@ -125,14 +125,22 @@ export class TaxInfoComponent { getTaxInfoRequest(): TaxInfoUpdateRequest { if (this.organizationId) { const request = new OrganizationTaxInfoUpdateRequest(); - request.taxId = this.taxInfo.taxId; - request.state = this.taxInfo.state; - request.line1 = this.taxInfo.line1; - request.line2 = this.taxInfo.line2; - request.city = this.taxInfo.city; - request.state = this.taxInfo.state; - request.postalCode = this.taxInfo.postalCode; request.country = this.taxInfo.country; + request.postalCode = this.taxInfo.postalCode; + + if (this.taxInfo.includeTaxId) { + request.taxId = this.taxInfo.taxId; + request.line1 = this.taxInfo.line1; + request.line2 = this.taxInfo.line2; + request.city = this.taxInfo.city; + request.state = this.taxInfo.state; + } else { + request.taxId = null; + request.line1 = null; + request.line2 = null; + request.city = null; + request.state = null; + } return request; } else { const request = new TaxInfoUpdateRequest(); From 8bef0883f54bb0c8c8ac7017cad8f2c4c835cc69 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Thu, 28 Sep 2023 08:44:57 -0400 Subject: [PATCH 15/16] Individual Vault Item Encryption Feature (#6241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PM-1049 - TODO: replace base component with business service * updated server version * disabled cipher key encryption * add new storage to replace MasterKey with UserSymKey * add storage for master key encrypted user symmetric key * Begin refactor of crypto service to support new key structure * remove provided key from getKeyForUserEncryption * add decryption with MasterKey method to crypto service * update makeKeyPair on crypto service to be generic * add type to parameter of setUserKey in abstraction of crypto service * add setUserSymKeyMasterKey so we can set the encrypted user sym key from server * update cli with new crypto service methods - decrypt user sym key and set when unlocking * separate the user key in memory from user keys in storage * add new memory concept to crypto service calls in cli * update auth service to use new crypto service * update register component in lib to use new crypto service * update register component again with more crypto service * update sync service to use new crypto service methods * update send service to use new crypto service methods * update folder service to use new crypto service methods * update cipher service to use new crypto service * update password generation service to use new crypto service * update vault timeout service with new crypto service * update collection service to use new crypto service * update emergency access components to use new crypto service methods * migrate login strategies to new key model - decrypt and set user symmetric key if Master Key is available - rename keys where applicable - update unit tests * migrate pin to use user's symmetric key instead of master key - set up new state - migrate on lock component - use new crypto service methods * update pin key when the user symmetric key is set - always set the protected pin so we can recreate pin key from user symmetric key - stop using EncryptionPair in account - use EncString for both pin key storage - update migration from old strategy on lock component * set user symmetric key on lock component - add missed key suffix types to crypto service methods * migrate auto key - add helper to internal crypto service method to migrate * remove additional keys in state service clean * clean up the old pin keys in more flows - in the case that the app is updated while logged in and the user changes their pin, this will clear the old pin keys * finish migrate auto key if needed - migrate whenever retrieved from storage - add back the user symmetric key toggle * migrate biometrics key - migrate only on retrieval * fix crypto calls for key connector and vault timeout settings * update change password components with new crypto service * update assortment of leftover old crypto service calls * update device-crypto service with new crypto service * remove old EncKey methods from crypto service * remove clearEncKey from crypto service * move crypto service jsdoc to abstraction * add org key type and new method to build a data enc key for orgs * fix typing of bulk confirm component * fix EncString serialization issues & various fixes Co-authored-by: Matt Gibson * update account model with new keys serialization * migrate native messaging for biometrics to use new key model - support backwards compatibility - update safari web extension to send user key - add error handling * add early exit to native messaging flow for errors * improve error strings in crypto service * disable disk cache for browser due to bg script/popup race conditions * clear bio key when pin is migrated as bio is refreshed * share disk cache to fix syncing issues between contexts * check for ephemeral pin before process reload * remove state no longer needed and add JSDOC * fix linter * add new types to tests * remove cryptoMasterKeyB64 from account * fix tests imports * use master key for device approvals still * cleanup old TODOs, add missing crypto service parameters * fix cli crypto service calls * share disk cache between contexts on browser * Revert "share disk cache between contexts on browser" This reverts commit 56a590c4919f119cb1465eb7091a4384f5d90699. * use user sym key for account changing unlock verification * PM-1565 Added item key property to cipher export domain (#5580) * PM-1565 Added item key property to cipher export domain * enabled cipher key encryption * Updated getCipherKeyEncryptionEnabled validation to also return true if the serverVersion matches the minVersion * Using async/await when getting decrypted ciphers on getOrganizationDecryptedExport * Disabling CipherKey * add tests to crypto service * rename 'user symmetric key' with 'user key' * remove userId from browser crypto service * updated EncKey to UserKey where applicable * jsdoc deprecate account properties * use encrypt service in crypto service * use encrypt service in crypto service * require key in validateUserKey * check storage for user key if missing in memory * change isPinLockSet to union type * move biometric check to electron crypto service * add secondary fallback name for bio key for safari * migrate master key if found * pass key to encrypt service * rename pinLock to pinEnabled * use org key or user key for encrypting attachments * refactor makeShareKey to be more clear its for orgs * rename retrieveUserKeyFromStorage * clear deprecated keys when setting new user key * fix cipher service test * options is nullable while setting user key * Reordering Service creation on cli's bw.ts to fix ConfigApiService (#5684) * more crypto service refactors - check for auto key when getting user key - consolidate getUserKeyFromMemory and FromStorage methods - move bio key references out of base crypto service - update either pin key when setting user key instead of lock component - group deprecated methods - rename key legacy method * Feature/PM-1049 - TDEFflow 3 login decryption options - PR feedback changes (#5642) * PM-1049 - PR Feedback change - Browser - replace incorrect use of routerlink with manual attribute styling to keep anchor styling + tab focus while not having a router action race condition for the log out action to complete. * PM-1049 - PR Feedback - State Service changes - rename get/setAcctDecryptionOptions to get/setAccountDecryptionOptions * PM-1049 - PR Feedback changes - LoginDecryptionOptionsComp - Remove unncessary appA11yTitle directives as title / aria text would be identical to the displayed inner button text. * DeviceType - Create sets of device types which other components can reference to avoid having to manually define groups of device types. * PM-1049 - PR Feedback Changes - Update base-login-decryption-options component to leverage async piped observables per best practices. Updated all client templates to leverage new data streams. * PM-1049 - BaseLoginDecryptionOptionsComp - Add validation service for generic error handling * PM-1049 - DeviceResponse mistakenly had name as a number instead of a string * PM-1049 - First draft of creating observable based data store service for Devices so that the base login comp can leverage it instead of calling the devices API service directly (as it will be moved into the SDK in the future). * PM-1049 - Register new DevicesService on jslib-services module for use in components. * PM-1049 - Add new hasDevicesOfTypes call to devices data store svc + devices API service. * PM-1049 - BaseLoginDecryptionOptionsComp - wire up call to devicesService.hasDevicesOfTypes to replace getDevices() to avoid bringing down all trusted device information unnecessarily. * PM-1049 - LoginDecryptionOptionsComp - Web HTML - clean up loading state so it displays spinner centered properly. * PM-1049 - LoginDecryptionOptionsComp - Desktop HTML - Don't show login initiated title while page is loading to match other clients behavior. * PM-1049 - Devices Services - Update naming of hasDevicesOfTypes to match new name on back end + route change to getDevicesExistenseByTypes * PM-1049 - Device Response & View models - remove keys which are going to be deprecated on the base model * PM-1049 - DevicesService - devicesBSubject --> devicesSubject rename per PR feedback * PM-1049 - Devices Services - correct spelling of existence (*facepalm*) * PM-1049 - Update comment for clarity per PR feedback * PM-1049 - DevicesSvc - UserSymKey --> UserKey rename * PM-1049 - BaseLoginDecryptionOptions - replace user email source - get from stateService vs tokenService. * PM-1049 - BaseLoginDecryptionOptions - Remove uncessary check for userEmail as we will always have it here otherwise everything in the app is broken. * PM-1049 - BaseLoginDecryptionOptions - Finish cleaning up removal of user email from showReqAdminApprovalBtn$ stream * PM-1049 - LoginDecryptionOptionsComp - HTML revisions in web & browser to better space out buttons using tailwind or top margin to avoid need for multiple async pipes and shareReplay. * PM-1049 - DevicesService - of course all observables should have $ suffix. Facepalm. * PM-1049 - BaseLoginDecryptionOptionsComp - Update verbiage and style of destroy observable used for hooking into ngOnDestroy lifecycle to clean up all observables * PM-1049 - BaseLoginDecryptionOptions - PR feedback changes - refactor user email to have an underlying bSubject stream to ensure subscription/promise execution separately from the template async pipe subscribing to the stream. * PM-1049 - DevicesApiService - getDevicesExistenceByTypes - PR feedback - explicitly convert result to boolean instead of casting. * PM-1049 - BaseLoginDecryptionOptionsComp - Add ShareReplay for getAccountDecryptionOptions + context per PR feedback * PM-1049 - LoginDecryptionOptionsComp - Completely back away from template async pipe reactive approach as it caused massively increased complexity for little gain. Instead, just focus on reactively pulling asynchronously retrieved data and setting page loading state simply. This just works and is so much less overhead. + Add comments re flows of the component to be done later * PM-1049- Revert DevicesService implementation from smart data store cache service giant mess into simple, clean data passthrough service to avoid complexity and keep moving forward. YAGNI Co-authored-by: Andreas Coroiu * PM-1049 - DeviceCryptoService - Add decryptUserKey method (WIP) * PM-1049 - AccountDecryptionOptions - add get helpers for checking for trusted device / key connector decryption option existence. * PM-1049 - SSO Login Strategy - added comments in setUserKey method for where we will probably be consuming device keys and determining if the device is trusted or not (i.e., if we can get a decrypted user sym key in memory) * PM-1049 - DeviceCryptoSvc.decryptUserKey - Update method to properly use state service device key retrieval + add TODO to figure out what to do if user has previously had a device key and has cleared their local cache (which will result in the device being untrusted now) * PM-1049 - SSO Login Strategy - add comment re future passkey login strategy support * PM-2759 - SSO & 2FA components updated with v0 of navigation logic to send users to LoginDecryptionOptions * PM-1049 - Account > AccountDecryptionOptions - can't create getter helper methods for determining if user has decryption options b/c of issues w/ account deserialization. Moving past b/c I can just easily check if the given options are not undefined. * PM-2759 - Add TODOs for deprecation of id token response resetMasterPassword logic and replacement with use of accountDecryptionOptions --------- Co-authored-by: Andreas Coroiu * PM-2582 Fix adding attachments (#5692) * revert sharing disk cache between contexts * fix tests * PM-2791 Reordered service creation (#5701) * Turned off flag in production.json * add better tests to crypto service * add hack to get around duplicate instances of disk cache on browser * prevent duplicate cache deletes in browser * fix browser state service tests * Feature/PM-1212 - TDE - Approve with master password flow (#5706) * PM-1212 - StateSvc - Add getUserDeviceTrustChoice && setUserDeviceTrustChoice to persist user's choice in local storage in case of refresh on login approval screens (ex: lock) * PM-1212 - DeviceCryptoSvc - Add getUserDeviceTrustChoice && setUserDeviceTrustChoice as state service is lower level service for caching * PM-1212 - LoginDecryptionOptionsComp - Save result of rememberEmail checkbox into local storage via deviceCryptoService.setUserDeviceTrustChoice * PM-1212 - Lock component - after user key is set, check if user chose to establish trust, and if they did, then establish trust and reset choice. * PM-1212 - Update naming of methods per discussion with Jake + add comment explaining intended single use retrieval and need for resetting the value. * DeviceCryptoService - Refactor - decryptUserKey --> decryptUserKeyWithDeviceKey to match crypto service refactor naming convention * PM-1212 - Refactor State Service per PR feedback to store trustDeviceChoiceForDecryption on Account.settings b/c the temp setting is scoped to a user. * PM-2759 - SSO & 2FA Navigation to TDE Comp - Needs more work - Found scenarios on web with 2FA in which the expected navigation doesn't work. Adding TODO to assist in fixing * (1) Add Trust to DeviceCryptoService name (2) Move DeviceTrustCryptoService under auth folder * PM-1212 - Add tests for new getUserTrustDeviceChoiceForDecryption and setUserTrustDeviceChoiceForDecryption methods + TODOs for future tests. * PM-1212- Renaming / moving DeviceTrustCryptoService broke all the things - fixed all the client builds. * PM-1212- Copy doc comment to abstraction per PR feedback * PM-1212 - BaseLoginDecryptionOptions comp - remove unncessary cast to form control as apparently reactive forms now properly derives types. * [PM-1203] Replace MP confirmation with verification code (#5656) * [PM-1203] feat: ask for OTP if user does not have MP * [PM-1203] feat: add backwards compatibility for accounts/servers without decryption options * [PM-1203] feat: move hasMasterPassword to user-verification.service * [PM-1203] fix: remove duplicate implementation from crypto service * [PM-1203] fix: cli build * Tweak device trust crypto service implementation to match mobile late… (#5744) * Tweak device trust crypto service implementation to match mobile latest which results in more single responsibility methods * Update tests to match device trust crypto service implementation changes * update comment about state service * update pinLockType states and add jsdocs * add missed pinLockType changes * [PM-1033] Org invite user creation flow 1 (#5611) * [PM-1033] feat: basic redirection to login initiated * [PM-1033] feat: add ui for TDE enrollment * [PM-1033] feat: implement auto-enroll * [PM-1033] chore: add todo * [PM-1033] feat: add support in browser * [PM-1033] feat: add support for desktop * [PM-1033] feat: improve key check hack to allow regular accounts * [PM-1033] feat: init asymmetric account keys * [PM-1033] chore: temporary fix bug from merge * [PM-1033] feat: properly check if user can go ahead an auto-enroll * [PM-1033] feat: simplify approval required * [PM-1033] feat: rewrite using discrete states * [PM-1033] fix: clean-up and fix merge artifacts * [PM-1033] chore: clean up empty ng-container * [PM-1033] fix: new user identification logic * [PM-1033] feat: optimize data fetching * [PM-1033] feat: split user creating and reset enrollment * [PM-1033] fix: add missing loading false statement * [PM-1033] fix: navigation logic in sso component * [PM-1033] fix: add missing query param * [PM-1033] chore: rename to `ExistingUserUntrustedDevice` * PM-1033 - fix component templates to reference `ExistingUserUntrustedDevice` so clients can build --------- Co-authored-by: Jared Snider * remove extra partial key * set master key on lock component * rename key hash to password hash on crypto service * fix cli * rename enc user key setter in crypto service * Adds Events & Human Readable Messages (#5746) * [PM-1202] Hide the Master Password tab on Settings / Security (#5649) * [PM-1203] feat: ask for OTP if user does not have MP * [PM-1203] feat: get master password status from decryption options * [PM-1203] feat: add backwards compatibility for accounts/servers without decryption options * [PM-1203] feat: move hasMasterPassword to user-verification.service * fix merge issues * Change getUserTrustDeviceChoiceForDecryption / setUserTrustDeviceChoiceForDecryption to getShouldTrustDevice / setShouldTrustDevice (#5795) * Auth/[PM-1260] - Existing User - Login with Trusted Device (Flow 2) (#5775) * PM-1378 - Refactor - StateSvc.getDeviceKey() must actually convert JSON obj into instance of SymmetricCryptoKey * TODO: BaseLoginDecryptionOptionsComponent - verify new user check doesn't improperly pick up key connector users * PM-1260 - Add new encrypted keys to TrustedDeviceUserDecryptionOptionResponse * PM-1260 - DeviceTrustCryptoSvc - decryptUserKeyWithDeviceKey: (1) update method to optionally accept deviceKey (2) Return null user key when no device key exists (3) decryption of user key now works in the happy path * PM-1260 - LoginStrategy - SaveAcctInfo - Must persist device key on new account entity created from IdTokenResponse for TDE to work * PM-1260 - SSO Login Strategy - setUserKey refactor - (1) Refactor existing logic into trySetUserKeyForKeyConnector + setUserKeyMasterKey call and (2) new trySetUserKeyWithDeviceKey method for TDE * PM-1260 - Refactor DeviceTrustCryptoService.decryptUserKeyWithDeviceKey(...) - Add try catch around decryption attempts which removes device key (and trust) on decryption failure + warn. * PM-1260 - Account - Add deviceKey to fromJSON * TODO: add device key tests to account keys * TODO: figure out state service issues with getDeviceKey or if they are an issue w/ the account deserialization as a whole * PM-1260 - Add test suite for decryptUserKeyWithDeviceKey * PM-1260 - Add interfaces for server responses for UserDecryptionOptions to make testing easier without having to use the dreaded any type. * PM-1260 - SSOLoginStrategy - SetUserKey - Add check looking for key connector url on user decryption options + comment about future deprecation of tokenResponse.keyConnectorUrl * PM-1260 - SSO Login Strategy Spec file - Add test suite for TDE set user key logic * PM-1260 - BaseLoginStrategy - add test to verify device key persists on login * PM-1260 - StateService - verified that settings persist properly post SSO and it's just device keys we must manually instantiate into SymmetricCryptoKeys * PM-1260 - Remove comment about being unable to feature flag auth service / login strategy code due to circ deps as we don't need to worry about it b/c of the way we've written the new logic to be additive. * PM-1260 - DevicesApiServiceImplementation - Update constructor to properly use abstraction for API service * PM-1260 - Browser - AuthService - (1) Add new, required service factories for auth svc and (2) Update auth svc creation in main.background with new deps * PM-1260 - CLI - Update AuthSvc deps * PM-1260 - Address PR feedback to add clarity / match conventions * PM-1260 - Resolving more minor PR feedback * PM-1260 - DeviceTrustCryptoService - remove debug warn * PM-1378 - DeviceTrustCryptoSvc - TrustDevice - Fix bug where we only partially encrypted the user key with the device public key b/c I incorrectly passed userKey.encKey (32 bytes) instead of userKey.key (64 bytes) to the rsaEncrypt function which lead to an encryption type mismatch when decrypting the user's private key with the 32 byte decrypted user key obtained after TDE login. (Updated happy path test to prevent this from happening again) * PM-1260 - AccountKeys tests - add tests for deviceKey persistence and deserialization * PM-1260 - DeviceTrustCryptoSvc Test - tweak verbiage per feedback * PM-1260 - DeviceTrustCryptoSvc - Test verbiage tweak part 2 * Update apps/browser/src/background/service-factories/devices-api-service.factory.ts per PR feedback Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Defect - LockComp - After setting user key, must AWAIT retrieval of user's previous choice to have trusted the device or not. (#5804) * [PM-2928] [PM-2929] [PM-2930] Fixes for: [PM-1203] Replace MP confirmation with verification code (#5798) * [PM-2928] feat: hide change email if user doen't have MP * [PM-2929] feat: hide KDF settings if user doesn't have MP * [PM-2930] feat: remove MP copy * Removed self-hosted check from TDE SSO config. (#5837) * [PM-2998] Move Approving Device Check (#5822) * Switch to retrieving approving device from token response - Remove exist-by-types API call - Define `HasApprovingDevices` on TDE options * Update Naming * Update Test * Update Missing Names * [PM-2908] feat: show account created toast (#5810) * fix bug where we weren't passing MP on Restart to migrate method in lock * fix: buffer null error (#5856) * Auth/[pm-2759] - TDE - SSO and 2FA routing logic (#5829) * PM-2759 - SsoComp - (1) Temp remove all TDE routing logic (2) Refactor existing navigation logic via new component utility function navigateViaCallbackOrRoute * PM-2759 - SSO Component - Create test suite for logIn logic * PM-2759 - SsoComp Tests - add disclaimer regarding testing private methods and props * PM-1259 - SSO Comp - Refactor LogIn method to use functions for each navigation case for improved readability * PM-1259 - SSO Comp Tests - Add tests for error case during login + test for new handleLoginError logic * PM-2759 - SsoComp - Deprecate resetMasterPassword and replace with AccountDecryptionOptions logic + update tests * PM-2759 - SsoComp + tests - Add trusted device encryption first draft handling which has login success and force password reset handling * PM-2759 - Minor SsoComp comment and method name tweaks * PM-2759 - BaseTwoFactorComp - (1) Comment out TDE stuff for now (2) Add test suite (3) Replace global window in base comp constructor with angular injection token for window which follows best practices and allows for mocking so the comp can be unit tested * PM-2759 - Update child 2FA components to use angular injection token for window like base comp * PM-2759 - TwoFactorComp - Finish testing all logic in doSubmit * PM-2759 - TwoFactorComponent - Refactor DoSubmit method logic into multiple simple functions to make logic easier to follow * PM-2759 - Add newtrustedDeviceOption.hasManageResetPasswordPermission property to match server changes * PM-2759 - Flag AuthResult.resetMasterPassword property as deprecated * PM-2759 - SSO comp - TDE routing logic - User without MP and ResetPassword permission must set a MP * PM-2759 - Update Sso Comp tests to reflect additionally added TDE > MP set required logic (when user has no MP but they can reset other user passwords) * PM-2759 - SsoComp - Add comment explaining the happy paths better for TDE success navigation * PM-2759 - SsoComp - Refactor isTrustedDeviceEncEnabled logic into own method * PM-2759 - SsoComp - As the 2FA comp passes the org id through to each route, going to standardize on doing so across the board for now to avoid any tricky scenarios down the line where it is needed and it's not present * PM-2759 - SsoComp - Finish renaming orgIdFromState to orgIdentifier * PM-2759 - SsoComp - update tests for forcePasswordReset flows now passing orgIdentifier as query param * PM-2759 - SsoComp Tests - Export mockAcctDecryptionOpts permutations so we can share them across SsoComp and TwoFactorComp tests * PM-2759 - Refactor 2FA comp post login redirect logic to match SSO component + add TDE logic * PM-2759 - SsoComp - Refactor tests a bit for improved re-use * PM-2759 - Sso Comp tests - can't export consts from a spec file or the other spec files that import them will re-execute the whole test suite as a nested test suite. TIL. * PM-2759 - TwoFactorComp tests - All existing navigation scenarios + new TDE scenarios should now be tested. * PM-2759 - Web - 2FA comp - Fix build error b/c of renamed base comp prop (identifier --> orgIdentifier) * PM-2759 - Fix SsoLogin strategy tests b/c they were broken w/ the addition of the HasManageResetPasswordPermission prop to the TrustedDeviceOption interface * PM-2759 - Web TwoFactorComp - goAfterLogIn method must be an arrow function to inherit the parent base component scope so that important things like angular services can be defined. Web 2FA flow does not work without this being an arrow func. * PM-2759 - Fix typo * PM-2759 - SsoComp and TwoFactorComp tests - move service and other mocks into the top level before each to better ensure no crossover between test states per PR feedback * PM-2759 - SsoComp - add clarity by refactoring unclear comment * PM-2759 - SsoComp - Per excellent PR feedback, refactor if else statements to guard statements for better readability / design * PM-2759 - TwoFactorComp - Replace ifs with guard statements * PM-2759 - TwoFactorComp - add clarity to comment per PR feedback * PM-2759 - Replace use of jest.Mocked with MockProxy per PR feedback * PM-2759 - Use unknown over any per PR feedback * Bypass Master Password Reprompt if a user does not have a MP set (#5600) * Add a check for a master password in PasswordRepromptService.enabled() * Add tests for enabled() * Update state service method call * Use UserVerificationService to determine if a user has a master password * rename password hash to master key hash * fix cli build from key hash renaming * [PM-1339] Allow Rotating Device Keys (#5806) * Merge remote-tracking branch 'origin/feature/trusted-device-encryption' into Auth/pm-1339/rotate-device-keys * Implement Rotation of Current Device Keys - Detects if you are on a trusted device - Will rotate your keys of only this device - Allows you to still log in through SSO and decrypt your vault because the device is still trusted * Address PR Feedback * Move Files to Auth Ownership * fix: getOrgKeys returning null * [PM-3143] Trusted device encryption: Refactor reset enroll service (#5869) * create new reset enrollment service * refactor: login decryption options according to TODO * feat: add tests * PM-3143 - Add override to overriden methods --------- Co-authored-by: Jared Snider * generate a master key from master password if needed (#5870) * [PM-3120] fix: device key not being saved properly (#5882) * pm-2582 Moved code to cipher service (#5818) * Auth/pm 1050/pm 1051/remaining tde approval flows (#5864) * fix: remove `Unauth guard` from `/login-with-device` * Turned encryption on (#5908) * [PM-3101] Fix autofill items not working for users without a master password (#5885) * Add service factories for user verification services * Update autofill service to check for existence of master password for autofill * Update the context menu to check for existence of master password for autofill * context menu test fixes * [PM-3210] fix: use back navigation (#5907) * Removed buttons (#5935) * PM-2759 - Fix broken backwards compatibility for authResult.resetMast… (#5940) * PM-2759 - Fix broken backwards compatibility for authResult.resetMasterPassword * PM-2759 - Update TODO with specific tech debt task + target release date * TDE - State Svc - setDeviceKey should support setting null for future support of clearing device key. (#5942) * Check if a user has a mp before showing kdf warning (#5929) * [PM-1200] Unlock settings changes for accounts without master password - clients (#5894) * [PM-1200] chore: add comment for jake * [PM-1200] chore: rename to `vault-timeout` * [PM-1200] feat: initial version of `getAvailableVaultTimeoutActions` * [PM-1200] feat: implement `getAvailableVaultTimeoutActions` * [PM-1200] feat: change helper text if only logout is available * [PM-1200] feat: only show available timeout actions * [PM-1200] fix: add new service factories and dependencies * [PM-1200] fix: order of dependencies `UserVerificationService` is needed by `VaultTimeoutSettingsService` * [PM-1200] feat: add helper text if no lock method added * [PM-1200] refactor: simplify prev/new values when changing timeout and action * [PM-1200] feat: fetch timeout action from new observable * [PM-1200] refactor: make `getAvailableVaultTimeoutActions` private * [PM-1200] feat: add test cases for `vaultTimeoutAction$` * [PM-1200] feat: implement new timeout action logic * [PM-1200] feat: add dynamic lock options to browser * [PM-1200] feat: enable/disable action select * [PM-1200] feat: add support for biometrics * [PM-1200] feat: add helper text and disable unavailable options * [PM-1200] feat: update action on unlock method changes * [PM-1200] feat: update browser to use async pipe * [PM-1200] fix: element not updating * [PM-1200] feat: hide masterPassOnRestart pin option * [PM-1200] feat: hide change master password from browser settins * [PM-1200] feat: hide change master password from app menu * [PM-1200] feat: logout if lock is not supported * [PM-1200] feat: auto logout from lock screen if unlocking is not supported * [PM-1200] feat: remove lock button from web menus * Revert "[PM-1200] fix: element not updating" This reverts commit b27f425f48570d0d5dbc9dedb9797023fef64d8b. * Revert "[PM-1200] feat: update browser to use async pipe" This reverts commit 766c15bc3dbadcf7dcef3053b148e7874f8939ce. * [PM-1200] chore: add comment regarding detectorRef * [PM-1200] feat: remove lock now button from browser settings * [PM-1200] feat: add `userId` to unlock settings related methods * [PM-1200] feat: remove non-lockable accounts from menu * [PM-1200] fix: cli not building --------- Co-authored-by: Todd Martin Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> * [PM-3215][PM-3289] Create MasterKey from Password If Needed (#5931) * Create MasterKey from Password - Check if the MasterKey is stored or not - Create it if it's not * Add getOrDeriveKey Helper * Use Helper In More Places * Changed settings menu to be enabled whenever the account is not locked. (#5965) * [PM-3169] Login decryption options in extension popup (#5909) * [PM-3169] refactor: lock guard and add new redirect guard * [PM-3169] feat: implement fully rewritten routing * [PM-3169] feat: close SSO window * [PM-3169] feat: store sso org identifier in state * [PM-3169] fix: tests * [PM-3169] feat: get rid of unconventional patch method * PM-3169 - SSO & 2FA Comps - Update naming of new callback to match existing pattern + add tests for callback logic execution. * PM-3169 - Update LockGuard to have a special exception for allowing the TDE Login with MP flow * PM-3169 - Per discussion w/ Jake and Justin, rename login-initiated guard to be tde decryption required guard (more named for functionality vs specific route) * PM-3169 - Add some additional context to new redirect guard scenario * PM-3169 - Per PR feedback, replace all callback types with Promise as the return values are not being used. * PM-3169 - StateSvc - Per PR feedback, update setUserSsoOrganizationIdentifier signature to explicitly use null instead of partial which doesn't do anything * PM-3169 - Replace onSuccessfulLogin type to compile * PM-3169 - Add clarification comment for why we are not using a query param for persisting the org identifier * PM-3169 - Per discussion with Justin, only use memory for SsoOrgId as we don't need to persist it beyond that; tested and it worked on all 3 clients for new user TDE creation * PM-3169 - Add missing ssoIdentifierRequired translation to desktop and browser * PM-3169 - After discussing with Justin again, we realized that memory doesn't work on desktop if user refreshes app or closes and re-opens it so must use disk. * PM-3169 - Per PR feedback, remove hasEverHadUserKey logic as we can just leverage existing getUserKey method to check if we have a user key or not; tested all guards in browser and web with no issues * PM-3169 - Per design discussion with Danielle, move account created toast after successful account creation vs on load of page. --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jared Snider * [PM-3314] Fixed missing MP prompt on lock component (#5966) * Updated lock component to handle no master password. * Added a comment. * Add Missing Slash (#5967) * Fix AdminAuthRequest Serialization on Desktop (#5970) - toJSON isn't being called by ElectronStorageService - Force it's conversion to JSON earlier so it happens for all storage methods * Fix issue where we were incorrectly calling setRememberEmailValues in the AdminAuthRequest state - no need to do this as the email is already saved to state. By calling this method, we would actually overwrite the already saved email with null as the user's choice to remember email wasn't persisted through SSO on the login service. (#5972) * PM-3329 - Restore everHadUserKey logic from PM-3169 which I incorrectly removed in order to fix routing logic so that user can lock and land on the lock screen properly (#5979) * PM-3210 - TDE - LoginWithDevice routing fix - Mirror PR #5950 in just simply providing a back action on click which works for all app generated scenarios (#5982) * PM-3332 - TDE - SsoLoginStrategy - For existing admin auth reqs, must… (#5980) * PM-3332 - TDE - SsoLoginStrategy - For existing admin auth reqs, must manually handle 404 error case to prevent app from hanging and clear the local state if the admin auth req in the DB has been purged; i.e., it should fail silently. * Add TODO for SSO Login Strategy tests * PM-3331 - TDE - Firefox - Browser extension - fix access denied error… (#5984) * PM-3331 - TDE - Firefox - Browser extension - fix access denied error on popup load which was caused by the canAccessFeature guard failing to lookup the TDE feature flag as the server config was returning null even after a successful server call as only returned the value if the user was unauthenticated for some reason * PM-3331 - After discussion with Andre, further refactor ConfigService logic to always return the latest information from the server so that requests for feature flag data will always get the most up to date information. * PM-3345 - TDE - Desktop - Biometrics setting submenu tweak - do not s… (#5988) * PM-3345 - TDE - Desktop - Biometrics setting submenu tweak - do not show require MP or PIN entry on restart if user doesn't have at least one of those options b/c otherwise user can get into a bad state where they cannot unlock * PM-3345 - TDE - Desktop - Settings comp - if user turns off PIN and Biometric is on + require PIN on restart is enabled then must turn that setting off to prevent bad user state * PM-3345 - Final tweak to logic * [PM-2852] Final merge from Key Migration branch to TDE Feature Branch (#5977) * [PM-3121] Added new copy with exclamation mark * [PM 3219] Fix key migration locking up the Desktop app (#5990) * Only check to migrate key on VaultTimeout startup * Remove desktop specific check * PM-3332 - LoginWithDevice - Add error handling logic around admin auth request retrieval similar to sso login strategy to prevent error state and allow re-creation of an admin auth request if it has been purged from the server for whatever reason. (#5991) * PM-3355 - TDE - Browser JIT Account Creation - Browser create user logic still had logic for simply closing the extension tab but as we no longer open the login decryption options in a tab we needed to update the logic here to navigate the user directly onto the vault. (#5993) * Add distinctUntilChanged to fix multiple value changes for biometrics firing (#5999) * Add optional chaining to master key (#6007) * PM-3369 - TDE - Persist user's choice to trust device to state when user ma… (#6000) * PM-3369 - Persist user's choice to trust device to state when user makes choice + persist previous choices out of state * PM-3369 - Must set trust device in state on load if it's never been set before * PM-3369 - Refactor BaseLoginDecOptions to properly set trust device choice in state on load * Update libs/angular/src/auth/components/base-login-decryption-options.component.ts Co-authored-by: Jake Fink --------- Co-authored-by: Jake Fink * Updated email change component to getOrDeriveMasterKey (#6009) * [PM-3330] Force Update to Lockable Accounts on PIN/Biometric Update (#6006) * Add Listener For Events that Need To Redraw the Menu * Send redrawMenu Message When Pin/Biometrics Updated * DeviceTrustCryptoService - don't worry about checking if a device should establish trust or not if the user doesn't have trusted device encryption on (#6010) * Auth / pm 3351 / TDE Login - Browser & Desktop vault sync issue fix (#6002) * PM-3351 - TDE Login on desktop and browser via SSO comp with no 2FA should trigger sync like standard onSuccessfulLogin process used to so user lands on vault with data. * PM-3351 - 2FA Comp - Refactor onSuccessfulLogin logic to only execute in the success path just like the SSO component + adding specific onSuccessfulLoginTde flow just like SSO comp. + removed unnecessary calls to loginService.clearValues(). Added browser & desktop definitions for onSuccessfulLoginTde which is just a fullSync kick off. * TODO * PM-3351 - remove await to restore code back to previous state without hang. * PM-3351 - 2FA Comp - Don't await onSuccessfulLoginTde b/c it causes a hang * PM-3351 - remove sso comp incorrect todo * PM-3351 - SsoComp - don't await onSuccessfulLoginTde for browsers sake * PM-3351 - SsoComp - remove awaits from onSuccessfulLoginTde and onSuccessfulLogin to avoid any hangs on desktop and browser * PM-3351 - Convert onSuccessfulLoginTde to promise as its return is not used + refactor all to be consistent and clearly communciate that the sync won't be awaited. * PM-3351 - Convert onSuccessfulLogin to promise and update all methods accordingly to more clearly indicate that the syncs and any other logic won't be awaited. * [PM-3356] Fallback to OTP When MasterPassword Hasn't Been Used (#6017) * Fallback to OTP When MasterPassword Hasn't Been Used * Update Test and Rename Method * Revert "DeviceTrustCryptoService - don't worry about checking if a device should establish trust or not if the user doesn't have trusted device encryption on (#6010)" (#6020) This reverts commit 6ec22f95702050c12716f79c7d7454835f9b2807. * PM-3390 - TDE - Redraw desktop after user creation to update isLocked checks and get menu to be enabled properly (#6018) * [PM-3383] Hide Change Password menu option for user with no MP (#6022) * Hide Change Master Password menu item on desktop when a user doesn't have a master password. * Renamed variable for consistency. * Updated to base logic on account. * Fixed menubar * Resolve merge errors in crypto service spec * Fixed autofill to use new method on userVerificationService (#6029) * conflict resolution * missing file * PM-3456 - TDE Admin Auth Req Flow - FF dead object issue - The foreground popup must retrieve the long lived background services for the new TDE services (the AuthRequestCryptoService service fixes this issue, but the DeviceTrustCryptoService should have been added to services.module as well) (#6037) * skip auto key check when using biometrics on browser (#6041) * Added comments for backward compatibility removal. (#6039) * Updated warning message. (#6059) * Tde pr feedback (#6051) * move pin migration to the crypto service * refactor config service logic * refactor lock component load logic * rename key connector methods * add date to backwards compat todo * update backwards compat todo * don't specify defaults in redirectGuard * nit * add null & undefined check for userid before using the account * fix ui tests * add todo for tech debt * add todo comment * Fix storybook per PR feedback * Desktop & Browser - lock comp - add optional chaining check for focusable input - user can just have biometric and not have a MP or a PIN so must support that. * Main.background.ts - remove duplicate instantiations of the userVerificationApiService and userVerificationService which were added in two separate PRs * Per PR feedback - (1) Browser app routing module - fix incorrect import for redirect guard (2) Created index.ts file for auth guards to simplify imports and updated imports * Per PR feedback, (1) Update jslib-services.module to provide actual instance of VaultTimeoutService (2) Update init service to use concrete VaultTimeoutService vs abstraction. Co-authored-by: Matt Gibson * Per PR feedback - update services module AuthRequestCryptoService and DeviceTrustCryptoService to use shorthand format. * Per PR feedback, add devicesService to main background and update services module to ensure the popup leverages the background devicesService --------- Co-authored-by: Jared Snider Co-authored-by: Matt Gibson * Updated message keys for CrowdIn to pick them up. (#6066) * TDE PR Feedback resolutions round 2 (#6068) * Per PR feedback - main.background.ts - move userVerificationService and userVerificationApiService to correct location * Per PR feedback - JS lib services + vault timeout service updates - (1) Correctly type callbacks based on injection tokens (2) Update vault timeout service to have proper types based on injection tokens * Per PR Feedback - update web init service to inject actual VaultTimeoutService vs abstraction similar to what we did for desktop here: https://github.com/bitwarden/clients/commit/55a797d4ff571a1942686a32fdcbb5ad0311b5ae * Per more feedback - revert incorrect changes to VaultTimeoutService based on existing injection token types for LOGOUT_CALLBACK and LOCKED_CALLBACK.. and instead update the injection token types themselves to match how they are being used. * Per PR feedback - in browser main.background.ts, inject concrete VaultTimeoutService instead of abstraction so we don't have to cast it anymore (matching web & desktop) * Conflict resolution * PM-2669 Added missing changes from conflict resolution * Turn cipher encryption on for testing purposes * Bumped up minimum version * Turn off cipher key encryption * Converted to jest-mock-extended and removed dependency * Remove key from cipher view * Added comment to Cipher for future refactoring (#6175) * Remove ConfigApiServiceAbstraction from popup services (#6174) * Replaced null orgId. (#6208) * Added reference to new aesGenerateKey function. (#6222) * Updated server version and feature flag for QA smoke tests. * [PM-2814] Add ConfigService to CipherService (#6239) * Updated CipherService to use ConfigService Updated version check. * Added missing DI for CLI. * Updated parameter name for consistency. * Addressed use of options pattern in config-service.factory.ts. * Added CLI initialization. (#6266) * Updated checkServerMeetsVersionRequirement to use observable (#6270) * [PM-2814] Handle key rotation missing key (#6267) * Fixed issue with key rotation * Updates to CipherService to handle not having key on the model. * More refactoring. * Updated abstraction to remove private method. * Fixed test. * Updated test to reflect the fact that we set key to null. * Resolved merge conflicte with logService added in master. * Updated Mv3 factory include log service from merge in ConfigService initialization. * Fixed another merge conflict with ConfigService to add logService dependency. * Disable configService timer for cli (#6319) The rxjs timer() function keeps the node process alive and stops it from exiting. CLI should not run long enough to actually use the timer, so just remove it. * [PM-3978] Handle sharing with org with cipher key encryption (#6370) * Added explicit parameters to encrypt to handle org sharing. * Updated add-edit to handle new parameter to encrypt * Updated minimum server version for QA testing. * Updated minimum version to `2023.8.0` and turned off cipher encryption for QA. * Updated minimum server version in preparation for release. * [PM-2669] PR review changes (#6415) * Addressed PR feedback. * Added comments and renamed parameters for clarity. * Updated vault export to keep immediate invocation and reformat for clarity. Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> * Updated comment. * Removed async that was left on saveCipherAttachment accidentally. --------- Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> --------- Co-authored-by: Jared Snider Co-authored-by: gbubemismith Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Co-authored-by: Jacob Fink Co-authored-by: Matt Gibson Co-authored-by: Andreas Coroiu Co-authored-by: Carlos Gonçalves Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Co-authored-by: Andreas Coroiu Co-authored-by: Andreas Coroiu Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: André Bispo Co-authored-by: Thomas Rittson Co-authored-by: Vincent Salucci Co-authored-by: Robyn MacCallum Co-authored-by: Jonathan Prusik Co-authored-by: Matt Gibson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- apps/browser/config/base.json | 3 +- apps/browser/config/development.json | 3 +- apps/browser/config/production.json | 4 +- .../background/notification.background.ts | 2 +- .../browser/src/background/main.background.ts | 74 ++--- .../config-api.service.factory.ts | 32 ++ .../config-service.factory.ts | 49 +++ .../cipher-service.factory.ts | 12 +- apps/cli/config/development.json | 4 +- apps/cli/config/production.json | 4 +- .../admin-console/commands/share.command.ts | 8 +- apps/cli/src/bw.ts | 58 ++-- apps/cli/src/commands/edit.command.ts | 12 +- apps/cli/src/commands/get.command.ts | 4 +- .../platform/services/cli-config.service.ts | 9 + apps/cli/src/vault/create.command.ts | 8 +- apps/desktop/config/base.json | 3 +- apps/desktop/config/development.json | 3 +- apps/desktop/config/production.json | 3 +- apps/desktop/src/locales/en/messages.json | 2 +- .../encrypted-message-handler.service.ts | 4 +- apps/web/config/base.json | 3 +- apps/web/config/cloud.json | 3 +- apps/web/config/development.json | 3 +- apps/web/config/euprd.json | 3 +- apps/web/config/qa.json | 3 +- apps/web/config/selfhosted.json | 3 +- .../app/vault/org-vault/add-edit.component.ts | 2 +- .../components/collections.component.ts | 4 +- .../angular/src/components/share.component.ts | 8 +- .../src/services/jslib-services.module.ts | 7 +- .../vault/components/add-edit.component.ts | 4 +- .../vault/components/attachments.component.ts | 12 +- .../components/password-history.component.ts | 4 +- .../src/vault/components/view.component.ts | 4 +- .../common/src/models/export/cipher.export.ts | 3 + .../config/config.service.abstraction.ts | 4 + .../platform/abstractions/crypto.service.ts | 6 + .../interfaces/decryptable.interface.ts | 2 +- libs/common/src/platform/misc/flags.ts | 1 + .../models/domain/symmetric-crypto-key.ts | 1 + .../services/config/config.service.ts | 25 +- .../src/platform/services/crypto.service.ts | 6 + .../src/vault/abstractions/cipher.service.ts | 4 +- .../src/vault/models/data/cipher.data.ts | 2 + .../src/vault/models/domain/cipher.spec.ts | 79 ++++- libs/common/src/vault/models/domain/cipher.ts | 24 +- .../vault/models/request/cipher.request.ts | 2 + .../vault/models/response/cipher.response.ts | 2 + .../src/vault/services/cipher.service.spec.ts | 98 +++++- .../src/vault/services/cipher.service.ts | 282 ++++++++++++------ .../services/vault-export.service.ts | 11 +- ...warden-password-protected-importer.spec.ts | 4 + .../bitwarden/bitwarden-json-importer.ts | 8 +- .../bitwarden-password-protected-importer.ts | 5 +- libs/importer/src/services/import.service.ts | 1 + 56 files changed, 729 insertions(+), 205 deletions(-) create mode 100644 apps/browser/src/platform/background/service-factories/config-api.service.factory.ts create mode 100644 apps/browser/src/platform/background/service-factories/config-service.factory.ts create mode 100644 apps/cli/src/platform/services/cli-config.service.ts diff --git a/apps/browser/config/base.json b/apps/browser/config/base.json index 81b11cd38b7..348f00d1f36 100644 --- a/apps/browser/config/base.json +++ b/apps/browser/config/base.json @@ -1,6 +1,7 @@ { "dev_flags": {}, "flags": { - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/browser/config/development.json b/apps/browser/config/development.json index 972812a9c59..eafd0ffd878 100644 --- a/apps/browser/config/development.json +++ b/apps/browser/config/development.json @@ -6,6 +6,7 @@ } }, "flags": { - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/browser/config/production.json b/apps/browser/config/production.json index b04d1531a2f..f57c3d9bc38 100644 --- a/apps/browser/config/production.json +++ b/apps/browser/config/production.json @@ -1,3 +1,5 @@ { - "flags": {} + "flags": { + "enableCipherKeyEncryption": false + } } diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 1cb006fa3a2..73bdc2cd16f 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -464,7 +464,7 @@ export default class NotificationBackground { private async getDecryptedCipherById(cipherId: string) { const cipher = await this.cipherService.get(cipherId); if (cipher != null && cipher.type === CipherType.Login) { - return await cipher.decrypt(); + return await cipher.decrypt(await this.cipherService.getKeyForCipherKeyDecryption(cipher)); } return null; } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 3aba0c77679..7a9863f3a70 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -322,23 +322,6 @@ export default class MainBackground { ); this.searchService = new SearchService(this.logService, this.i18nService); - this.cipherService = new CipherService( - this.cryptoService, - this.settingsService, - this.apiService, - this.i18nService, - this.searchService, - this.stateService, - this.encryptService, - this.cipherFileUploadService - ); - this.folderService = new BrowserFolderService( - this.cryptoService, - this.i18nService, - this.cipherService, - this.stateService - ); - this.folderApiService = new FolderApiService(this.folderService, this.apiService); this.collectionService = new CollectionService( this.cryptoService, this.i18nService, @@ -362,14 +345,6 @@ export default class MainBackground { this.cryptoFunctionService, logoutCallback ); - this.vaultFilterService = new VaultFilterService( - this.stateService, - this.organizationService, - this.folderService, - this.cipherService, - this.collectionService, - this.policyService - ); this.passwordStrengthService = new PasswordStrengthService(); @@ -436,6 +411,36 @@ export default class MainBackground { this.userVerificationApiService ); + this.configApiService = new ConfigApiService(this.apiService, this.authService); + + this.configService = new BrowserConfigService( + this.stateService, + this.configApiService, + this.authService, + this.environmentService, + this.logService, + true + ); + + this.cipherService = new CipherService( + this.cryptoService, + this.settingsService, + this.apiService, + this.i18nService, + this.searchService, + this.stateService, + this.encryptService, + this.cipherFileUploadService, + this.configService + ); + this.folderService = new BrowserFolderService( + this.cryptoService, + this.i18nService, + this.cipherService, + this.stateService + ); + this.folderApiService = new FolderApiService(this.folderService, this.apiService); + this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService( this.cryptoService, this.tokenService, @@ -444,6 +449,15 @@ export default class MainBackground { this.userVerificationService ); + this.vaultFilterService = new VaultFilterService( + this.stateService, + this.organizationService, + this.folderService, + this.cipherService, + this.collectionService, + this.policyService + ); + this.vaultTimeoutService = new VaultTimeoutService( this.cipherService, this.folderService, @@ -533,16 +547,6 @@ export default class MainBackground { this.messagingService ); - this.configApiService = new ConfigApiService(this.apiService, this.authService); - - this.configService = new BrowserConfigService( - this.stateService, - this.configApiService, - this.authService, - this.environmentService, - this.logService, - true - ); this.browserPopoutWindowService = new BrowserPopoutWindowService(); const systemUtilsServiceReloadCallback = () => { diff --git a/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts b/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts new file mode 100644 index 00000000000..e9e1b86488a --- /dev/null +++ b/apps/browser/src/platform/background/service-factories/config-api.service.factory.ts @@ -0,0 +1,32 @@ +import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; +import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; + +import { + authServiceFactory, + AuthServiceInitOptions, +} from "../../../auth/background/service-factories/auth-service.factory"; + +import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory"; +import { FactoryOptions, CachedServices, factory } from "./factory-options"; + +type ConfigApiServiceFactoyOptions = FactoryOptions; + +export type ConfigApiServiceInitOptions = ConfigApiServiceFactoyOptions & + ApiServiceInitOptions & + AuthServiceInitOptions; + +export function configApiServiceFactory( + cache: { configApiService?: ConfigApiServiceAbstraction } & CachedServices, + opts: ConfigApiServiceInitOptions +): Promise { + return factory( + cache, + "configApiService", + opts, + async () => + new ConfigApiService( + await apiServiceFactory(cache, opts), + await authServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/platform/background/service-factories/config-service.factory.ts b/apps/browser/src/platform/background/service-factories/config-service.factory.ts new file mode 100644 index 00000000000..a5dc6016c65 --- /dev/null +++ b/apps/browser/src/platform/background/service-factories/config-service.factory.ts @@ -0,0 +1,49 @@ +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; + +import { + authServiceFactory, + AuthServiceInitOptions, +} from "../../../auth/background/service-factories/auth-service.factory"; + +import { configApiServiceFactory, ConfigApiServiceInitOptions } from "./config-api.service.factory"; +import { + environmentServiceFactory, + EnvironmentServiceInitOptions, +} from "./environment-service.factory"; +import { FactoryOptions, CachedServices, factory } from "./factory-options"; +import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; +import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; + +type ConfigServiceFactoryOptions = FactoryOptions & { + configServiceOptions?: { + subscribe?: boolean; + }; +}; + +export type ConfigServiceInitOptions = ConfigServiceFactoryOptions & + StateServiceInitOptions & + ConfigApiServiceInitOptions & + AuthServiceInitOptions & + EnvironmentServiceInitOptions & + LogServiceInitOptions; + +export function configServiceFactory( + cache: { configService?: ConfigServiceAbstraction } & CachedServices, + opts: ConfigServiceInitOptions +): Promise { + return factory( + cache, + "configService", + opts, + async () => + new ConfigService( + await stateServiceFactory(cache, opts), + await configApiServiceFactory(cache, opts), + await authServiceFactory(cache, opts), + await environmentServiceFactory(cache, opts), + await logServiceFactory(cache, opts), + opts.configServiceOptions?.subscribe ?? true + ) + ); +} diff --git a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts index 006daadc1af..46062ebc9cc 100644 --- a/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts +++ b/apps/browser/src/vault/background/service_factories/cipher-service.factory.ts @@ -18,8 +18,12 @@ import { ApiServiceInitOptions, } from "../../../platform/background/service-factories/api-service.factory"; import { - CryptoServiceInitOptions, + configServiceFactory, + ConfigServiceInitOptions, +} from "../../../platform/background/service-factories/config-service.factory"; +import { cryptoServiceFactory, + CryptoServiceInitOptions, } from "../../../platform/background/service-factories/crypto-service.factory"; import { EncryptServiceInitOptions, @@ -49,7 +53,8 @@ export type CipherServiceInitOptions = CipherServiceFactoryOptions & I18nServiceInitOptions & SearchServiceInitOptions & StateServiceInitOptions & - EncryptServiceInitOptions; + EncryptServiceInitOptions & + ConfigServiceInitOptions; export function cipherServiceFactory( cache: { cipherService?: AbstractCipherService } & CachedServices, @@ -68,7 +73,8 @@ export function cipherServiceFactory( await searchServiceFactory(cache, opts), await stateServiceFactory(cache, opts), await encryptServiceFactory(cache, opts), - await cipherFileUploadServiceFactory(cache, opts) + await cipherFileUploadServiceFactory(cache, opts), + await configServiceFactory(cache, opts) ) ); } diff --git a/apps/cli/config/development.json b/apps/cli/config/development.json index b04d1531a2f..f57c3d9bc38 100644 --- a/apps/cli/config/development.json +++ b/apps/cli/config/development.json @@ -1,3 +1,5 @@ { - "flags": {} + "flags": { + "enableCipherKeyEncryption": false + } } diff --git a/apps/cli/config/production.json b/apps/cli/config/production.json index b04d1531a2f..f57c3d9bc38 100644 --- a/apps/cli/config/production.json +++ b/apps/cli/config/production.json @@ -1,3 +1,5 @@ { - "flags": {} + "flags": { + "enableCipherKeyEncryption": false + } } diff --git a/apps/cli/src/admin-console/commands/share.command.ts b/apps/cli/src/admin-console/commands/share.command.ts index 68bd8a18056..88fe256af18 100644 --- a/apps/cli/src/admin-console/commands/share.command.ts +++ b/apps/cli/src/admin-console/commands/share.command.ts @@ -45,11 +45,15 @@ export class ShareCommand { if (cipher.organizationId != null) { return Response.badRequest("This item already belongs to an organization."); } - const cipherView = await cipher.decrypt(); + const cipherView = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); try { await this.cipherService.shareWithServer(cipherView, organizationId, req); const updatedCipher = await this.cipherService.get(cipher.id); - const decCipher = await updatedCipher.decrypt(); + const decCipher = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher) + ); const res = new CipherResponse(decCipher); return Response.success(res); } catch (e) { diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 42ba158ee72..ca501690193 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -25,11 +25,13 @@ import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.ser import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; import { ClientType, KeySuffixOptions, LogLevelType } from "@bitwarden/common/enums"; +import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Account } from "@bitwarden/common/platform/models/domain/account"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { AppIdService } from "@bitwarden/common/platform/services/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/services/broadcaster.service"; +import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; @@ -75,6 +77,7 @@ import { } from "@bitwarden/importer"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; +import { CliConfigService } from "./platform/services/cli-config.service"; import { CliPlatformUtilsService } from "./platform/services/cli-platform-utils.service"; import { ConsoleLogService } from "./platform/services/console-log.service"; import { I18nService } from "./platform/services/i18n.service"; @@ -147,6 +150,8 @@ export class Main { devicesApiService: DevicesApiServiceAbstraction; deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; authRequestCryptoService: AuthRequestCryptoServiceAbstraction; + configApiService: ConfigApiServiceAbstraction; + configService: CliConfigService; constructor() { let p = null; @@ -252,28 +257,8 @@ export class Main { this.searchService = new SearchService(this.logService, this.i18nService); - this.cipherService = new CipherService( - this.cryptoService, - this.settingsService, - this.apiService, - this.i18nService, - this.searchService, - this.stateService, - this.encryptService, - this.cipherFileUploadService - ); - this.broadcasterService = new BroadcasterService(); - this.folderService = new FolderService( - this.cryptoService, - this.i18nService, - this.cipherService, - this.stateService - ); - - this.folderApiService = new FolderApiService(this.folderService, this.apiService); - this.collectionService = new CollectionService( this.cryptoService, this.i18nService, @@ -349,6 +334,38 @@ export class Main { this.authRequestCryptoService ); + this.configApiService = new ConfigApiService(this.apiService, this.authService); + + this.configService = new CliConfigService( + this.stateService, + this.configApiService, + this.authService, + this.environmentService, + this.logService, + true + ); + + this.cipherService = new CipherService( + this.cryptoService, + this.settingsService, + this.apiService, + this.i18nService, + this.searchService, + this.stateService, + this.encryptService, + this.cipherFileUploadService, + this.configService + ); + + this.folderService = new FolderService( + this.cryptoService, + this.i18nService, + this.cipherService, + this.stateService + ); + + this.folderApiService = new FolderApiService(this.folderService, this.apiService); + const lockedCallback = async (userId?: string) => await this.cryptoService.clearStoredUserKey(KeySuffixOptions.Auto); @@ -472,6 +489,7 @@ export class Main { const locale = await this.stateService.getLocale(); await this.i18nService.init(locale); this.twoFactorService.init(); + this.configService.init(); const installedVersion = await this.stateService.getInstalledVersion(); const currentVersion = await this.platformUtilsService.getApplicationVersion(); diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index 960b0999146..60e5ee7936e 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -77,7 +77,9 @@ export class EditCommand { return Response.notFound(); } - let cipherView = await cipher.decrypt(); + let cipherView = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); if (cipherView.isDeleted) { return Response.badRequest("You may not edit a deleted item. Use the restore command first."); } @@ -86,7 +88,9 @@ export class EditCommand { try { await this.cipherService.updateWithServer(encCipher); const updatedCipher = await this.cipherService.get(cipher.id); - const decCipher = await updatedCipher.decrypt(); + const decCipher = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher) + ); const res = new CipherResponse(decCipher); return Response.success(res); } catch (e) { @@ -109,7 +113,9 @@ export class EditCommand { try { await this.cipherService.saveCollectionsWithServer(cipher); const updatedCipher = await this.cipherService.get(cipher.id); - const decCipher = await updatedCipher.decrypt(); + const decCipher = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher) + ); const res = new CipherResponse(decCipher); return Response.success(res); } catch (e) { diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 265e24c9d4e..4a84f1efefd 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -103,7 +103,9 @@ export class GetCommand extends DownloadCommand { if (Utils.isGuid(id)) { const cipher = await this.cipherService.get(id); if (cipher != null) { - decCipher = await cipher.decrypt(); + decCipher = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); } } else if (id.trim() !== "") { let ciphers = await this.cipherService.getAllDecrypted(); diff --git a/apps/cli/src/platform/services/cli-config.service.ts b/apps/cli/src/platform/services/cli-config.service.ts new file mode 100644 index 00000000000..6faa1b12e8a --- /dev/null +++ b/apps/cli/src/platform/services/cli-config.service.ts @@ -0,0 +1,9 @@ +import { NEVER } from "rxjs"; + +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; + +export class CliConfigService extends ConfigService { + // The rxjs timer uses setTimeout/setInterval under the hood, which prevents the node process from exiting + // when the command is finished. Cli should never be alive long enough to use the timer, so we disable it. + protected refreshTimer$ = NEVER; +} diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts index 3f4d53e9737..01217dbc307 100644 --- a/apps/cli/src/vault/create.command.ts +++ b/apps/cli/src/vault/create.command.ts @@ -80,7 +80,9 @@ export class CreateCommand { try { await this.cipherService.createWithServer(cipher); const newCipher = await this.cipherService.get(cipher.id); - const decCipher = await newCipher.decrypt(); + const decCipher = await newCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(newCipher) + ); const res = new CipherResponse(decCipher); return Response.success(res); } catch (e) { @@ -141,7 +143,9 @@ export class CreateCommand { new Uint8Array(fileBuf).buffer ); const updatedCipher = await this.cipherService.get(cipher.id); - const decCipher = await updatedCipher.decrypt(); + const decCipher = await updatedCipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher) + ); return Response.success(new CipherResponse(decCipher)); } catch (e) { return Response.error(e); diff --git a/apps/desktop/config/base.json b/apps/desktop/config/base.json index 3ae895a19cf..cb408a87d87 100644 --- a/apps/desktop/config/base.json +++ b/apps/desktop/config/base.json @@ -1,6 +1,7 @@ { "dev_flags": {}, "flags": { - "multithreadDecryption": false + "multithreadDecryption": false, + "enableCipherKeyEncryption": false } } diff --git a/apps/desktop/config/development.json b/apps/desktop/config/development.json index b587e9ecfb9..d2b10738124 100644 --- a/apps/desktop/config/development.json +++ b/apps/desktop/config/development.json @@ -1,6 +1,7 @@ { "devFlags": {}, "flags": { - "showDDGSetting": true + "showDDGSetting": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/desktop/config/production.json b/apps/desktop/config/production.json index 56f19341304..39b78094d0f 100644 --- a/apps/desktop/config/production.json +++ b/apps/desktop/config/production.json @@ -1,5 +1,6 @@ { "flags": { - "showDDGSetting": true + "showDDGSetting": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 48e3f0e7bd0..d3a13313e9e 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1,4 +1,4 @@ -{ +{ "bitwarden": { "message": "Bitwarden" }, diff --git a/apps/desktop/src/services/encrypted-message-handler.service.ts b/apps/desktop/src/services/encrypted-message-handler.service.ts index 1889bfc7451..1ce52cfecc0 100644 --- a/apps/desktop/src/services/encrypted-message-handler.service.ts +++ b/apps/desktop/src/services/encrypted-message-handler.service.ts @@ -190,7 +190,9 @@ export class EncryptedMessageHandlerService { if (cipher === null) { return { status: "failure" }; } - const cipherView = await cipher.decrypt(); + const cipherView = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); cipherView.name = credentialUpdatePayload.name; cipherView.login.password = credentialUpdatePayload.password; cipherView.login.username = credentialUpdatePayload.userName; diff --git a/apps/web/config/base.json b/apps/web/config/base.json index ed0bc0a850d..a377298c637 100644 --- a/apps/web/config/base.json +++ b/apps/web/config/base.json @@ -12,6 +12,7 @@ }, "flags": { "secretsManager": false, - "showPasswordless": false + "showPasswordless": false, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 45269d18c63..6e5c65af1d3 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -18,6 +18,7 @@ }, "flags": { "secretsManager": true, - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/config/development.json b/apps/web/config/development.json index 7aeffe55d0d..e3107f8788b 100644 --- a/apps/web/config/development.json +++ b/apps/web/config/development.json @@ -11,6 +11,7 @@ }, "flags": { "secretsManager": true, - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/config/euprd.json b/apps/web/config/euprd.json index 19864cdab66..4b6c9fa9098 100644 --- a/apps/web/config/euprd.json +++ b/apps/web/config/euprd.json @@ -12,6 +12,7 @@ }, "flags": { "secretsManager": true, - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/config/qa.json b/apps/web/config/qa.json index 6f514079010..be9911cc578 100644 --- a/apps/web/config/qa.json +++ b/apps/web/config/qa.json @@ -12,6 +12,7 @@ }, "flags": { "secretsManager": true, - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/config/selfhosted.json b/apps/web/config/selfhosted.json index 8ffbc0f9e5a..6b290baa6b5 100644 --- a/apps/web/config/selfhosted.json +++ b/apps/web/config/selfhosted.json @@ -8,6 +8,7 @@ }, "flags": { "secretsManager": false, - "showPasswordless": true + "showPasswordless": true, + "enableCipherKeyEncryption": false } } diff --git a/apps/web/src/app/vault/org-vault/add-edit.component.ts b/apps/web/src/app/vault/org-vault/add-edit.component.ts index 46d8440d489..bea55856a4a 100644 --- a/apps/web/src/app/vault/org-vault/add-edit.component.ts +++ b/apps/web/src/app/vault/org-vault/add-edit.component.ts @@ -110,7 +110,7 @@ export class AddEditComponent extends BaseAddEditComponent { if (!this.organization.canEditAnyCollection) { return super.encryptCipher(); } - return this.cipherService.encrypt(this.cipher, null, this.originalCipher); + return this.cipherService.encrypt(this.cipher, null, null, this.originalCipher); } protected async deleteCipher() { diff --git a/libs/angular/src/admin-console/components/collections.component.ts b/libs/angular/src/admin-console/components/collections.component.ts index 2fa5b5d3d3a..945a79d1d78 100644 --- a/libs/angular/src/admin-console/components/collections.component.ts +++ b/libs/angular/src/admin-console/components/collections.component.ts @@ -37,7 +37,9 @@ export class CollectionsComponent implements OnInit { async load() { this.cipherDomain = await this.loadCipher(); this.collectionIds = this.loadCipherCollections(); - this.cipher = await this.cipherDomain.decrypt(); + this.cipher = await this.cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain) + ); this.collections = await this.loadCollections(); this.collections.forEach((c) => ((c as any).checked = false)); diff --git a/libs/angular/src/components/share.component.ts b/libs/angular/src/components/share.component.ts index 73792f91fef..6d0764be9b3 100644 --- a/libs/angular/src/components/share.component.ts +++ b/libs/angular/src/components/share.component.ts @@ -66,7 +66,9 @@ export class ShareComponent implements OnInit, OnDestroy { }); const cipherDomain = await this.cipherService.get(this.cipherId); - this.cipher = await cipherDomain.decrypt(); + this.cipher = await cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain) + ); this.filterCollections(); } @@ -94,7 +96,9 @@ export class ShareComponent implements OnInit, OnDestroy { } const cipherDomain = await this.cipherService.get(this.cipherId); - const cipherView = await cipherDomain.decrypt(); + const cipherView = await cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain) + ); const orgs = await firstValueFrom(this.organizations$); const orgName = orgs.find((o) => o.id === this.organizationId)?.name ?? this.i18nService.t("organization"); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 02f6278fec7..5c19d0fe771 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -271,7 +271,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; searchService: SearchServiceAbstraction, stateService: StateServiceAbstraction, encryptService: EncryptService, - fileUploadService: CipherFileUploadServiceAbstraction + fileUploadService: CipherFileUploadServiceAbstraction, + configService: ConfigServiceAbstraction ) => new CipherService( cryptoService, @@ -281,7 +282,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; searchService, stateService, encryptService, - fileUploadService + fileUploadService, + configService ), deps: [ CryptoServiceAbstraction, @@ -292,6 +294,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; StateServiceAbstraction, EncryptService, CipherFileUploadServiceAbstraction, + ConfigServiceAbstraction, ], }, { diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 377fe88b63f..3aca51e44a8 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -225,7 +225,9 @@ export class AddEditComponent implements OnInit, OnDestroy { if (this.cipher == null) { if (this.editMode) { const cipher = await this.loadCipher(); - this.cipher = await cipher.decrypt(); + this.cipher = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); // Adjust Cipher Name if Cloning if (this.cloneMode) { diff --git a/libs/angular/src/vault/components/attachments.component.ts b/libs/angular/src/vault/components/attachments.component.ts index c7a8dd2ee27..e1974e2b7d5 100644 --- a/libs/angular/src/vault/components/attachments.component.ts +++ b/libs/angular/src/vault/components/attachments.component.ts @@ -73,7 +73,9 @@ export class AttachmentsComponent implements OnInit { try { this.formPromise = this.saveCipherAttachment(files[0]); this.cipherDomain = await this.formPromise; - this.cipher = await this.cipherDomain.decrypt(); + this.cipher = await this.cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain) + ); this.platformUtilsService.showToast("success", null, this.i18nService.t("attachmentSaved")); this.onUploadedAttachment.emit(); } catch (e) { @@ -179,7 +181,9 @@ export class AttachmentsComponent implements OnInit { protected async init() { this.cipherDomain = await this.loadCipher(); - this.cipher = await this.cipherDomain.decrypt(); + this.cipher = await this.cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain) + ); const canAccessPremium = await this.stateService.getCanAccessPremium(); this.canAccessAttachments = canAccessPremium || this.cipher.organizationId != null; @@ -229,7 +233,9 @@ export class AttachmentsComponent implements OnInit { decBuf, admin ); - this.cipher = await this.cipherDomain.decrypt(); + this.cipher = await this.cipherDomain.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain) + ); // 3. Delete old this.deletePromises[attachment.id] = this.deleteCipherAttachment(attachment.id); diff --git a/libs/angular/src/vault/components/password-history.component.ts b/libs/angular/src/vault/components/password-history.component.ts index 8fbea9d8fb9..3a25b6930a8 100644 --- a/libs/angular/src/vault/components/password-history.component.ts +++ b/libs/angular/src/vault/components/password-history.component.ts @@ -33,7 +33,9 @@ export class PasswordHistoryComponent implements OnInit { protected async init() { const cipher = await this.cipherService.get(this.cipherId); - const decCipher = await cipher.decrypt(); + const decCipher = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory; } } diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index 18ea5474fd6..40523afd850 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -113,7 +113,9 @@ export class ViewComponent implements OnDestroy, OnInit { this.cleanUp(); const cipher = await this.cipherService.get(this.cipherId); - this.cipher = await cipher.decrypt(); + this.cipher = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); this.canAccessPremium = await this.stateService.getCanAccessPremium(); this.showPremiumRequiredTotp = this.cipher.login.totp && !this.canAccessPremium && !this.cipher.organizationUseTotp; diff --git a/libs/common/src/models/export/cipher.export.ts b/libs/common/src/models/export/cipher.export.ts index 342cf59fd86..3ae6c9757dd 100644 --- a/libs/common/src/models/export/cipher.export.ts +++ b/libs/common/src/models/export/cipher.export.ts @@ -88,6 +88,7 @@ export class CipherExport { domain.notes = req.notes != null ? new EncString(req.notes) : null; domain.favorite = req.favorite; domain.reprompt = req.reprompt ?? CipherRepromptType.None; + domain.key = req.key != null ? new EncString(req.key) : null; if (req.fields != null) { domain.fields = req.fields.map((f) => FieldExport.toDomain(f)); @@ -135,6 +136,7 @@ export class CipherExport { revisionDate: Date = null; creationDate: Date = null; deletedDate: Date = null; + key: string; // Use build method instead of ctor so that we can control order of JSON stringify for pretty print build(o: CipherView | CipherDomain) { @@ -149,6 +151,7 @@ export class CipherExport { } else { this.name = o.name?.encryptedString; this.notes = o.notes?.encryptedString; + this.key = o.key?.encryptedString; } this.favorite = o.favorite; diff --git a/libs/common/src/platform/abstractions/config/config.service.abstraction.ts b/libs/common/src/platform/abstractions/config/config.service.abstraction.ts index 59f87b0fa29..67f7f2f4ce7 100644 --- a/libs/common/src/platform/abstractions/config/config.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/config/config.service.abstraction.ts @@ -1,4 +1,5 @@ import { Observable } from "rxjs"; +import { SemVer } from "semver"; import { FeatureFlag } from "../../../enums/feature-flag.enum"; import { Region } from "../environment.service"; @@ -16,6 +17,9 @@ export abstract class ConfigServiceAbstraction { key: FeatureFlag, defaultValue?: T ) => Promise; + checkServerMeetsVersionRequirement$: ( + minimumRequiredServerVersion: SemVer + ) => Observable; /** * Force ConfigService to fetch an updated config from the server and emit it from serverConfig$ diff --git a/libs/common/src/platform/abstractions/crypto.service.ts b/libs/common/src/platform/abstractions/crypto.service.ts index a868484bd04..8f1d9c48662 100644 --- a/libs/common/src/platform/abstractions/crypto.service.ts +++ b/libs/common/src/platform/abstractions/crypto.service.ts @@ -6,6 +6,7 @@ import { KeySuffixOptions, KdfType, HashPurpose } from "../../enums"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncString } from "../models/domain/enc-string"; import { + CipherKey, MasterKey, OrgKey, PinKey, @@ -372,6 +373,11 @@ export abstract class CryptoService { */ rsaDecrypt: (encValue: string, privateKeyValue?: Uint8Array) => Promise; randomNumber: (min: number, max: number) => Promise; + /** + * Generates a new cipher key + * @returns A new cipher key + */ + makeCipherKey: () => Promise; /** * Initialize all necessary crypto keys needed for a new account. diff --git a/libs/common/src/platform/interfaces/decryptable.interface.ts b/libs/common/src/platform/interfaces/decryptable.interface.ts index ae5e8ebbf82..35895bfd6ff 100644 --- a/libs/common/src/platform/interfaces/decryptable.interface.ts +++ b/libs/common/src/platform/interfaces/decryptable.interface.ts @@ -8,5 +8,5 @@ import { InitializerMetadata } from "./initializer-metadata.interface"; * @example Cipher implements Decryptable */ export interface Decryptable extends InitializerMetadata { - decrypt: (key?: SymmetricCryptoKey) => Promise; + decrypt: (key: SymmetricCryptoKey) => Promise; } diff --git a/libs/common/src/platform/misc/flags.ts b/libs/common/src/platform/misc/flags.ts index c1bdafaa0f3..53609505675 100644 --- a/libs/common/src/platform/misc/flags.ts +++ b/libs/common/src/platform/misc/flags.ts @@ -3,6 +3,7 @@ export type SharedFlags = { multithreadDecryption: boolean; showPasswordless?: boolean; + enableCipherKeyEncryption?: boolean; }; // required to avoid linting errors when there are no flags diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts index 818155ef98d..8f3b46f077c 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts @@ -83,3 +83,4 @@ export type MasterKey = Opaque; export type PinKey = Opaque; export type OrgKey = Opaque; export type ProviderKey = Opaque; +export type CipherKey = Opaque; diff --git a/libs/common/src/platform/services/config/config.service.ts b/libs/common/src/platform/services/config/config.service.ts index 008f5a764d3..45db66af0eb 100644 --- a/libs/common/src/platform/services/config/config.service.ts +++ b/libs/common/src/platform/services/config/config.service.ts @@ -10,6 +10,7 @@ import { merge, timer, } from "rxjs"; +import { SemVer } from "semver"; import { AuthService } from "../../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; @@ -25,10 +26,13 @@ import { ServerConfigData } from "../../models/data/server-config.data"; const ONE_HOUR_IN_MILLISECONDS = 1000 * 3600; export class ConfigService implements ConfigServiceAbstraction { + private inited = false; + protected _serverConfig = new ReplaySubject(1); serverConfig$ = this._serverConfig.asObservable(); + private _forceFetchConfig = new Subject(); - private inited = false; + protected refreshTimer$ = timer(ONE_HOUR_IN_MILLISECONDS, ONE_HOUR_IN_MILLISECONDS); // after 1 hour, then every hour cloudRegion$ = this.serverConfig$.pipe( map((config) => config?.environment?.cloudRegion ?? Region.US) @@ -62,7 +66,7 @@ export class ConfigService implements ConfigServiceAbstraction { // If you need to fetch a new config when an event occurs, add an observable that emits on that event here merge( - timer(ONE_HOUR_IN_MILLISECONDS, ONE_HOUR_IN_MILLISECONDS), // after 1 hour, then every hour + this.refreshTimer$, // an overridable interval this.environmentService.urls, // when environment URLs change (including when app is started) this._forceFetchConfig // manual ) @@ -103,4 +107,21 @@ export class ConfigService implements ConfigServiceAbstraction { await this.stateService.setServerConfig(data); this.environmentService.setCloudWebVaultUrl(data.environment?.cloudRegion); } + + /** + * Verifies whether the server version meets the minimum required version + * @param minimumRequiredServerVersion The minimum version required + * @returns True if the server version is greater than or equal to the minimum required version + */ + checkServerMeetsVersionRequirement$(minimumRequiredServerVersion: SemVer) { + return this.serverConfig$.pipe( + map((serverConfig) => { + if (serverConfig == null) { + return false; + } + const serverVersion = new SemVer(serverConfig.version); + return serverVersion.compare(minimumRequiredServerVersion) >= 0; + }) + ); + } } diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts index 3b4090ef344..0ea9acc53da 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/common/src/platform/services/crypto.service.ts @@ -27,6 +27,7 @@ import { EFFLongWordList } from "../misc/wordlist"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncString } from "../models/domain/enc-string"; import { + CipherKey, MasterKey, OrgKey, PinKey, @@ -596,6 +597,11 @@ export class CryptoService implements CryptoServiceAbstraction { return new SymmetricCryptoKey(sendKey); } + async makeCipherKey(): Promise { + const randomBytes = await this.cryptoFunctionService.aesGenerateKey(512); + return new SymmetricCryptoKey(randomBytes) as CipherKey; + } + async clearKeys(userId?: string): Promise { await this.clearUserKey(true, userId); await this.clearMasterKeyHash(userId); diff --git a/libs/common/src/vault/abstractions/cipher.service.ts b/libs/common/src/vault/abstractions/cipher.service.ts index e328c5bd491..404a58abb1a 100644 --- a/libs/common/src/vault/abstractions/cipher.service.ts +++ b/libs/common/src/vault/abstractions/cipher.service.ts @@ -11,7 +11,8 @@ export abstract class CipherService { clearCache: (userId?: string) => Promise; encrypt: ( model: CipherView, - key?: SymmetricCryptoKey, + keyForEncryption?: SymmetricCryptoKey, + keyForCipherKeyDecryption?: SymmetricCryptoKey, originalCipher?: Cipher ) => Promise; encryptFields: (fieldsModel: FieldView[], key: SymmetricCryptoKey) => Promise; @@ -81,4 +82,5 @@ export abstract class CipherService { organizationId?: string, asAdmin?: boolean ) => Promise; + getKeyForCipherKeyDecryption: (cipher: Cipher) => Promise; } diff --git a/libs/common/src/vault/models/data/cipher.data.ts b/libs/common/src/vault/models/data/cipher.data.ts index 2f83ee194b4..1452ffe7ee0 100644 --- a/libs/common/src/vault/models/data/cipher.data.ts +++ b/libs/common/src/vault/models/data/cipher.data.ts @@ -33,6 +33,7 @@ export class CipherData { creationDate: string; deletedDate: string; reprompt: CipherRepromptType; + key: string; constructor(response?: CipherResponse, collectionIds?: string[]) { if (response == null) { @@ -54,6 +55,7 @@ export class CipherData { this.creationDate = response.creationDate; this.deletedDate = response.deletedDate; this.reprompt = response.reprompt; + this.key = response.key; switch (this.type) { case CipherType.Login: diff --git a/libs/common/src/vault/models/domain/cipher.spec.ts b/libs/common/src/vault/models/domain/cipher.spec.ts index d5c141487a2..6234fe7029b 100644 --- a/libs/common/src/vault/models/domain/cipher.spec.ts +++ b/libs/common/src/vault/models/domain/cipher.spec.ts @@ -2,10 +2,14 @@ import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { Jsonify } from "type-fest"; -import { mockEnc, mockFromJson } from "../../../../spec"; +import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils"; import { FieldType, SecureNoteType, UriMatchType } from "../../../enums"; +import { CryptoService } from "../../../platform/abstractions/crypto.service"; +import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncString } from "../../../platform/models/domain/enc-string"; +import { ContainerService } from "../../../platform/services/container.service"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; +import { CipherService } from "../../abstractions/cipher.service"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; import { CipherType } from "../../enums/cipher-type"; import { CipherData } from "../../models/data/cipher.data"; @@ -47,6 +51,7 @@ describe("Cipher DTO", () => { attachments: null, fields: null, passwordHistory: null, + key: null, }); }); @@ -69,6 +74,7 @@ describe("Cipher DTO", () => { creationDate: "2022-01-01T12:00:00.000Z", deletedDate: null, reprompt: CipherRepromptType.None, + key: "EncryptedString", login: { uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }], username: "EncryptedString", @@ -136,6 +142,7 @@ describe("Cipher DTO", () => { creationDate: new Date("2022-01-01T12:00:00.000Z"), deletedDate: null, reprompt: 0, + key: { encryptedString: "EncryptedString", encryptionType: 0 }, login: { passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"), autofillOnPageLoad: false, @@ -206,6 +213,7 @@ describe("Cipher DTO", () => { cipher.creationDate = new Date("2022-01-01T12:00:00.000Z"); cipher.deletedDate = null; cipher.reprompt = CipherRepromptType.None; + cipher.key = mockEnc("EncKey"); const loginView = new LoginView(); loginView.username = "username"; @@ -215,7 +223,20 @@ describe("Cipher DTO", () => { login.decrypt(Arg.any(), Arg.any()).resolves(loginView); cipher.login = login; - const cipherView = await cipher.decrypt(); + const cryptoService = Substitute.for(); + const encryptService = Substitute.for(); + const cipherService = Substitute.for(); + + encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64)); + + (window as any).bitwardenContainerService = new ContainerService( + cryptoService, + encryptService + ); + + const cipherView = await cipher.decrypt( + await cipherService.getKeyForCipherKeyDecryption(cipher) + ); expect(cipherView).toMatchObject({ id: "id", @@ -261,6 +282,7 @@ describe("Cipher DTO", () => { creationDate: "2022-01-01T12:00:00.000Z", deletedDate: null, reprompt: CipherRepromptType.None, + key: "EncKey", secureNote: { type: SecureNoteType.Generic, }, @@ -292,6 +314,7 @@ describe("Cipher DTO", () => { attachments: null, fields: null, passwordHistory: null, + key: { encryptedString: "EncKey", encryptionType: 0 }, }); }); @@ -318,8 +341,22 @@ describe("Cipher DTO", () => { cipher.reprompt = CipherRepromptType.None; cipher.secureNote = new SecureNote(); cipher.secureNote.type = SecureNoteType.Generic; + cipher.key = mockEnc("EncKey"); - const cipherView = await cipher.decrypt(); + const cryptoService = Substitute.for(); + const encryptService = Substitute.for(); + const cipherService = Substitute.for(); + + encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64)); + + (window as any).bitwardenContainerService = new ContainerService( + cryptoService, + encryptService + ); + + const cipherView = await cipher.decrypt( + await cipherService.getKeyForCipherKeyDecryption(cipher) + ); expect(cipherView).toMatchObject({ id: "id", @@ -373,6 +410,7 @@ describe("Cipher DTO", () => { expYear: "EncryptedString", code: "EncryptedString", }, + key: "EncKey", }; }); @@ -408,6 +446,7 @@ describe("Cipher DTO", () => { attachments: null, fields: null, passwordHistory: null, + key: { encryptedString: "EncKey", encryptionType: 0 }, }); }); @@ -432,6 +471,7 @@ describe("Cipher DTO", () => { cipher.creationDate = new Date("2022-01-01T12:00:00.000Z"); cipher.deletedDate = null; cipher.reprompt = CipherRepromptType.None; + cipher.key = mockEnc("EncKey"); const cardView = new CardView(); cardView.cardholderName = "cardholderName"; @@ -441,7 +481,20 @@ describe("Cipher DTO", () => { card.decrypt(Arg.any(), Arg.any()).resolves(cardView); cipher.card = card; - const cipherView = await cipher.decrypt(); + const cryptoService = Substitute.for(); + const encryptService = Substitute.for(); + const cipherService = Substitute.for(); + + encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64)); + + (window as any).bitwardenContainerService = new ContainerService( + cryptoService, + encryptService + ); + + const cipherView = await cipher.decrypt( + await cipherService.getKeyForCipherKeyDecryption(cipher) + ); expect(cipherView).toMatchObject({ id: "id", @@ -487,6 +540,7 @@ describe("Cipher DTO", () => { creationDate: "2022-01-01T12:00:00.000Z", deletedDate: null, reprompt: CipherRepromptType.None, + key: "EncKey", identity: { title: "EncryptedString", firstName: "EncryptedString", @@ -554,6 +608,7 @@ describe("Cipher DTO", () => { attachments: null, fields: null, passwordHistory: null, + key: { encryptedString: "EncKey", encryptionType: 0 }, }); }); @@ -578,6 +633,7 @@ describe("Cipher DTO", () => { cipher.creationDate = new Date("2022-01-01T12:00:00.000Z"); cipher.deletedDate = null; cipher.reprompt = CipherRepromptType.None; + cipher.key = mockEnc("EncKey"); const identityView = new IdentityView(); identityView.firstName = "firstName"; @@ -587,7 +643,20 @@ describe("Cipher DTO", () => { identity.decrypt(Arg.any(), Arg.any()).resolves(identityView); cipher.identity = identity; - const cipherView = await cipher.decrypt(); + const cryptoService = Substitute.for(); + const encryptService = Substitute.for(); + const cipherService = Substitute.for(); + + encryptService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(64)); + + (window as any).bitwardenContainerService = new ContainerService( + cryptoService, + encryptService + ); + + const cipherView = await cipher.decrypt( + await cipherService.getKeyForCipherKeyDecryption(cipher) + ); expect(cipherView).toMatchObject({ id: "id", diff --git a/libs/common/src/vault/models/domain/cipher.ts b/libs/common/src/vault/models/domain/cipher.ts index f85b6cb45c9..23349695a72 100644 --- a/libs/common/src/vault/models/domain/cipher.ts +++ b/libs/common/src/vault/models/domain/cipher.ts @@ -1,6 +1,7 @@ import { Jsonify } from "type-fest"; import { Decryptable } from "../../../platform/interfaces/decryptable.interface"; +import { Utils } from "../../../platform/misc/utils"; import Domain from "../../../platform/models/domain/domain-base"; import { EncString } from "../../../platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; @@ -45,6 +46,7 @@ export class Cipher extends Domain implements Decryptable { creationDate: Date; deletedDate: Date; reprompt: CipherRepromptType; + key: EncString; constructor(obj?: CipherData, localData: LocalData = null) { super(); @@ -61,6 +63,7 @@ export class Cipher extends Domain implements Decryptable { folderId: null, name: null, notes: null, + key: null, }, ["id", "organizationId", "folderId"] ); @@ -117,9 +120,17 @@ export class Cipher extends Domain implements Decryptable { } } - async decrypt(encKey?: SymmetricCryptoKey): Promise { + // We are passing the organizationId into the EncString.decrypt() method here, but because the encKey will always be + // present and so the organizationId will not be used. + // We will refactor the EncString.decrypt() in https://bitwarden.atlassian.net/browse/PM-3762 to remove the dependency on the organizationId. + async decrypt(encKey: SymmetricCryptoKey): Promise { const model = new CipherView(this); + if (this.key != null) { + const encryptService = Utils.getContainerService().getEncryptService(); + encKey = new SymmetricCryptoKey(await encryptService.decryptToBytes(this.key, encKey)); + } + await this.decryptObj( model, { @@ -147,14 +158,12 @@ export class Cipher extends Domain implements Decryptable { break; } - const orgId = this.organizationId; - if (this.attachments != null && this.attachments.length > 0) { const attachments: any[] = []; await this.attachments.reduce((promise, attachment) => { return promise .then(() => { - return attachment.decrypt(orgId, encKey); + return attachment.decrypt(this.organizationId, encKey); }) .then((decAttachment) => { attachments.push(decAttachment); @@ -168,7 +177,7 @@ export class Cipher extends Domain implements Decryptable { await this.fields.reduce((promise, field) => { return promise .then(() => { - return field.decrypt(orgId, encKey); + return field.decrypt(this.organizationId, encKey); }) .then((decField) => { fields.push(decField); @@ -182,7 +191,7 @@ export class Cipher extends Domain implements Decryptable { await this.passwordHistory.reduce((promise, ph) => { return promise .then(() => { - return ph.decrypt(orgId, encKey); + return ph.decrypt(this.organizationId, encKey); }) .then((decPh) => { passwordHistory.push(decPh); @@ -209,6 +218,7 @@ export class Cipher extends Domain implements Decryptable { c.creationDate = this.creationDate != null ? this.creationDate.toISOString() : null; c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null; c.reprompt = this.reprompt; + c.key = this.key?.encryptedString; this.buildDataModel(this, c, { name: null, @@ -257,6 +267,7 @@ export class Cipher extends Domain implements Decryptable { const attachments = obj.attachments?.map((a: any) => Attachment.fromJSON(a)); const fields = obj.fields?.map((f: any) => Field.fromJSON(f)); const passwordHistory = obj.passwordHistory?.map((ph: any) => Password.fromJSON(ph)); + const key = EncString.fromJSON(obj.key); Object.assign(domain, obj, { name, @@ -266,6 +277,7 @@ export class Cipher extends Domain implements Decryptable { attachments, fields, passwordHistory, + key, }); switch (obj.type) { diff --git a/libs/common/src/vault/models/request/cipher.request.ts b/libs/common/src/vault/models/request/cipher.request.ts index cae48fb7af9..0f34200e79e 100644 --- a/libs/common/src/vault/models/request/cipher.request.ts +++ b/libs/common/src/vault/models/request/cipher.request.ts @@ -29,6 +29,7 @@ export class CipherRequest { attachments2: { [id: string]: AttachmentRequest }; lastKnownRevisionDate: Date; reprompt: CipherRepromptType; + key: string; constructor(cipher: Cipher) { this.type = cipher.type; @@ -39,6 +40,7 @@ export class CipherRequest { this.favorite = cipher.favorite; this.lastKnownRevisionDate = cipher.revisionDate; this.reprompt = cipher.reprompt; + this.key = cipher.key?.encryptedString; switch (this.type) { case CipherType.Login: diff --git a/libs/common/src/vault/models/response/cipher.response.ts b/libs/common/src/vault/models/response/cipher.response.ts index 71e43373775..8bc8a37874e 100644 --- a/libs/common/src/vault/models/response/cipher.response.ts +++ b/libs/common/src/vault/models/response/cipher.response.ts @@ -32,6 +32,7 @@ export class CipherResponse extends BaseResponse { creationDate: string; deletedDate: string; reprompt: CipherRepromptType; + key: string; constructor(response: any) { super(response); @@ -90,5 +91,6 @@ export class CipherResponse extends BaseResponse { } this.reprompt = this.getResponseProperty("Reprompt") || CipherRepromptType.None; + this.key = this.getResponseProperty("Key") || null; } } diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 4df3d202ff2..2c9adce553b 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -1,15 +1,24 @@ -// eslint-disable-next-line no-restricted-imports import { mock, mockReset } from "jest-mock-extended"; +import { of } from "rxjs"; +import { makeStaticByteArray } from "../../../spec/utils"; import { ApiService } from "../../abstractions/api.service"; import { SearchService } from "../../abstractions/search.service"; import { SettingsService } from "../../abstractions/settings.service"; import { UriMatchType, FieldType } from "../../enums"; +import { ConfigServiceAbstraction } from "../../platform/abstractions/config/config.service.abstraction"; import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; -import { OrgKey, SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; +import { EncArrayBuffer } from "../../platform/models/domain/enc-array-buffer"; +import { EncString } from "../../platform/models/domain/enc-string"; +import { + CipherKey, + OrgKey, + SymmetricCryptoKey, +} from "../../platform/models/domain/symmetric-crypto-key"; +import { ContainerService } from "../../platform/services/container.service"; import { CipherFileUploadService } from "../abstractions/file-upload/cipher-file-upload.service"; import { CipherRepromptType } from "../enums/cipher-reprompt-type"; import { CipherType } from "../enums/cipher-type"; @@ -18,9 +27,13 @@ import { Cipher } from "../models/domain/cipher"; import { CipherCreateRequest } from "../models/request/cipher-create.request"; import { CipherPartialRequest } from "../models/request/cipher-partial.request"; import { CipherRequest } from "../models/request/cipher.request"; +import { CipherView } from "../models/view/cipher.view"; import { CipherService } from "./cipher.service"; +const ENCRYPTED_TEXT = "This data has been encrypted"; +const ENCRYPTED_BYTES = mock(); + const cipherData: CipherData = { id: "id", organizationId: "orgId", @@ -35,6 +48,7 @@ const cipherData: CipherData = { notes: "EncryptedString", creationDate: "2022-01-01T12:00:00.000Z", deletedDate: null, + key: "EncKey", reprompt: CipherRepromptType.None, login: { uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }], @@ -88,6 +102,7 @@ describe("Cipher Service", () => { const i18nService = mock(); const searchService = mock(); const encryptService = mock(); + const configService = mock(); let cipherService: CipherService; let cipherObj: Cipher; @@ -101,6 +116,12 @@ describe("Cipher Service", () => { mockReset(i18nService); mockReset(searchService); mockReset(encryptService); + mockReset(configService); + + encryptService.encryptToBytes.mockReturnValue(Promise.resolve(ENCRYPTED_BYTES)); + encryptService.encrypt.mockReturnValue(Promise.resolve(new EncString(ENCRYPTED_TEXT))); + + (window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService); cipherService = new CipherService( cryptoService, @@ -110,7 +131,8 @@ describe("Cipher Service", () => { searchService, stateService, encryptService, - cipherFileUploadService + cipherFileUploadService, + configService ); cipherObj = new Cipher(cipherData); @@ -125,6 +147,12 @@ describe("Cipher Service", () => { cryptoService.makeDataEncKey.mockReturnValue( Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32))) ); + + configService.checkServerMeetsVersionRequirement$.mockReturnValue(of(false)); + process.env.FLAGS = JSON.stringify({ + enableCipherKeyEncryption: false, + }); + const spy = jest.spyOn(cipherFileUploadService, "upload"); await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData); @@ -216,4 +244,68 @@ describe("Cipher Service", () => { expect(spy).toHaveBeenCalledWith(cipherObj.id, expectedObj); }); }); + + describe("encrypt", () => { + let cipherView: CipherView; + + beforeEach(() => { + cipherView = new CipherView(); + cipherView.type = CipherType.Login; + + encryptService.decryptToBytes.mockReturnValue(Promise.resolve(makeStaticByteArray(64))); + configService.checkServerMeetsVersionRequirement$.mockReturnValue(of(true)); + cryptoService.makeCipherKey.mockReturnValue( + Promise.resolve(new SymmetricCryptoKey(makeStaticByteArray(64)) as CipherKey) + ); + cryptoService.encrypt.mockReturnValue(Promise.resolve(new EncString(ENCRYPTED_TEXT))); + }); + + describe("cipher.key", () => { + it("is null when enableCipherKeyEncryption flag is false", async () => { + process.env.FLAGS = JSON.stringify({ + enableCipherKeyEncryption: false, + }); + + const cipher = await cipherService.encrypt(cipherView); + + expect(cipher.key).toBeNull(); + }); + + it("is defined when enableCipherKeyEncryption flag is true", async () => { + process.env.FLAGS = JSON.stringify({ + enableCipherKeyEncryption: true, + }); + + const cipher = await cipherService.encrypt(cipherView); + + expect(cipher.key).toBeDefined(); + }); + }); + + describe("encryptWithCipherKey", () => { + beforeEach(() => { + jest.spyOn(cipherService, "encryptCipherWithCipherKey"); + }); + + it("is not called when enableCipherKeyEncryption is false", async () => { + process.env.FLAGS = JSON.stringify({ + enableCipherKeyEncryption: false, + }); + + await cipherService.encrypt(cipherView); + + expect(cipherService["encryptCipherWithCipherKey"]).not.toHaveBeenCalled(); + }); + + it("is called when enableCipherKeyEncryption is true", async () => { + process.env.FLAGS = JSON.stringify({ + enableCipherKeyEncryption: true, + }); + + await cipherService.encrypt(cipherView); + + expect(cipherService["encryptCipherWithCipherKey"]).toHaveBeenCalled(); + }); + }); + }); }); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 03c70cca84e..b9bbc3e291e 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -1,13 +1,18 @@ +import { firstValueFrom } from "rxjs"; +import { SemVer } from "semver"; + import { ApiService } from "../../abstractions/api.service"; import { SearchService } from "../../abstractions/search.service"; import { SettingsService } from "../../abstractions/settings.service"; import { FieldType, UriMatchType } from "../../enums"; import { ErrorResponse } from "../../models/response/error.response"; import { View } from "../../models/view/view"; +import { ConfigServiceAbstraction } from "../../platform/abstractions/config/config.service.abstraction"; import { CryptoService } from "../../platform/abstractions/crypto.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; import { StateService } from "../../platform/abstractions/state.service"; +import { flagEnabled } from "../../platform/misc/flags"; import { sequentialize } from "../../platform/misc/sequentialize"; import { Utils } from "../../platform/misc/utils"; import Domain from "../../platform/models/domain/domain-base"; @@ -47,6 +52,8 @@ import { CipherView } from "../models/view/cipher.view"; import { FieldView } from "../models/view/field.view"; import { PasswordHistoryView } from "../models/view/password-history.view"; +const CIPHER_KEY_ENC_MIN_SERVER_VER = new SemVer("2023.9.1"); + export class CipherService implements CipherServiceAbstraction { private sortedCiphersCache: SortedCiphersCache = new SortedCiphersCache( this.sortCiphersByLastUsed @@ -60,7 +67,8 @@ export class CipherService implements CipherServiceAbstraction { private searchService: SearchService, private stateService: StateService, private encryptService: EncryptService, - private cipherFileUploadService: CipherFileUploadService + private cipherFileUploadService: CipherFileUploadService, + private configService: ConfigServiceAbstraction ) {} async getDecryptedCipherCache(): Promise { @@ -85,63 +93,18 @@ export class CipherService implements CipherServiceAbstraction { async encrypt( model: CipherView, - key?: SymmetricCryptoKey, + keyForEncryption?: SymmetricCryptoKey, + keyForCipherKeyDecryption?: SymmetricCryptoKey, originalCipher: Cipher = null ): Promise { - // Adjust password history if (model.id != null) { if (originalCipher == null) { originalCipher = await this.get(model.id); } if (originalCipher != null) { - const existingCipher = await originalCipher.decrypt(); - model.passwordHistory = existingCipher.passwordHistory || []; - if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) { - if ( - existingCipher.login.password != null && - existingCipher.login.password !== "" && - existingCipher.login.password !== model.login.password - ) { - const ph = new PasswordHistoryView(); - ph.password = existingCipher.login.password; - ph.lastUsedDate = model.login.passwordRevisionDate = new Date(); - model.passwordHistory.splice(0, 0, ph); - } else { - model.login.passwordRevisionDate = existingCipher.login.passwordRevisionDate; - } - } - if (existingCipher.hasFields) { - const existingHiddenFields = existingCipher.fields.filter( - (f) => - f.type === FieldType.Hidden && - f.name != null && - f.name !== "" && - f.value != null && - f.value !== "" - ); - const hiddenFields = - model.fields == null - ? [] - : model.fields.filter( - (f) => f.type === FieldType.Hidden && f.name != null && f.name !== "" - ); - existingHiddenFields.forEach((ef) => { - const matchedField = hiddenFields.find((f) => f.name === ef.name); - if (matchedField == null || matchedField.value !== ef.value) { - const ph = new PasswordHistoryView(); - ph.password = ef.name + ": " + ef.value; - ph.lastUsedDate = new Date(); - model.passwordHistory.splice(0, 0, ph); - } - }); - } - } - if (model.passwordHistory != null && model.passwordHistory.length === 0) { - model.passwordHistory = null; - } else if (model.passwordHistory != null && model.passwordHistory.length > 5) { - // only save last 5 history - model.passwordHistory = model.passwordHistory.slice(0, 5); + await this.updateModelfromExistingCipher(model, originalCipher); } + this.adjustPasswordHistoryLength(model); } const cipher = new Cipher(); @@ -155,35 +118,32 @@ export class CipherService implements CipherServiceAbstraction { cipher.reprompt = model.reprompt; cipher.edit = model.edit; - if (key == null && cipher.organizationId != null) { - key = await this.cryptoService.getOrgKey(cipher.organizationId); - if (key == null) { - throw new Error("Cannot encrypt cipher for organization. No key."); - } - } - await Promise.all([ - this.encryptObjProperty( + if (await this.getCipherKeyEncryptionEnabled()) { + cipher.key = originalCipher?.key ?? null; + const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher); + // The keyForEncryption is only used for encrypting the cipher key, not the cipher itself, since cipher key encryption is enabled. + // If the caller has provided a key for cipher key encryption, use it. Otherwise, use the user or org key. + keyForEncryption ||= userOrOrgKey; + // If the caller has provided a key for cipher key decryption, use it. Otherwise, use the user or org key. + keyForCipherKeyDecryption ||= userOrOrgKey; + return this.encryptCipherWithCipherKey( model, cipher, - { - name: null, - notes: null, - }, - key - ), - this.encryptCipherData(cipher, model, key), - this.encryptFields(model.fields, key).then((fields) => { - cipher.fields = fields; - }), - this.encryptPasswordHistories(model.passwordHistory, key).then((ph) => { - cipher.passwordHistory = ph; - }), - this.encryptAttachments(model.attachments, key).then((attachments) => { - cipher.attachments = attachments; - }), - ]); - - return cipher; + keyForEncryption, + keyForCipherKeyDecryption + ); + } else { + if (keyForEncryption == null && cipher.organizationId != null) { + keyForEncryption = await this.cryptoService.getOrgKey(cipher.organizationId); + if (keyForEncryption == null) { + throw new Error("Cannot encrypt cipher for organization. No key."); + } + } + // We want to ensure that the cipher key is null if cipher key encryption is disabled + // so that decryption uses the proper key. + cipher.key = null; + return this.encryptCipher(model, cipher, keyForEncryption); + } } async encryptAttachments( @@ -579,7 +539,7 @@ export class CipherService implements CipherServiceAbstraction { cipher.organizationId = organizationId; cipher.collectionIds = collectionIds; - const encCipher = await this.encrypt(cipher); + const encCipher = await this.encryptSharedCipher(cipher); const request = new CipherShareRequest(encCipher); const response = await this.apiService.putShareCipher(cipher.id, request); const data = new CipherData(response, collectionIds); @@ -597,7 +557,7 @@ export class CipherService implements CipherServiceAbstraction { cipher.organizationId = organizationId; cipher.collectionIds = collectionIds; promises.push( - this.encrypt(cipher).then((c) => { + this.encryptSharedCipher(cipher).then((c) => { encCiphers.push(c); }) ); @@ -645,14 +605,29 @@ export class CipherService implements CipherServiceAbstraction { data: Uint8Array, admin = false ): Promise { - let encKey: UserKey | OrgKey; - encKey = await this.cryptoService.getOrgKey(cipher.organizationId); - encKey ||= await this.cryptoService.getUserKeyWithLegacySupport(); + const encKey = await this.getKeyForCipherKeyDecryption(cipher); + const cipherKeyEncryptionEnabled = await this.getCipherKeyEncryptionEnabled(); - const dataEncKey = await this.cryptoService.makeDataEncKey(encKey); + const cipherEncKey = + cipherKeyEncryptionEnabled && cipher.key != null + ? (new SymmetricCryptoKey( + await this.encryptService.decryptToBytes(cipher.key, encKey) + ) as UserKey) + : encKey; - const encFileName = await this.encryptService.encrypt(filename, encKey); - const encData = await this.encryptService.encryptToBytes(data, dataEncKey[0]); + //if cipher key encryption is disabled but the item has an individual key, + //then we rollback to using the user key as the main key of encryption of the item + //in order to keep item and it's attachments with the same encryption level + if (cipher.key != null && !cipherKeyEncryptionEnabled) { + const model = await cipher.decrypt(await this.getKeyForCipherKeyDecryption(cipher)); + cipher = await this.encrypt(model); + await this.updateWithServer(cipher); + } + + const encFileName = await this.encryptService.encrypt(filename, cipherEncKey); + + const dataEncKey = await this.cryptoService.makeDataEncKey(cipherEncKey); + const encData = await this.encryptService.encryptToBytes(new Uint8Array(data), dataEncKey[0]); const response = await this.cipherFileUploadService.upload( cipher, @@ -963,8 +938,80 @@ export class CipherService implements CipherServiceAbstraction { await this.restore(restores); } + async getKeyForCipherKeyDecryption(cipher: Cipher): Promise { + return ( + (await this.cryptoService.getOrgKey(cipher.organizationId)) || + ((await this.cryptoService.getUserKeyWithLegacySupport()) as UserKey) + ); + } + // Helpers + // In the case of a cipher that is being shared with an organization, we want to decrypt the + // cipher key with the user's key and then re-encrypt it with the organization's key. + private async encryptSharedCipher(model: CipherView): Promise { + const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport(); + return await this.encrypt(model, null, keyForCipherKeyDecryption); + } + + private async updateModelfromExistingCipher( + model: CipherView, + originalCipher: Cipher + ): Promise { + const existingCipher = await originalCipher.decrypt( + await this.getKeyForCipherKeyDecryption(originalCipher) + ); + model.passwordHistory = existingCipher.passwordHistory || []; + if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) { + if ( + existingCipher.login.password != null && + existingCipher.login.password !== "" && + existingCipher.login.password !== model.login.password + ) { + const ph = new PasswordHistoryView(); + ph.password = existingCipher.login.password; + ph.lastUsedDate = model.login.passwordRevisionDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } else { + model.login.passwordRevisionDate = existingCipher.login.passwordRevisionDate; + } + } + if (existingCipher.hasFields) { + const existingHiddenFields = existingCipher.fields.filter( + (f) => + f.type === FieldType.Hidden && + f.name != null && + f.name !== "" && + f.value != null && + f.value !== "" + ); + const hiddenFields = + model.fields == null + ? [] + : model.fields.filter( + (f) => f.type === FieldType.Hidden && f.name != null && f.name !== "" + ); + existingHiddenFields.forEach((ef) => { + const matchedField = hiddenFields.find((f) => f.name === ef.name); + if (matchedField == null || matchedField.value !== ef.value) { + const ph = new PasswordHistoryView(); + ph.password = ef.name + ": " + ef.value; + ph.lastUsedDate = new Date(); + model.passwordHistory.splice(0, 0, ph); + } + }); + } + } + + private adjustPasswordHistoryLength(model: CipherView) { + if (model.passwordHistory != null && model.passwordHistory.length === 0) { + model.passwordHistory = null; + } else if (model.passwordHistory != null && model.passwordHistory.length > 5) { + // only save last 5 history + model.passwordHistory = model.passwordHistory.slice(0, 5); + } + } + private async shareAttachmentWithServer( attachmentView: AttachmentView, cipherId: string, @@ -1193,4 +1240,69 @@ export class CipherService implements CipherServiceAbstraction { private clearSortedCiphers() { this.sortedCiphersCache.clear(); } + + private async encryptCipher( + model: CipherView, + cipher: Cipher, + key: SymmetricCryptoKey + ): Promise { + await Promise.all([ + this.encryptObjProperty( + model, + cipher, + { + name: null, + notes: null, + }, + key + ), + this.encryptCipherData(cipher, model, key), + this.encryptFields(model.fields, key).then((fields) => { + cipher.fields = fields; + }), + this.encryptPasswordHistories(model.passwordHistory, key).then((ph) => { + cipher.passwordHistory = ph; + }), + this.encryptAttachments(model.attachments, key).then((attachments) => { + cipher.attachments = attachments; + }), + ]); + + return cipher; + } + + private async encryptCipherWithCipherKey( + model: CipherView, + cipher: Cipher, + keyForCipherKeyEncryption: SymmetricCryptoKey, + keyForCipherKeyDecryption: SymmetricCryptoKey + ): Promise { + // First, we get the key for cipher key encryption, in its decrypted form + let decryptedCipherKey: SymmetricCryptoKey; + if (cipher.key == null) { + decryptedCipherKey = await this.cryptoService.makeCipherKey(); + } else { + decryptedCipherKey = new SymmetricCryptoKey( + await this.encryptService.decryptToBytes(cipher.key, keyForCipherKeyDecryption) + ); + } + + // Then, we have to encrypt the cipher key with the proper key. + cipher.key = await this.encryptService.encrypt( + decryptedCipherKey.key, + keyForCipherKeyEncryption + ); + + // Finally, we can encrypt the cipher with the decrypted cipher key. + return this.encryptCipher(model, cipher, decryptedCipherKey); + } + + private async getCipherKeyEncryptionEnabled(): Promise { + return ( + flagEnabled("enableCipherKeyEncryption") && + (await firstValueFrom( + this.configService.checkServerMeetsVersionRequirement$(CIPHER_KEY_ENC_MIN_SERVER_VER) + )) + ); + } } diff --git a/libs/exporter/src/vault-export/services/vault-export.service.ts b/libs/exporter/src/vault-export/services/vault-export.service.ts index 8609ce0d1c8..c214646d9aa 100644 --- a/libs/exporter/src/vault-export/services/vault-export.service.ts +++ b/libs/exporter/src/vault-export/services/vault-export.service.ts @@ -258,12 +258,15 @@ export class VaultExportService implements VaultExportServiceAbstraction { if (exportData.ciphers != null && exportData.ciphers.length > 0) { exportData.ciphers .filter((c) => c.deletedDate === null) - .forEach((c) => { + .forEach(async (c) => { const cipher = new Cipher(new CipherData(c)); exportPromises.push( - cipher.decrypt().then((decCipher) => { - decCiphers.push(decCipher); - }) + this.cipherService + .getKeyForCipherKeyDecryption(cipher) + .then((key) => cipher.decrypt(key)) + .then((decCipher) => { + decCiphers.push(decCipher); + }) ); }); } diff --git a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts index 0f8f6418628..5d24cef4140 100644 --- a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts +++ b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts @@ -4,6 +4,7 @@ import { KdfType } from "@bitwarden/common/enums"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { BitwardenPasswordProtectedImporter, @@ -17,6 +18,7 @@ describe("BitwardenPasswordProtectedImporter", () => { let importer: BitwardenPasswordProtectedImporter; let cryptoService: MockProxy; let i18nService: MockProxy; + let cipherService: MockProxy; const password = Utils.newGuid(); const promptForPassword_callback = async () => { return password; @@ -25,10 +27,12 @@ describe("BitwardenPasswordProtectedImporter", () => { beforeEach(() => { cryptoService = mock(); i18nService = mock(); + cipherService = mock(); importer = new BitwardenPasswordProtectedImporter( cryptoService, i18nService, + cipherService, promptForPassword_callback ); }); diff --git a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts index 5c281dc6b73..895b64e91ce 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts @@ -6,6 +6,7 @@ import { import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { @@ -25,7 +26,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { protected constructor( protected cryptoService: CryptoService, - protected i18nService: I18nService + protected i18nService: I18nService, + protected cipherService: CipherService ) { super(); } @@ -96,7 +98,9 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { }); } - const view = await cipher.decrypt(); + const view = await cipher.decrypt( + await this.cipherService.getKeyForCipherKeyDecryption(cipher) + ); this.cleanupCipher(view); this.result.ciphers.push(view); } diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts index a8c2a711a0b..49288e9dd8c 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts @@ -4,21 +4,24 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { BitwardenPasswordProtectedFileFormat } from "@bitwarden/exporter/vault-export/bitwarden-json-export-types"; import { ImportResult } from "../../models/import-result"; import { Importer } from "../importer"; import { BitwardenJsonImporter } from "./bitwarden-json-importer"; + export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter implements Importer { private key: SymmetricCryptoKey; constructor( cryptoService: CryptoService, i18nService: I18nService, + cipherService: CipherService, private promptForPassword_callback: () => Promise ) { - super(cryptoService, i18nService); + super(cryptoService, i18nService, cipherService); } async parse(data: string): Promise { diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 4ff15174c56..437e51436c5 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -203,6 +203,7 @@ export class ImportService implements ImportServiceAbstraction { return new BitwardenPasswordProtectedImporter( this.cryptoService, this.i18nService, + this.cipherService, promptForPassword_callback ); case "lastpasscsv": From 58cd35ef76389cccbb79b6d95ef8864226d497ae Mon Sep 17 00:00:00 2001 From: Will Martin Date: Thu, 28 Sep 2023 09:04:16 -0400 Subject: [PATCH 16/16] [CL-80] feat: indeterminate checkbox (#6357) * add indeterminate svg --- .../src/checkbox/checkbox.component.ts | 25 +++++++++++++++---- .../src/checkbox/checkbox.stories.ts | 9 +++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/libs/components/src/checkbox/checkbox.component.ts b/libs/components/src/checkbox/checkbox.component.ts index 8e966fa2272..5e1e17a5cac 100644 --- a/libs/components/src/checkbox/checkbox.component.ts +++ b/libs/components/src/checkbox/checkbox.component.ts @@ -44,21 +44,33 @@ export class CheckboxComponent implements BitFormControlAbstraction { "checked:tw-bg-primary-500", "checked:tw-border-primary-500", - "checked:hover:tw-bg-primary-700", "checked:hover:tw-border-primary-700", "[&>label:hover]:checked:tw-bg-primary-700", "[&>label:hover]:checked:tw-border-primary-700", - "checked:before:tw-bg-text-contrast", - "checked:before:tw-mask-image-[var(--mask-image)]", "checked:before:tw-mask-position-[center]", "checked:before:tw-mask-repeat-[no-repeat]", - "checked:disabled:tw-border-secondary-100", "checked:disabled:tw-bg-secondary-100", - "checked:disabled:before:tw-bg-text-muted", + + "[&:not(:indeterminate)]:checked:before:tw-mask-image-[var(--mask-image)]", + "indeterminate:before:tw-mask-image-[var(--indeterminate-mask-image)]", + + "indeterminate:tw-bg-primary-500", + "indeterminate:tw-border-primary-500", + "indeterminate:hover:tw-bg-primary-700", + "indeterminate:hover:tw-border-primary-700", + "[&>label:hover]:indeterminate:tw-bg-primary-700", + "[&>label:hover]:indeterminate:tw-border-primary-700", + "indeterminate:before:tw-bg-text-contrast", + "indeterminate:before:tw-mask-position-[center]", + "indeterminate:before:tw-mask-repeat-[no-repeat]", + "indeterminate:before:tw-mask-image-[var(--indeterminate-mask-image)]", + "indeterminate:disabled:tw-border-secondary-100", + "indeterminate:disabled:tw-bg-secondary-100", + "indeterminate:disabled:before:tw-bg-text-muted", ]; constructor(@Optional() @Self() private ngControl?: NgControl) {} @@ -66,6 +78,9 @@ export class CheckboxComponent implements BitFormControlAbstraction { @HostBinding("style.--mask-image") protected maskImage = `url('data:image/svg+xml,%3Csvg class="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="8" height="8" viewBox="0 0 10 10"%3E%3Cpath d="M0.5 6.2L2.9 8.6L9.5 1.4" fill="none" stroke="white" stroke-width="2"%3E%3C/path%3E%3C/svg%3E')`; + @HostBinding("style.--indeterminate-mask-image") + protected indeterminateImage = `url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="none" viewBox="0 0 13 13"%3E%3Cpath stroke="%23fff" stroke-width="2" d="M2.5 6.5h8"/%3E%3C/svg%3E%0A')`; + @HostBinding() @Input() get disabled() { diff --git a/libs/components/src/checkbox/checkbox.stories.ts b/libs/components/src/checkbox/checkbox.stories.ts index c2c924e2a33..11ef32eac77 100644 --- a/libs/components/src/checkbox/checkbox.stories.ts +++ b/libs/components/src/checkbox/checkbox.stories.ts @@ -110,3 +110,12 @@ export const Custom: Story = { `, }), }; + +export const Indeterminate: Story = { + render: (args) => ({ + props: args, + template: ` + + `, + }), +};