diff --git a/.eslintrc.json b/.eslintrc.json index f3115898ff8..4fba3dd6128 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -156,7 +156,10 @@ { "files": ["libs/components/src/**/*.ts"], "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/components/*", "src/**/*"] }] + "no-restricted-imports": [ + "error", + { "patterns": ["@bitwarden/components/*", "src/**/*", "@bitwarden/angular/*"] } + ] } }, { @@ -176,6 +179,12 @@ "rules": { "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] } + }, + { + "files": ["libs/vault/src/**/*.ts"], + "rules": { + "no-restricted-imports": ["error", { "patterns": ["@bitwarden/vault/*", "src/**/*"] }] + } } ] } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 15de72d785b..c5f55674aee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,7 @@ # The following owners will be the default owners for everything in the repo. # Unless a later match takes precedence -* @bitwarden/team-leads-eng +* @bitwarden/tech-leads ## Secrets Manager team files ## bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev @@ -39,6 +39,7 @@ apps/desktop/src/vault @bitwarden/team-vault-dev apps/web/src/app/vault @bitwarden/team-vault-dev libs/angular/src/vault @bitwarden/team-vault-dev libs/common/src/vault @bitwarden/team-vault-dev +libs/vault @bitwarden/team-vault-dev ## Admin Console team files ## apps/browser/src/admin-console @bitwarden/team-admin-console-dev diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 17047f4333d..f5a60f817ce 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -2,10 +2,7 @@ ./apps/browser/src/safari/desktop/Assets.xcassets/AccentColor.colorset ./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset ./apps/browser/src/safari/desktop/Base.lproj -./apps/browser/src/services/vaultTimeout ./apps/browser/store/windows/Assets -./libs/common/src/abstractions/vaultTimeout -./libs/common/src/services/vaultTimeout ./bitwarden_license/README.md ./libs/angular/src/directives/cipherListVirtualScroll.directive.ts ./libs/angular/src/scss/webfonts/Open_Sans-italic-700.woff @@ -25,13 +22,10 @@ ./libs/common/src/misc/linkedFieldOption.decorator.ts ./libs/common/src/misc/serviceUtils.ts ./libs/common/src/misc/serviceUtils.spec.ts -./libs/common/src/abstractions/vaultTimeout/vaultTimeoutSettings.service.ts -./libs/common/src/abstractions/vaultTimeout/vaultTimeout.service.ts ./libs/common/src/abstractions/anonymousHub.service.ts -./libs/common/src/services/vaultTimeout/vaultTimeoutSettings.service.ts -./libs/common/src/services/vaultTimeout/vaultTimeout.service.ts ./libs/common/src/services/anonymousHub.service.ts ./libs/auth/README.md +./libs/vault/README.md ./README.md ./LICENSE_BITWARDEN.txt ./CONTRIBUTING.md @@ -78,5 +72,4 @@ ./apps/browser/src/safari/safari/SafariWebExtensionHandler.swift ./apps/browser/src/safari/safari/Info.plist ./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist -./apps/browser/src/services/vaultTimeout/vaultTimeout.service.ts ./SECURITY.md diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 9c670f01c65..b17cb816336 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -31,6 +31,9 @@ on: description: "Custom image tag extension" required: false +env: + _AZ_REGISTRY: bitwardenprod.azurecr.io + jobs: cloc: name: CLOC @@ -65,8 +68,7 @@ jobs: build-artifacts: name: Build artifacts runs-on: ubuntu-22.04 - needs: - - setup + needs: setup env: _VERSION: ${{ needs.setup.outputs.version }} strategy: @@ -146,13 +148,10 @@ jobs: matrix: include: - artifact_name: cloud-QA - registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web-qa-cloud - artifact_name: ee - registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web-ee - artifact_name: selfhosted-COMMERCIAL - registries: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io] image_name: web env: _VERSION: ${{ needs.setup.outputs.version }} @@ -174,15 +173,7 @@ jobs: fi ########## ACRs ########## - - name: Login to Azure - QA - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 - with: - creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }} - - - name: Log into QA container registry - run: az acr login -n bitwardenqa - - - name: Login to Azure - Prod + - name: Login to Prod Azure uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} @@ -190,6 +181,18 @@ jobs: - name: Log into Prod container registry run: az acr login -n bitwardenprod + - name: Login to Azure - CI Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve github PAT secrets + id: retrieve-secret-pat + uses: bitwarden/gh-actions/get-keyvault-secrets@f096207b7a2f31723165aee6ad03e91716686e78 + with: + keyvault: "bitwarden-ci" + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + - name: Download ${{ matrix.artifact_name }} artifact uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: @@ -218,37 +221,17 @@ jobs: echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT - - name: Generate tag list - id: tag-list - env: - IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} - PROJECT_NAME: ${{ matrix.image_name }} - run: echo "tags=bitwardenqa.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG},bitwardenprod.azurecr.io/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - ########## Build Image ########## - name: Extract artifact working-directory: apps/web run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip - - name: Login to Azure - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 - with: - creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - - - name: Retrieve github PAT secrets - id: retrieve-secret-pat - uses: bitwarden/gh-actions/get-keyvault-secrets@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - keyvault: "bitwarden-ci" - secrets: "github-pat-bitwarden-devops-bot-repo-scope" - - - name: Setup DCT - if: ${{ env.is_publish_branch == 'true' }} - id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - azure-keyvault-name: "bitwarden-ci" + - name: Generate image full name + id: image-name + env: + IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} + PROJECT_NAME: ${{ matrix.image_name }} + run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - name: Build Docker image uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1 @@ -257,21 +240,10 @@ jobs: file: apps/web/Dockerfile platforms: linux/amd64 push: true - tags: ${{ steps.tag-list.outputs.tags }} + tags: ${{ steps.image-name.outputs.name }} secrets: | "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" - - name: Push to DockerHub - if: contains(matrix.registries, 'bitwarden') && env.is_publish_branch == 'true' - env: - IMAGE_TAG: ${{ steps.tag.outputs.image_tag }} - PROJECT_NAME: ${{ matrix.image_name }} - DOCKER_CONTENT_TRUST: 1 - DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} - run: | - docker tag bitwardenprod.azurecr.io/$PROJECT_NAME:$IMAGE_TAG bitwarden/$PROJECT_NAME:$IMAGE_TAG - docker push bitwarden/$PROJECT_NAME:$IMAGE_TAG - - name: Log out of Docker run: docker logout @@ -279,8 +251,7 @@ jobs: crowdin-push: name: Crowdin Push if: github.ref == 'refs/heads/master' - needs: - - build-artifacts + needs: build-artifacts runs-on: ubuntu-22.04 steps: - name: Checkout repo diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 601d788449c..b53542747d9 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -15,10 +15,13 @@ on: - Redeploy - Dry Run +env: + _AZ_REGISTRY: bitwardenprod.azurecr.io + jobs: setup: name: Setup - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: release_version: ${{ steps.version.outputs.version }} tag_version: ${{ steps.version.outputs.tag }} @@ -46,10 +49,9 @@ jobs: monorepo: true monorepo-project: web - self-host: name: Release self-host docker - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: setup env: _BRANCH_NAME: ${{ github.ref_name }} @@ -67,42 +69,6 @@ jobs: - name: Checkout repo uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - ########## DockerHub ########## - - name: Setup DCT - id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a30e9c3d658dc97c4c2e61ec749fdab64b83386c - with: - azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - azure-keyvault-name: "bitwarden-ci" - - - name: Pull branch image - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker pull bitwarden/web:latest - else - docker pull bitwarden/web:$_BRANCH_NAME - fi - - - name: Docker Tag version - run: | - if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/web:latest bitwarden/web:$_RELEASE_VERSION - else - docker tag bitwarden/web:$_BRANCH_NAME bitwarden/web:$_RELEASE_VERSION - fi - - - name: Docker Push version - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - env: - DOCKER_CONTENT_TRUST: 1 - DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ steps.setup-dct.outputs.dct-delegate-repo-passphrase }} - run: docker push bitwarden/web:$_RELEASE_VERSION - - - name: Log out of Docker and disable Docker Notary - run: | - docker logout - echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV - ########## ACR ########## - name: Login to Azure - PROD Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 @@ -112,38 +78,46 @@ jobs: - name: Login to Azure ACR run: az acr login -n bitwardenprod - - name: Tag version - env: - REGISTRY: bitwardenprod.azurecr.io + - name: Pull branch image run: | if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then - docker tag bitwarden/web:latest $REGISTRY/web:$_RELEASE_VERSION - - docker tag bitwarden/web:latest $REGISTRY/web-sh:$_RELEASE_VERSION + docker pull $_AZ_REGISTRY/web:latest else - docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web:$_RELEASE_VERSION + docker pull $_AZ_REGISTRY/web:$_BRANCH_NAME + fi - docker tag bitwarden/web:$_BRANCH_NAME $REGISTRY/web-sh:$_RELEASE_VERSION + - name: Tag version + run: | + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web:dryrun + docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web-sh:dryrun + else + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:$_RELEASE_VERSION + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:latest + docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:latest fi - name: Push version - if: ${{ github.event.inputs.release_type != 'Dry Run' }} - env: - REGISTRY: bitwardenprod.azurecr.io run: | - docker push $REGISTRY/web:$_RELEASE_VERSION - - docker push $REGISTRY/web-sh:$_RELEASE_VERSION + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + docker push $_AZ_REGISTRY/web:dryrun + docker push $_AZ_REGISTRY/web-sh:dryrun + else + docker push $_AZ_REGISTRY/web:$_RELEASE_VERSION + docker push $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION + docker push $_AZ_REGISTRY/web:latest + docker push $_AZ_REGISTRY/web-sh:latest + fi - name: Log out of Docker run: docker logout ghpages-deploy: - name: Deploy to GitHub Pages - runs-on: ubuntu-20.04 - needs: - - setup + name: Create Deploy PR for GitHub Pages + runs-on: ubuntu-22.04 + needs: setup env: _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} _TAG_VERSION: ${{ needs.setup.outputs.tag_version }} @@ -191,7 +165,7 @@ jobs: - name: Unzip build asset working-directory: assets run: unzip web-*-cloud-COMMERCIAL.zip - + - name: Create new branch run: | cd ${{ github.workspace }}/ghpages-deployment @@ -200,12 +174,12 @@ jobs: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ git config --global url."https://".insteadOf ssh:// git checkout -b ${_BRANCH} - + - name: Copy build files run: | rm -rf ${{ github.workspace }}/ghpages-deployment/* cp -Rf ${{ github.workspace }}/assets/build/* ghpages-deployment/ - + - name: Commit and push changes working-directory: ghpages-deployment run: | @@ -233,7 +207,7 @@ jobs: release: name: Create GitHub Release - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - setup - self-host diff --git a/.vscode/settings.json b/.vscode/settings.json index 07423dd18ba..27e3a9b293a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "cSpell.words": ["Csprng", "decryptable", "Popout", "Reprompt", "takeuntil"] + "cSpell.words": ["Csprng", "decryptable", "Popout", "Reprompt", "takeuntil"], + "search.exclude": { + "**/locales/[^e]*/messages.json": true, + "**/locales/*[^n]/messages.json": true, + "**/_locales/[^e]*/messages.json": true, + "**/_locales/*[^n]/messages.json": true + } } diff --git a/apps/browser/package.json b/apps/browser/package.json index 5e1ad9bfd8d..7808666ccce 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2023.7.1", + "version": "2023.8.0", "scripts": { "build": "webpack", "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index bf021b10513..772f77d2039 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "استعادة العنصر" }, - "restoreItemConfirmation": { - "message": "هل أنت متأكد من أنك تريد استعادة هذا العنصر؟" - }, "restoredItem": { "message": "تم استعادة العنصر" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 26d26ac38a2..d986efb5a47 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Elementi bərpa et" }, - "restoreItemConfirmation": { - "message": "Elementi bərpa etmək istədiyinizə əminsiniz?" - }, "restoredItem": { "message": "Element bərpa edildi" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 946b2dc5d9d..50526f7839d 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Аднавіць элемент" }, - "restoreItemConfirmation": { - "message": "Вы сапраўды хочаце аднавіць гэты элемент?" - }, "restoredItem": { "message": "Элемент адноўлены" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 04a9f08050d..e57654ed012 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Възстановяване на запис" }, - "restoreItemConfirmation": { - "message": "Сигурни ли сте, че искате да възстановите записа?" - }, "restoredItem": { "message": "Записът е възстановен" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index aef8c25bfeb..7555b212220 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "বস্তু পুনরুদ্ধার" }, - "restoreItemConfirmation": { - "message": "আপনি কি নিশ্চিত যে আপনি এই বস্তুটি পুনরুদ্ধার করতে চান?" - }, "restoredItem": { "message": "বস্তু পুনরুদ্ধারকৃত" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 4f85dde0c37..04f25644349 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 916eeea79f4..a30b8305c2f 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restaura l'element" }, - "restoreItemConfirmation": { - "message": "Esteu segur que voleu restaurar aquest element?" - }, "restoredItem": { "message": "Element restaurat" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index de48e4c237d..120b6512a83 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Obnovit položku" }, - "restoreItemConfirmation": { - "message": "Opravdu chcete tuto položku obnovit?" - }, "restoredItem": { "message": "Položka byla obnovena" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 2d0dc622f72..13c15666016 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Adfer yr eitem" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 58dbe45aec2..10bf61a453a 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Gendan element" }, - "restoreItemConfirmation": { - "message": "Er du sikker på, at du vil gendanne dette element?" - }, "restoredItem": { "message": "Element gendannet" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index d48d1ec04c2..f00576263ab 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Eintrag wiederherstellen" }, - "restoreItemConfirmation": { - "message": "Soll dieser Eintrag wirklich wiederhergestellt werden?" - }, "restoredItem": { "message": "Eintrag wiederhergestellt" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 9a3ab302bce..90bad88038b 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Ανάκτηση Στοιχείου" }, - "restoreItemConfirmation": { - "message": "Είστε βέβαιοι ότι θέλετε να ανακτήσετε αυτό το στοιχείο;" - }, "restoredItem": { "message": "Στοιχείο που έχει Ανακτηθεί" }, diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index e31b317b03b..b068befd0d0 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -338,6 +338,9 @@ "other": { "message": "Other" }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, "rateExtension": { "message": "Rate the extension" }, @@ -1449,9 +1452,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, @@ -1608,6 +1608,12 @@ "biometricsNotSupportedDesc": { "message": "Browser biometrics is not supported on this device." }, + "biometricsFailedTitle": { + "message": "Biometrics failed" + }, + "biometricsFailedDesc": { + "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + }, "nativeMessaginPermissionErrorTitle": { "message": "Permission not provided" }, @@ -2158,8 +2164,8 @@ "notificationSentDevice": { "message": "A notification has been sent to your device." }, - "logInInitiated": { - "message": "Log in initiated" + "loginInitiated": { + "message": "Login initiated" }, "exposedMasterPassword": { "message": "Exposed Master Password" @@ -2245,6 +2251,31 @@ "opensInANewWindow": { "message": "Opens in a new window" }, + "deviceApprovalRequired": { + "message": "Device approval required. Select an approval option below:" + }, + "rememberThisDevice": { + "message": "Remember this device" + }, + "uncheckIfPublicDevice": { + "message": "Uncheck if using a public device" + }, + "approveFromYourOtherDevice": { + "message": "Approve from your other device" + }, + "requestAdminApproval": { + "message": "Request admin approval" + }, + "approveWithMasterPassword": { + "message": "Approve with master password" + }, + "ssoIdentifierRequired": { + "message": "Organization SSO identifier is required." + }, + "eu": { + "message": "EU", + "description": "European Union" + }, "usDomain": { "message": "bitwarden.com" }, @@ -2260,6 +2291,134 @@ "display": { "message": "Display" }, + "accountSuccessfullyCreated": { + "message": "Account successfully created!" + }, + "adminApprovalRequested": { + "message": "Admin approval requested" + }, + "adminApprovalRequestSentToAdmins": { + "message": "Your request has been sent to your admin." + }, + "youWillBeNotifiedOnceApproved": { + "message": "You will be notified once approved." + }, + "troubleLoggingIn": { + "message": "Trouble logging in?" + }, + "loginApproved": { + "message": "Login approved" + }, + "userEmailMissing": { + "message": "User email missing" + }, + "deviceTrusted": { + "message": "Device trusted" + }, + "inputRequired": { + "message": "Input is required." + }, + "required": { + "message": "required" + }, + "search": { + "message": "Search" + }, + "inputMinLength": { + "message": "Input must be at least $COUNT$ characters long.", + "placeholders": { + "count": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxLength": { + "message": "Input must not exceed $COUNT$ characters in length.", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "inputForbiddenCharacters": { + "message": "The following characters are not allowed: $CHARACTERS$", + "placeholders": { + "characters": { + "content": "$1", + "example": "@, #, $, %" + } + } + }, + "inputMinValue": { + "message": "Input value must be at least $MIN$.", + "placeholders": { + "min": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxValue": { + "message": "Input value must not exceed $MAX$.", + "placeholders": { + "max": { + "content": "$1", + "example": "100" + } + } + }, + "multipleInputEmails": { + "message": "1 or more emails are invalid" + }, + "inputTrimValidator": { + "message": "Input must not contain only whitespace.", + "description": "Notification to inform the user that a form's input can't contain only whitespace." + }, + "inputEmail": { + "message": "Input is not an email address." + }, + "fieldsNeedAttention": { + "message": "$COUNT$ field(s) above need your attention.", + "placeholders": { + "count": { + "content": "$1", + "example": "4" + } + } + }, + "selectPlaceholder": { + "message": "-- Select --" + }, + "multiSelectPlaceholder": { + "message": "-- Type to filter --" + }, + "multiSelectLoading": { + "message": "Retrieving options..." + }, + "multiSelectNotFound": { + "message": "No items found" + }, + "multiSelectClearAll": { + "message": "Clear all" + }, + "plusNMore": { + "message": "+ $QUANTITY$ more", + "placeholders": { + "quantity": { + "content": "$1", + "example": "5" + } + } + }, + "submenu": { + "message": "Submenu" + }, + "toggleCollapse": { + "message": "Toggle collapse", + "description": "Toggling an expand/collapse state." + }, "loginPasskey": { "message": "This login uses a passkey" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 9627efab01d..739c56b094c 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 5903b01eb3b..258b99300e4 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Restored item" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index e9dd0024495..b6504f13c97 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restaurar elemento" }, - "restoreItemConfirmation": { - "message": "¿Estás seguro de que quieres restaurar este elemento?" - }, "restoredItem": { "message": "Elemento restaurado" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 7ae023a60f5..5a5536aebd5 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Taasta kirje" }, - "restoreItemConfirmation": { - "message": "Oled kindel, et soovid selle kirje taastada?" - }, "restoredItem": { "message": "Kirje on taastatud" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index eaab1c4b4b9..e2d4739708d 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Berreskuratu elementua" }, - "restoreItemConfirmation": { - "message": "Ziur zaude elementu hau berreskuratu nahi duzula?" - }, "restoredItem": { "message": "Elementua berreskuratua" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 5ebfe79be2b..2bbe4f69b7e 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "بازیابی مورد" }, - "restoreItemConfirmation": { - "message": "آیا مطمئن هستید که می‌خواهید این مورد را بازیابی کنید؟" - }, "restoredItem": { "message": "مورد بازیابی شد" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index e95aa2bc803..6f91ffde6aa 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Palauta kohde" }, - "restoreItemConfirmation": { - "message": "Haluatko varmasti palauttaa kohteen?" - }, "restoredItem": { "message": "Kohde palautettiin" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index c6082c51669..08a1400ed88 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Ibalik ang item" }, - "restoreItemConfirmation": { - "message": "Sigurado ka bang nais mong ibalik ang item na ito?" - }, "restoredItem": { "message": "Item na nai-restore" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index d5216ae72d7..8369936cd53 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restaurer l'élément" }, - "restoreItemConfirmation": { - "message": "Êtes-vous sûr de vouloir restaurer cet élément ?" - }, "restoredItem": { "message": "Élément restauré" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 64c8bf51176..fca4e448637 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "שחזר פריט" }, - "restoreItemConfirmation": { - "message": "האם אתה בטוח שברצונך לשחזר פריט זה?" - }, "restoredItem": { "message": "פריט ששוחזר" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index c91afe71cf3..7ac864a5c30 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "आइटम बहाल करें" }, - "restoreItemConfirmation": { - "message": "क्या आप सुनिश्चित हैं कि आप इस आइटम को बहाल करना चाहते हैं?" - }, "restoredItem": { "message": "बहाल आइटम" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 65bbc664fdd..f7f491610f1 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Vrati stavku" }, - "restoreItemConfirmation": { - "message": "Sigurno želiš vratiti ovu stavku?" - }, "restoredItem": { "message": "Stavka vraćena" }, diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index bd106eac48d..fd9bed689a0 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Elem visszaállítása" }, - "restoreItemConfirmation": { - "message": "Biztosan visszaállításra kerüljön ezt az elem?" - }, "restoredItem": { "message": "Visszaállított elem" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 724e8bc30d0..89491f6b7e4 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Pulihkan Item" }, - "restoreItemConfirmation": { - "message": "Apakah Anda yakin ingin memulihkan item ini?" - }, "restoredItem": { "message": "Item Yang Dipulihkan" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 20f338b39e1..15a62b57e0c 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Ripristina elemento" }, - "restoreItemConfirmation": { - "message": "Sei sicuro di voler ripristinare questo elemento?" - }, "restoredItem": { "message": "Elemento ripristinato" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index e52752179b7..d0851ec36d7 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "アイテムをリストア" }, - "restoreItemConfirmation": { - "message": "このアイテムをリストアしますか?" - }, "restoredItem": { "message": "リストアされたアイテム" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 569277e8e34..8e9e2576438 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 3a9a33dda1a..dab35128eee 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ" }, - "restoreItemConfirmation": { - "message": "ಈ ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?" - }, "restoredItem": { "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗಿದೆ" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 50cadc8a949..f7c45565d4b 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "항목 복원" }, - "restoreItemConfirmation": { - "message": "정말 이 항목을 복원하시겠습니까?" - }, "restoredItem": { "message": "복원된 항목" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index b1dc314612c..8c610f8261d 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Atkurti elementą" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Elementas atkurtas" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index bd42b3bcbbf..a5dcdb1c21c 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Atjaunot vienumu" }, - "restoreItemConfirmation": { - "message": "Jūs tiešām atjaunot šo vienumu?" - }, "restoredItem": { "message": "Vienums atjaunots" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index ad8f6083c22..3f2ba29f295 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "ഇനം വീണ്ടെടുക്കുക " }, - "restoreItemConfirmation": { - "message": "ഈ ഇനം വീണ്ടെടുക്കണമെന്ന് ഉറപ്പാണോ?" - }, "restoredItem": { "message": "വീണ്ടെടുത്ത ഇനം" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 70b41cafbdb..55709a1a5b6 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 0e4904dcd15..1de995e52d4 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Gjenopprett objekt" }, - "restoreItemConfirmation": { - "message": "Er du sikker på at du vil gjenopprette dette elementet?" - }, "restoredItem": { "message": "Gjenopprettet objekt" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 7ff5ec8b36b..34780583ba6 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Item herstellen" }, - "restoreItemConfirmation": { - "message": "Weet je zeker dat je dit item wilt herstellen?" - }, "restoredItem": { "message": "Hersteld item" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index f010699118b..c2f2138bfce 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Przywróć element" }, - "restoreItemConfirmation": { - "message": "Czy na pewno chcesz przywrócić ten element?" - }, "restoredItem": { "message": "Element został przywrócony" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index d0370af24e9..d65e745ddbe 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restaurar Item" }, - "restoreItemConfirmation": { - "message": "Você tem certeza que deseja restaurar esse item?" - }, "restoredItem": { "message": "Item Restaurado" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 5254144a08b..532b280e9dd 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restaurar item" }, - "restoreItemConfirmation": { - "message": "Tem a certeza de que pretende restaurar este item?" - }, "restoredItem": { "message": "Item restaurado" }, @@ -2129,7 +2126,7 @@ "message": "Iniciar sessão com o dispositivo" }, "loginWithDeviceEnabledInfo": { - "message": "O início de sessão com o dispositivo deve ser ativado nas definições da aplicação Bitwarden. Necessita de outra opção?" + "message": "O início de sessão com o dispositivo deve ser ativado nas definições da aplicação Bitwarden. Precisa de outra opção?" }, "fingerprintPhraseHeader": { "message": "Frase de impressão digital" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index f4369278d64..12bc1d421b5 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restabilire articol" }, - "restoreItemConfirmation": { - "message": "Sigur doriți să restabiliți acest articol?" - }, "restoredItem": { "message": "Articol restabilit" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index f9c9b0141ab..3c1ecb4b44b 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -631,7 +631,7 @@ "message": "Обновить" }, "notificationUnlockDesc": { - "message": "Разблокируйте свое хранилище Bitwarden для завершения запроса автозаполнения." + "message": "Разблокируйте свое хранилище Bitwarden чтобы выполнить автозаполнение." }, "notificationUnlock": { "message": "Разблокировать" @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Восстановить элемент" }, - "restoreItemConfirmation": { - "message": "Вы уверены, что хотите восстановить этот элемент?" - }, "restoredItem": { "message": "Элемент восстановлен" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 0f209dd000b..369f1c0bf38 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "අයිතමය යළි පිහිටුවන්න" }, - "restoreItemConfirmation": { - "message": "ඔබට මෙම අයිතමය යථා තත්වයට පත් කිරීමට අවශ්ය බව ඔබට විශ්වාසද?" - }, "restoredItem": { "message": "ප්රතිෂ්ඨාපනය අයිතමය" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index bccc8a74ce5..caf8cc51b5e 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Obnoviť položku" }, - "restoreItemConfirmation": { - "message": "Naozaj chcete obnoviť túto položku?" - }, "restoredItem": { "message": "Obnovená položka" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 64f3415ae27..81280a088fb 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Obnovi element" }, - "restoreItemConfirmation": { - "message": "Ste prepričani, da želite obnoviti ta element?" - }, "restoredItem": { "message": "Element obnovljen" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 38ba6e1a4b1..08e99691c1c 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Врати ставку" }, - "restoreItemConfirmation": { - "message": "Да ли сте сигурни да желите да вратите ову ставку?" - }, "restoredItem": { "message": "Ставка враћена" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 36ebb906412..d761d526a61 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -11,7 +11,7 @@ "description": "Extension description" }, "loginOrCreateNewAccount": { - "message": "Logga in eller skapa ett nytt konto för att komma åt dina lösenord." + "message": "Logga in eller skapa ett nytt konto för att komma åt ditt säkra valv." }, "createAccount": { "message": "Skapa konto" @@ -342,7 +342,7 @@ "message": "Betygsätt tillägget" }, "rateExtensionDesc": { - "message": "Överväg gärna att hjälpa oss genom att ge oss en bra recension!" + "message": "Överväg gärna att skriva en recension om oss!" }, "browserNotSupportClipboard": { "message": "Din webbläsare har inte stöd för att enkelt kopiera till urklipp. Kopiera till urklipp manuellt istället." @@ -370,7 +370,7 @@ } }, "invalidMasterPassword": { - "message": "Felaktigt huvudlösenord" + "message": "Ogiltigt huvudlösenord" }, "vaultTimeout": { "message": "Valvets tidsgräns" @@ -736,7 +736,7 @@ "message": "Kopiera verifieringskod" }, "attachments": { - "message": "Bifogade filer" + "message": "Bilagor" }, "deleteAttachment": { "message": "Radera bilaga" @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Återställ objekt" }, - "restoreItemConfirmation": { - "message": "Är du säker på att du vill återställa detta objekt?" - }, "restoredItem": { "message": "Återställde objekt" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 63dd227e52b..400e75dd1b4 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Restore item" }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 667c26a1e12..3b677f92acf 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "กู้คืนรายการ" }, - "restoreItemConfirmation": { - "message": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนรายการนี้" - }, "restoredItem": { "message": "คืนค่ารายการแล้ว" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 23727c5af9a..a03f2e11bf5 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Kaydı geri yükle" }, - "restoreItemConfirmation": { - "message": "Bu kaydı geri yüklemek istediğinizden emin misiniz?" - }, "restoredItem": { "message": "Kayıt geri yüklendi" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 45177b5bdea..d4d8a2c1d3f 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -631,10 +631,10 @@ "message": "Оновити" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the auto-fill request." + "message": "Розблокуйте своє сховище Bitwarden, щоб завершити запит автозаповнення." }, "notificationUnlock": { - "message": "Unlock" + "message": "Розблокувати" }, "enableContextMenuItem": { "message": "Показувати в контекстному меню" @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Відновити запис" }, - "restoreItemConfirmation": { - "message": "Ви дійсно хочете відновити цей запис?" - }, "restoredItem": { "message": "Запис відновлено" }, @@ -2228,7 +2225,7 @@ } }, "loggingInOn": { - "message": "Logging in on" + "message": "Увійти на" }, "opensInANewWindow": { "message": "Відкривається у новому вікні" diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 0bb3aaefb85..2d1b03486a8 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "Khôi phục mục" }, - "restoreItemConfirmation": { - "message": "Bạn có chắc chắn muốn khôi phục mục này không?" - }, "restoredItem": { "message": "Mục đã được khôi phục" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 4f5d42ef1ad..c4838ff8775 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "恢复项目" }, - "restoreItemConfirmation": { - "message": "您确定要恢复此项目吗?" - }, "restoredItem": { "message": "项目已恢复" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index c7350529d77..6c4caad3948 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -1446,9 +1446,6 @@ "restoreItem": { "message": "還原項目" }, - "restoreItemConfirmation": { - "message": "您確定要還原此項目嗎?" - }, "restoredItem": { "message": "項目已還原" }, diff --git a/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts new file mode 100644 index 00000000000..e1757f98129 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/auth-request-crypto-service.factory.ts @@ -0,0 +1,29 @@ +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; +import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; + +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + CachedServices, + FactoryOptions, + factory, +} from "../../../platform/background/service-factories/factory-options"; + +type AuthRequestCryptoServiceFactoryOptions = FactoryOptions; + +export type AuthRequestCryptoServiceInitOptions = AuthRequestCryptoServiceFactoryOptions & + CryptoServiceInitOptions; + +export function authRequestCryptoServiceFactory( + cache: { authRequestCryptoService?: AuthRequestCryptoServiceAbstraction } & CachedServices, + opts: AuthRequestCryptoServiceInitOptions +): Promise { + return factory( + cache, + "authRequestCryptoService", + opts, + async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts index 5612cedb91c..6aaeb476369 100644 --- a/apps/browser/src/auth/background/service-factories/auth-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/auth-service.factory.ts @@ -52,6 +52,14 @@ import { PasswordStrengthServiceInitOptions, } from "../../../tools/background/service_factories/password-strength-service.factory"; +import { + authRequestCryptoServiceFactory, + AuthRequestCryptoServiceInitOptions, +} from "./auth-request-crypto-service.factory"; +import { + deviceTrustCryptoServiceFactory, + DeviceTrustCryptoServiceInitOptions, +} from "./device-trust-crypto-service.factory"; import { keyConnectorServiceFactory, KeyConnectorServiceInitOptions, @@ -75,7 +83,9 @@ export type AuthServiceInitOptions = AuthServiceFactoyOptions & I18nServiceInitOptions & EncryptServiceInitOptions & PolicyServiceInitOptions & - PasswordStrengthServiceInitOptions; + PasswordStrengthServiceInitOptions & + DeviceTrustCryptoServiceInitOptions & + AuthRequestCryptoServiceInitOptions; export function authServiceFactory( cache: { authService?: AbstractAuthService } & CachedServices, @@ -101,7 +111,9 @@ export function authServiceFactory( await i18nServiceFactory(cache, opts), await encryptServiceFactory(cache, opts), await passwordStrengthServiceFactory(cache, opts), - await policyServiceFactory(cache, opts) + await policyServiceFactory(cache, opts), + await deviceTrustCryptoServiceFactory(cache, opts), + await authRequestCryptoServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts new file mode 100644 index 00000000000..430d50fea75 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/device-trust-crypto-service.factory.ts @@ -0,0 +1,74 @@ +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; + +import { + DevicesApiServiceInitOptions, + devicesApiServiceFactory, +} from "../../../background/service-factories/devices-api-service.factory"; +import { + AppIdServiceInitOptions, + appIdServiceFactory, +} from "../../../platform/background/service-factories/app-id-service.factory"; +import { + CryptoFunctionServiceInitOptions, + cryptoFunctionServiceFactory, +} from "../../../platform/background/service-factories/crypto-function-service.factory"; +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + EncryptServiceInitOptions, + encryptServiceFactory, +} from "../../../platform/background/service-factories/encrypt-service.factory"; +import { + CachedServices, + FactoryOptions, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + I18nServiceInitOptions, + i18nServiceFactory, +} from "../../../platform/background/service-factories/i18n-service.factory"; +import { + PlatformUtilsServiceInitOptions, + platformUtilsServiceFactory, +} from "../../../platform/background/service-factories/platform-utils-service.factory"; +import { + StateServiceInitOptions, + stateServiceFactory, +} from "../../../platform/background/service-factories/state-service.factory"; + +type DeviceTrustCryptoServiceFactoryOptions = FactoryOptions; + +export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactoryOptions & + CryptoFunctionServiceInitOptions & + CryptoServiceInitOptions & + EncryptServiceInitOptions & + StateServiceInitOptions & + AppIdServiceInitOptions & + DevicesApiServiceInitOptions & + I18nServiceInitOptions & + PlatformUtilsServiceInitOptions; + +export function deviceTrustCryptoServiceFactory( + cache: { deviceTrustCryptoService?: DeviceTrustCryptoServiceAbstraction } & CachedServices, + opts: DeviceTrustCryptoServiceInitOptions +): Promise { + return factory( + cache, + "deviceTrustCryptoService", + opts, + async () => + new DeviceTrustCryptoService( + await cryptoFunctionServiceFactory(cache, opts), + await cryptoServiceFactory(cache, opts), + await encryptServiceFactory(cache, opts), + await stateServiceFactory(cache, opts), + await appIdServiceFactory(cache, opts), + await devicesApiServiceFactory(cache, opts), + await i18nServiceFactory(cache, opts), + await platformUtilsServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts new file mode 100644 index 00000000000..01bfb0f13cb --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/user-verification-api-service.factory.ts @@ -0,0 +1,29 @@ +import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; +import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service"; + +import { + ApiServiceInitOptions, + apiServiceFactory, +} from "../../../platform/background/service-factories/api-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../../platform/background/service-factories/factory-options"; + +type UserVerificationApiServiceFactoryOptions = FactoryOptions; + +export type UserVerificationApiServiceInitOptions = UserVerificationApiServiceFactoryOptions & + ApiServiceInitOptions; + +export function userVerificationApiServiceFactory( + cache: { userVerificationApiService?: UserVerificationApiServiceAbstraction } & CachedServices, + opts: UserVerificationApiServiceInitOptions +): Promise { + return factory( + cache, + "userVerificationApiService", + opts, + async () => new UserVerificationApiService(await apiServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts new file mode 100644 index 00000000000..79d327c9485 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts @@ -0,0 +1,51 @@ +import { UserVerificationService as AbstractUserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; + +import { + CryptoServiceInitOptions, + cryptoServiceFactory, +} from "../../../platform/background/service-factories/crypto-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + I18nServiceInitOptions, + i18nServiceFactory, +} from "../../../platform/background/service-factories/i18n-service.factory"; +import { + StateServiceInitOptions, + stateServiceFactory, +} from "../../../platform/background/service-factories/state-service.factory"; + +import { + UserVerificationApiServiceInitOptions, + userVerificationApiServiceFactory, +} from "./user-verification-api-service.factory"; + +type UserVerificationServiceFactoryOptions = FactoryOptions; + +export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryOptions & + StateServiceInitOptions & + CryptoServiceInitOptions & + I18nServiceInitOptions & + UserVerificationApiServiceInitOptions; + +export function userVerificationServiceFactory( + cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices, + opts: UserVerificationServiceInitOptions +): Promise { + return factory( + cache, + "userVerificationService", + opts, + async () => + new UserVerificationService( + await stateServiceFactory(cache, opts), + await cryptoServiceFactory(cache, opts), + await i18nServiceFactory(cache, opts), + await userVerificationApiServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/auth/popup/lock.component.html b/apps/browser/src/auth/popup/lock.component.html index cf964c15646..e787e0106d1 100644 --- a/apps/browser/src/auth/popup/lock.component.html +++ b/apps/browser/src/auth/popup/lock.component.html @@ -5,14 +5,20 @@ {{ "verifyIdentity" | i18n }}
- +
-
-
+
+
-
+
{ - document.getElementById(this.pinLock ? "pin" : "masterPassword").focus(); + document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus(); if ( this.biometricLock && !disableAutoBiometricsPrompt && @@ -95,7 +98,7 @@ export class LockComponent extends BaseLockComponent { }, 100); } - async unlockBiometric(): Promise { + override async unlockBiometric(): Promise { if (!this.biometricLock) { return; } diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html new file mode 100644 index 00000000000..32e3ea0c598 --- /dev/null +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.html @@ -0,0 +1,108 @@ +
+
+

+ {{ "loginInitiated" | i18n }} +

+
+ + +
diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..7fac9a42b06 --- /dev/null +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,18 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; + +@Component({ + selector: "browser-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/tabs/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } +} diff --git a/apps/browser/src/auth/popup/login-with-device.component.html b/apps/browser/src/auth/popup/login-with-device.component.html index d794b7d212b..127f7ec96fe 100644 --- a/apps/browser/src/auth/popup/login-with-device.component.html +++ b/apps/browser/src/auth/popup/login-with-device.component.html @@ -5,32 +5,57 @@
diff --git a/apps/browser/src/auth/popup/login-with-device.component.ts b/apps/browser/src/auth/popup/login-with-device.component.ts index cf0e57b5ee5..f3a1dfffaa0 100644 --- a/apps/browser/src/auth/popup/login-with-device.component.ts +++ b/apps/browser/src/auth/popup/login-with-device.component.ts @@ -1,10 +1,13 @@ +import { Location } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -42,7 +45,10 @@ export class LoginWithDeviceComponent validationService: ValidationService, stateService: StateService, loginService: LoginService, - syncService: SyncService + syncService: SyncService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private location: Location ) { super( router, @@ -59,10 +65,16 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); super.onSuccessfulLogin = async () => { await syncService.fullSync(true); }; } + + protected back() { + this.location.back(); + } } diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login.component.ts index 776b792fa1d..d9b789e4d81 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login.component.ts @@ -4,8 +4,8 @@ import { ActivatedRoute, Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; diff --git a/apps/browser/src/auth/popup/register.component.ts b/apps/browser/src/auth/popup/register.component.ts index ad7a9e1cc68..1599af2216c 100644 --- a/apps/browser/src/auth/popup/register.component.ts +++ b/apps/browser/src/auth/popup/register.component.ts @@ -4,7 +4,6 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -15,6 +14,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-register", @@ -38,7 +38,7 @@ export class RegisterComponent extends BaseRegisterComponent { environmentService: EnvironmentService, logService: LogService, auditService: AuditService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( formValidationErrorService, diff --git a/apps/browser/src/auth/popup/services/index.ts b/apps/browser/src/auth/popup/services/index.ts index 06bfe0009bd..63563f61fd9 100644 --- a/apps/browser/src/auth/popup/services/index.ts +++ b/apps/browser/src/auth/popup/services/index.ts @@ -1,2 +1 @@ -export { LockGuardService } from "./lock-guard.service"; export { UnauthGuardService } from "./unauth-guard.service"; diff --git a/apps/browser/src/auth/popup/services/lock-guard.service.ts b/apps/browser/src/auth/popup/services/lock-guard.service.ts deleted file mode 100644 index ef6ebc73aca..00000000000 --- a/apps/browser/src/auth/popup/services/lock-guard.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { LockGuard as BaseLockGuardService } from "@bitwarden/angular/auth/guards/lock.guard"; - -@Injectable() -export class LockGuardService extends BaseLockGuardService { - protected homepage = "tabs/current"; -} diff --git a/apps/browser/src/auth/popup/services/unauth-guard.service.ts b/apps/browser/src/auth/popup/services/unauth-guard.service.ts index 4aa700b5568..062239a7d36 100644 --- a/apps/browser/src/auth/popup/services/unauth-guard.service.ts +++ b/apps/browser/src/auth/popup/services/unauth-guard.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; -import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards/unauth.guard"; +import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards"; @Injectable() export class UnauthGuardService extends BaseUnauthGuardService { diff --git a/apps/browser/src/auth/popup/set-password.component.ts b/apps/browser/src/auth/popup/set-password.component.ts index 83ec3572737..73b092f32e0 100644 --- a/apps/browser/src/auth/popup/set-password.component.ts +++ b/apps/browser/src/auth/popup/set-password.component.ts @@ -2,7 +2,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/components/set-password.component"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -15,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-set-password", @@ -36,7 +36,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { route: ActivatedRoute, organizationApiService: OrganizationApiServiceAbstraction, organizationUserService: OrganizationUserService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( i18nService, diff --git a/apps/browser/src/auth/popup/sso.component.ts b/apps/browser/src/auth/popup/sso.component.ts index 2214e91687a..9d04701680e 100644 --- a/apps/browser/src/auth/popup/sso.component.ts +++ b/apps/browser/src/auth/popup/sso.component.ts @@ -1,11 +1,12 @@ -import { Component } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -35,7 +36,8 @@ export class SsoComponent extends BaseSsoComponent { syncService: SyncService, environmentService: EnvironmentService, logService: LogService, - private vaultTimeoutService: VaultTimeoutService + configService: ConfigServiceAbstraction, + @Inject(WINDOW) private win: Window ) { super( authService, @@ -48,7 +50,8 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService, environmentService, passwordGenerationService, - logService + logService, + configService ); const url = this.environmentService.getWebVaultUrl(); @@ -57,15 +60,22 @@ export class SsoComponent extends BaseSsoComponent { this.clientId = "browser"; super.onSuccessfulLogin = async () => { - await syncService.fullSync(true); + syncService.fullSync(true); // If the vault is unlocked then this will clear keys from memory, which we don't want to do if ((await this.authService.getAuthStatus()) !== AuthenticationStatus.Unlocked) { BrowserApi.reloadOpenWindows(); } - const thisWindow = window.open("", "_self"); - thisWindow.close(); + this.win.close(); + }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTdeNavigate = async () => { + this.win.close(); }; } } diff --git a/apps/browser/src/auth/popup/two-factor.component.ts b/apps/browser/src/auth/popup/two-factor.component.ts index 21a2bd42f85..c0af31d25e5 100644 --- a/apps/browser/src/auth/popup/two-factor.component.ts +++ b/apps/browser/src/auth/popup/two-factor.component.ts @@ -1,9 +1,9 @@ -import { Component } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; @@ -11,6 +11,7 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -18,6 +19,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; import { BrowserApi } from "../../platform/browser/browser-api"; import { PopupUtilsService } from "../../popup/services/popup-utils.service"; @@ -48,7 +50,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { twoFactorService: TwoFactorService, appIdService: AppIdService, loginService: LoginService, - private dialogService: DialogServiceAbstraction + configService: ConfigServiceAbstraction, + private dialogService: DialogService, + @Inject(WINDOW) protected win: Window ) { super( authService, @@ -56,19 +60,28 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService, apiService, platformUtilsService, - window, + win, environmentService, stateService, route, logService, twoFactorService, appIdService, - loginService + loginService, + configService ); - super.onSuccessfulLogin = () => { - this.loginService.clearValues(); - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTdeNavigate = async () => { + this.win.close(); + }; + super.successRoute = "/tabs/vault"; // FIXME: Chromium 110 has broken WebAuthn support in extensions via an iframe this.webAuthnNewTab = true; @@ -107,7 +120,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "warning" }, content: { key: "popup2faCloseMessage" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (confirmed) { this.popupUtilsService.popOut(window); @@ -117,11 +130,11 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.queryParams.pipe(first()).subscribe(async (qParams) => { if (qParams.sso === "true") { - super.onSuccessfulLogin = () => { + super.onSuccessfulLogin = async () => { // This is not awaited so we don't pause the application while the sync is happening. // This call is executed by the service that lives in the background script so it will continue // the sync even if this tab closes. - const syncPromise = this.syncService.fullSync(true); + this.syncService.fullSync(true); // Force sidebars (FF && Opera) to reload while exempting current window // because we are just going to close the current window. @@ -130,8 +143,6 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { // We don't need this window anymore because the intent is for the user to be left // on the web vault screen which tells them to continue in the browser extension (sidebar or popup) BrowserApi.closeBitwardenExtensionTab(); - - return syncPromise; }; } }); diff --git a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts index a802fd8cf1c..efa5bdcffbb 100644 --- a/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts +++ b/apps/browser/src/autofill/background/service_factories/autofill-service.factory.ts @@ -2,6 +2,10 @@ import { TotpServiceInitOptions, totpServiceFactory, } from "../../../auth/background/service-factories/totp-service.factory"; +import { + UserVerificationServiceInitOptions, + userVerificationServiceFactory, +} from "../../../auth/background/service-factories/user-verification-service.factory"; import { EventCollectionServiceInitOptions, eventCollectionServiceFactory, @@ -38,7 +42,8 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions & TotpServiceInitOptions & EventCollectionServiceInitOptions & LogServiceInitOptions & - SettingsServiceInitOptions; + SettingsServiceInitOptions & + UserVerificationServiceInitOptions; export function autofillServiceFactory( cache: { autofillService?: AbstractAutoFillService } & CachedServices, @@ -55,7 +60,8 @@ export function autofillServiceFactory( await totpServiceFactory(cache, opts), await eventCollectionServiceFactory(cache, opts), await logServiceFactory(cache, opts), - await settingsServiceFactory(cache, opts) + await settingsServiceFactory(cache, opts), + await userVerificationServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts index 53d3d100769..dbe391ce4ab 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; @@ -13,6 +14,7 @@ describe("CipherContextMenuHandler", () => { let mainContextMenuHandler: MockProxy; let authService: MockProxy; let cipherService: MockProxy; + let userVerificationService: MockProxy; let sut: CipherContextMenuHandler; @@ -20,10 +22,17 @@ describe("CipherContextMenuHandler", () => { mainContextMenuHandler = mock(); authService = mock(); cipherService = mock(); + userVerificationService = mock(); + userVerificationService.hasMasterPassword.mockResolvedValue(true); jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue(); - sut = new CipherContextMenuHandler(mainContextMenuHandler, authService, cipherService); + sut = new CipherContextMenuHandler( + mainContextMenuHandler, + authService, + cipherService, + userVerificationService + ); }); afterEach(() => jest.resetAllMocks()); @@ -83,11 +92,11 @@ describe("CipherContextMenuHandler", () => { }; cipherService.getAllDecryptedForUrl.mockResolvedValue([ - null, - undefined, - { type: CipherType.Card }, - { type: CipherType.Login, reprompt: CipherRepromptType.Password }, - realCipher, + null, // invalid cipher + undefined, // invalid cipher + { type: CipherType.Card }, // invalid cipher + { type: CipherType.Login, reprompt: CipherRepromptType.Password }, // invalid cipher + realCipher, // valid cipher ] as any[]); await sut.update("https://test.com"); @@ -96,7 +105,7 @@ describe("CipherContextMenuHandler", () => { expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); - expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(1); + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2); expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( "Test Cipher (Test Username)", @@ -105,5 +114,61 @@ describe("CipherContextMenuHandler", () => { realCipher ); }); + + it("adds ciphers with master password reprompt if the user does not have a master password", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); + + // User does not have a master password, or has one but hasn't logged in with it (key connector user or TDE user) + userVerificationService.hasMasterPasswordAndMasterKeyHash.mockResolvedValue(false); + + mainContextMenuHandler.init.mockResolvedValue(true); + + const realCipher = { + id: "5", + type: CipherType.Login, + reprompt: CipherRepromptType.None, + name: "Test Cipher", + login: { username: "Test Username" }, + }; + + const repromptCipher = { + id: "6", + type: CipherType.Login, + reprompt: CipherRepromptType.Password, + name: "Test Reprompt Cipher", + login: { username: "Test Username" }, + }; + + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + null, // invalid cipher + undefined, // invalid cipher + { type: CipherType.Card }, // invalid cipher + repromptCipher, // valid cipher + realCipher, // valid cipher + ] as any[]); + + await sut.update("https://test.com"); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); + + // Should call this twice, once for each valid cipher + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( + "Test Cipher (Test Username)", + "5", + "https://test.com", + realCipher + ); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( + "Test Reprompt Cipher (Test Username)", + "6", + "https://test.com", + repromptCipher + ); + }); }); }); diff --git a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts index d4f72e56633..1d1be8f8386 100644 --- a/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts +++ b/apps/browser/src/autofill/browser/cipher-context-menu-handler.ts @@ -1,10 +1,10 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -12,6 +12,7 @@ import { authServiceFactory, AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; +import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory"; import { Account } from "../../models/account"; import { CachedServices } from "../../platform/background/service-factories/factory-options"; import { BrowserApi } from "../../platform/browser/browser-api"; @@ -38,7 +39,8 @@ export class CipherContextMenuHandler { constructor( private mainContextMenuHandler: MainContextMenuHandler, private authService: AuthService, - private cipherService: CipherService + private cipherService: CipherService, + private userVerificationService: UserVerificationService ) {} static async create(cachedServices: CachedServices) { @@ -77,7 +79,8 @@ export class CipherContextMenuHandler { return new CipherContextMenuHandler( await MainContextMenuHandler.mv3Create(cachedServices), await authServiceFactory(cachedServices, serviceOptions), - await cipherServiceFactory(cachedServices, serviceOptions) + await cipherServiceFactory(cachedServices, serviceOptions), + await userVerificationServiceFactory(cachedServices, serviceOptions) ); } @@ -180,7 +183,7 @@ export class CipherContextMenuHandler { if ( cipher == null || cipher.type !== CipherType.Login || - cipher.reprompt !== CipherRepromptType.None + (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) ) { return; } diff --git a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts index a5cf1c70655..38e605abe70 100644 --- a/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts +++ b/apps/browser/src/autofill/browser/context-menu-clicked-handler.ts @@ -203,17 +203,45 @@ export class ContextMenuClickedHandler { if (tab == null) { return; } - await this.autofillAction(tab, cipher); + + if (cipher.reprompt !== CipherRepromptType.None) { + await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { + cipherId: cipher.id, + action: AUTOFILL_ID, + }); + } else { + await this.autofillAction(tab, cipher); + } + break; case COPY_USERNAME_ID: this.copyToClipboard({ text: cipher.login.username, tab: tab }); break; case COPY_PASSWORD_ID: - this.copyToClipboard({ text: cipher.login.password, tab: tab }); - this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); + if (cipher.reprompt !== CipherRepromptType.None) { + await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { + cipherId: cipher.id, + action: COPY_PASSWORD_ID, + }); + } else { + this.copyToClipboard({ text: cipher.login.password, tab: tab }); + this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); + } + break; case COPY_VERIFICATIONCODE_ID: - this.copyToClipboard({ text: await this.totpService.getCode(cipher.login.totp), tab: tab }); + if (cipher.reprompt !== CipherRepromptType.None) { + await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { + cipherId: cipher.id, + action: COPY_VERIFICATIONCODE_ID, + }); + } else { + this.copyToClipboard({ + text: await this.totpService.getCode(cipher.login.totp), + tab: tab, + }); + } + break; } } diff --git a/apps/browser/src/autofill/content/message_handler.ts b/apps/browser/src/autofill/content/message_handler.ts index 5302a5042be..5ef0abdb7cc 100644 --- a/apps/browser/src/autofill/content/message_handler.ts +++ b/apps/browser/src/autofill/content/message_handler.ts @@ -28,6 +28,7 @@ window.addEventListener( const forwardCommands = [ "promptForLogin", + "passwordReprompt", "addToLockedVaultPendingNotifications", "unlockCompleted", "addedCipher", diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index 9ced0104b8e..571b4af7213 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -1,6 +1,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { EventType, FieldType, UriMatchType } from "@bitwarden/common/enums"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -45,7 +46,8 @@ export default class AutofillService implements AutofillServiceInterface { private totpService: TotpService, private eventCollectionService: EventCollectionService, private logService: LogService, - private settingsService: SettingsService + private settingsService: SettingsService, + private userVerificationService: UserVerificationService ) {} getFormsWithPasswordFields(pageDetails: AutofillPageDetails): FormData[] { @@ -234,7 +236,19 @@ export default class AutofillService implements AutofillServiceInterface { } } - if (cipher == null || cipher.reprompt !== CipherRepromptType.None) { + if (cipher == null) { + return null; + } + + if ( + cipher.reprompt !== CipherRepromptType.None && + (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash()) + ) { + await BrowserApi.tabSendMessageData(tab, "passwordReprompt", { + cipherId: cipher.id, + action: "autofill", + }); + return null; } diff --git a/apps/browser/src/background/commands.background.ts b/apps/browser/src/background/commands.background.ts index 118953b9da0..0cbf91c6666 100644 --- a/apps/browser/src/background/commands.background.ts +++ b/apps/browser/src/background/commands.background.ts @@ -1,4 +1,4 @@ -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 3854d2383b4..7200301c795 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -1,5 +1,5 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 69335ca5586..03b297aecea 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1,27 +1,33 @@ import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; +import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service"; import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service"; -import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction"; import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { AuthRequestCryptoServiceImplementation } from "@bitwarden/common/auth/services/auth-request-crypto.service.implementation"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; +import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service"; @@ -60,12 +66,13 @@ import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/we import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service"; import { ApiService } from "@bitwarden/common/services/api.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; +import { DevicesServiceImplementation } from "@bitwarden/common/services/devices/devices.service.implementation"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { TotpService } from "@bitwarden/common/services/totp.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; import { PasswordGenerationService, PasswordGenerationServiceAbstraction, @@ -136,7 +143,7 @@ import { PopupUtilsService } from "../popup/services/popup-utils.service"; import { BrowserSendService } from "../services/browser-send.service"; import { BrowserSettingsService } from "../services/browser-settings.service"; import { BrowserFido2UserInterfaceService } from "../services/fido2/browser-fido2-user-interface.service"; -import VaultTimeoutService from "../services/vaultTimeout/vaultTimeout.service"; +import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service"; import { BrowserFolderService } from "../vault/services/browser-folder.service"; import { VaultFilterService } from "../vault/services/vault-filter.service"; @@ -164,7 +171,7 @@ export default class MainBackground { cipherService: CipherServiceAbstraction; folderService: InternalFolderServiceAbstraction; collectionService: CollectionServiceAbstraction; - vaultTimeoutService: VaultTimeoutServiceAbstraction; + vaultTimeoutService: VaultTimeoutService; vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction; syncService: SyncServiceAbstraction; passwordGenerationService: PasswordGenerationServiceAbstraction; @@ -207,6 +214,10 @@ export default class MainBackground { cipherContextMenuHandler: CipherContextMenuHandler; configService: ConfigServiceAbstraction; configApiService: ConfigApiServiceAbstraction; + devicesApiService: DevicesApiServiceAbstraction; + devicesService: DevicesServiceAbstraction; + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction; + authRequestCryptoService: AuthRequestCryptoServiceAbstraction; popupUtilsService: PopupUtilsService; browserPopoutWindowService: BrowserPopoutWindowService; @@ -399,6 +410,23 @@ export default class MainBackground { that.runtimeBackground.processMessage(message, that as any); }; })(); + + this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); + this.deviceTrustCryptoService = new DeviceTrustCryptoService( + this.cryptoFunctionService, + this.cryptoService, + this.encryptService, + this.stateService, + this.appIdService, + this.devicesApiService, + this.i18nService, + this.platformUtilsService + ); + + this.devicesService = new DevicesServiceImplementation(this.devicesApiService); + + this.authRequestCryptoService = new AuthRequestCryptoServiceImplementation(this.cryptoService); + this.authService = new AuthService( this.cryptoService, this.apiService, @@ -414,14 +442,26 @@ export default class MainBackground { this.i18nService, this.encryptService, this.passwordStrengthService, - this.policyService + this.policyService, + this.deviceTrustCryptoService, + this.authRequestCryptoService + ); + + this.userVerificationApiService = new UserVerificationApiService(this.apiService); + + this.userVerificationService = new UserVerificationService( + this.stateService, + this.cryptoService, + this.i18nService, + this.userVerificationApiService ); this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService( this.cryptoService, this.tokenService, this.policyService, - this.stateService + this.stateService, + this.userVerificationService ); this.vaultTimeoutService = new VaultTimeoutService( @@ -432,7 +472,6 @@ export default class MainBackground { this.platformUtilsService, this.messagingService, this.searchService, - this.keyConnectorService, this.stateService, this.authService, this.vaultTimeoutSettingsService, @@ -483,13 +522,15 @@ export default class MainBackground { this.eventUploadService ); this.totpService = new TotpService(this.cryptoFunctionService, this.logService); + this.autofillService = new AutofillService( this.cipherService, this.stateService, this.totpService, this.eventCollectionService, this.logService, - this.settingsService + this.settingsService, + this.userVerificationService ); this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); this.exportService = new VaultExportService( @@ -511,15 +552,6 @@ export default class MainBackground { this.authService, this.messagingService ); - - this.userVerificationApiService = new UserVerificationApiService(this.apiService); - - this.userVerificationService = new UserVerificationService( - this.cryptoService, - this.i18nService, - this.userVerificationApiService - ); - this.configApiService = new ConfigApiService(this.apiService, this.authService); this.configService = new ConfigService( @@ -528,6 +560,7 @@ export default class MainBackground { this.authService, this.environmentService ); + this.browserPopoutWindowService = new BrowserPopoutWindowService(); this.popupUtilsService = new PopupUtilsService(this.isPrivateMode); @@ -669,7 +702,8 @@ export default class MainBackground { this.cipherContextMenuHandler = new CipherContextMenuHandler( this.mainContextMenuHandler, this.authService, - this.cipherService + this.cipherService, + this.userVerificationService ); } } @@ -679,7 +713,7 @@ export default class MainBackground { await this.stateService.init(); - await (this.vaultTimeoutService as VaultTimeoutService).init(true); + await this.vaultTimeoutService.init(true); await (this.i18nService as BrowserI18nService).init(); await (this.eventUploadService as EventUploadService).init(true); await this.runtimeBackground.init(); diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 742cd39f15c..8f8705b41c7 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -10,7 +10,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { + MasterKey, + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { BrowserApi } from "../platform/browser/browser-api"; @@ -42,6 +46,7 @@ type ReceiveMessage = { // Unlock key keyB64?: string; + userKeyB64?: string; }; type ReceiveMessageOuter = { @@ -320,16 +325,55 @@ export class NativeMessagingBackground { } if (message.response === "unlocked") { - await this.cryptoService.setKey( - new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64)) - ); + try { + if (message.userKeyB64) { + const userKey = new SymmetricCryptoKey( + Utils.fromB64ToArray(message.userKeyB64) + ) as UserKey; + await this.cryptoService.setUserKey(userKey); + } else if (message.keyB64) { + // Backwards compatibility to support cases in which the user hasn't updated their desktop app + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472) + let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + encUserKey ||= await this.stateService.getMasterKeyEncryptedUserKey(); + if (!encUserKey) { + throw new Error("No encrypted user key found"); + } + const masterKey = new SymmetricCryptoKey( + Utils.fromB64ToArray(message.keyB64) + ) as MasterKey; + const userKey = await this.cryptoService.decryptUserKeyWithMasterKey( + masterKey, + new EncString(encUserKey) + ); + await this.cryptoService.setMasterKey(masterKey); + await this.cryptoService.setUserKey(userKey); + } else { + throw new Error("No key received"); + } + } catch (e) { + this.logService.error("Unable to set key: " + e); + this.messagingService.send("showDialog", { + title: { key: "biometricsFailedTitle" }, + content: { key: "biometricsFailedDesc" }, + acceptButtonText: { key: "ok" }, + cancelButtonText: null, + type: "danger", + }); + + // Exit early + if (this.resolver) { + this.resolver(message); + } + return; + } // Verify key is correct by attempting to decrypt a secret try { await this.cryptoService.getFingerprint(await this.stateService.getUserId()); } catch (e) { this.logService.error("Unable to verify key: " + e); - await this.cryptoService.clearKey(); + await this.cryptoService.clearKeys(); this.showWrongUserDialog(); // Exit early diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 3c2ac7a96cc..f8754798241 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -52,7 +52,11 @@ export default class RuntimeBackground { sender: chrome.runtime.MessageSender, sendResponse: any ) => { - const messagesWithResponse = ["fido2RegisterCredentialRequest", "fido2GetCredentialRequest"]; + const messagesWithResponse = [ + "checkFido2FeatureEnabled", + "fido2RegisterCredentialRequest", + "fido2GetCredentialRequest", + ]; if (messagesWithResponse.includes(msg.command)) { this.processMessage(msg, sender).then( @@ -75,6 +79,8 @@ export default class RuntimeBackground { } async processMessage(msg: any, sender: chrome.runtime.MessageSender) { + const cipherId = msg.data?.cipherId; + switch (msg.command) { case "loggedIn": case "unlocked": { @@ -82,7 +88,7 @@ export default class RuntimeBackground { if (this.lockedVaultPendingNotifications?.length > 0) { item = this.lockedVaultPendingNotifications.pop(); - await this.browserPopoutWindowService.closeLoginPrompt(); + await this.browserPopoutWindowService.closeUnlockPrompt(); } await this.main.refreshBadge(); @@ -122,13 +128,22 @@ export default class RuntimeBackground { break; case "promptForLogin": case "bgReopenPromptForLogin": - await this.browserPopoutWindowService.openLoginPrompt(sender.tab?.windowId); + await this.browserPopoutWindowService.openUnlockPrompt(sender.tab?.windowId); + break; + case "passwordReprompt": + if (cipherId) { + await this.browserPopoutWindowService.openPasswordRepromptPrompt(sender.tab?.windowId, { + cipherId: cipherId, + senderTabId: sender.tab.id, + action: msg.data?.action, + }); + } break; case "openAddEditCipher": { const addEditCipherUrl = - msg.data?.cipherId == null + cipherId == null ? "popup/index.html#/edit-cipher" - : "popup/index.html#/edit-cipher?cipherId=" + msg.data.cipherId; + : "popup/index.html#/edit-cipher?cipherId=" + cipherId; BrowserApi.openBitwardenExtensionTab(addEditCipherUrl, true); break; @@ -233,6 +248,8 @@ export default class RuntimeBackground { case "fido2AbortRequest": this.abortControllers.get(msg.abortedRequestId)?.abort(); break; + case "checkFido2FeatureEnabled": + return await this.main.fido2ClientService.isFido2FeatureEnabled(); case "fido2RegisterCredentialRequest": return await this.main.fido2ClientService .createCredential(msg.data, this.createAbortController(msg.requestId)) diff --git a/apps/browser/src/background/service-factories/devices-api-service.factory.ts b/apps/browser/src/background/service-factories/devices-api-service.factory.ts new file mode 100644 index 00000000000..8999b7c2c72 --- /dev/null +++ b/apps/browser/src/background/service-factories/devices-api-service.factory.ts @@ -0,0 +1,28 @@ +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; + +import { + ApiServiceInitOptions, + apiServiceFactory, +} from "../../platform/background/service-factories/api-service.factory"; +import { + FactoryOptions, + CachedServices, + factory, +} from "../../platform/background/service-factories/factory-options"; + +type DevicesApiServiceFactoryOptions = FactoryOptions; + +export type DevicesApiServiceInitOptions = DevicesApiServiceFactoryOptions & ApiServiceInitOptions; + +export function devicesApiServiceFactory( + cache: { devicesApiService?: DevicesApiServiceAbstraction } & CachedServices, + opts: DevicesApiServiceInitOptions +): Promise { + return factory( + cache, + "devicesApiService", + opts, + async () => new DevicesApiServiceImplementation(await apiServiceFactory(cache, opts)) + ); +} diff --git a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts index 601867ad385..b019db3297d 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-service.factory.ts @@ -1,13 +1,9 @@ -import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService as AbstractVaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { authServiceFactory, AuthServiceInitOptions, } from "../../auth/background/service-factories/auth-service.factory"; -import { - keyConnectorServiceFactory, - KeyConnectorServiceInitOptions, -} from "../../auth/background/service-factories/key-connector-service.factory"; import { CryptoServiceInitOptions, cryptoServiceFactory, @@ -29,7 +25,7 @@ import { StateServiceInitOptions, stateServiceFactory, } from "../../platform/background/service-factories/state-service.factory"; -import VaultTimeoutService from "../../services/vaultTimeout/vaultTimeout.service"; +import VaultTimeoutService from "../../services/vault-timeout/vault-timeout.service"; import { cipherServiceFactory, CipherServiceInitOptions, @@ -64,7 +60,6 @@ export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions & PlatformUtilsServiceInitOptions & MessagingServiceInitOptions & SearchServiceInitOptions & - KeyConnectorServiceInitOptions & StateServiceInitOptions & AuthServiceInitOptions & VaultTimeoutSettingsServiceInitOptions; @@ -86,7 +81,6 @@ export function vaultTimeoutServiceFactory( await platformUtilsServiceFactory(cache, opts), await messagingServiceFactory(cache, opts), await searchServiceFactory(cache, opts), - await keyConnectorServiceFactory(cache, opts), await stateServiceFactory(cache, opts), await authServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts index 724993127b7..eda86c0a156 100644 --- a/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts +++ b/apps/browser/src/background/service-factories/vault-timeout-settings-service.factory.ts @@ -1,5 +1,5 @@ -import { VaultTimeoutSettingsService as AbstractVaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService as AbstractVaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; import { policyServiceFactory, @@ -9,6 +9,10 @@ import { tokenServiceFactory, TokenServiceInitOptions, } from "../../auth/background/service-factories/token-service.factory"; +import { + userVerificationServiceFactory, + UserVerificationServiceInitOptions, +} from "../../auth/background/service-factories/user-verification-service.factory"; import { CryptoServiceInitOptions, cryptoServiceFactory, @@ -29,7 +33,8 @@ export type VaultTimeoutSettingsServiceInitOptions = VaultTimeoutSettingsService CryptoServiceInitOptions & TokenServiceInitOptions & PolicyServiceInitOptions & - StateServiceInitOptions; + StateServiceInitOptions & + UserVerificationServiceInitOptions; export function vaultTimeoutSettingsServiceFactory( cache: { vaultTimeoutSettingsService?: AbstractVaultTimeoutSettingsService } & CachedServices, @@ -44,7 +49,8 @@ export function vaultTimeoutSettingsServiceFactory( await cryptoServiceFactory(cache, opts), await tokenServiceFactory(cache, opts), await policyServiceFactory(cache, opts), - await stateServiceFactory(cache, opts) + await stateServiceFactory(cache, opts), + await userVerificationServiceFactory(cache, opts) ) ); } diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 4eadf4bb552..bbbcd752e62 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.7.1", + "version": "2023.8.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 2a366053ce3..ce21088709a 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2023.7.1", + "version": "2023.8.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index b93781aa977..391f8830301 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -45,11 +45,20 @@ export class BrowserApi { }); } - static async getTab(tabId: number) { - if (tabId == null) { + static async getTab(tabId: number): Promise | null { + if (!tabId) { return null; } - return await chrome.tabs.get(tabId); + + if (BrowserApi.manifestVersion === 3) { + return await chrome.tabs.get(tabId); + } + + return new Promise((resolve) => + chrome.tabs.get(tabId, (tab) => { + resolve(tab); + }) + ); } static async getTabFromCurrentWindow(): Promise | null { diff --git a/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts b/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts index ca22e369d80..0b3f55ee990 100644 --- a/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts +++ b/apps/browser/src/platform/popup/abstractions/browser-popout-window.service.ts @@ -1,6 +1,15 @@ interface BrowserPopoutWindowService { - openLoginPrompt(senderWindowId: number): Promise; - closeLoginPrompt(): Promise; + openUnlockPrompt(senderWindowId: number): Promise; + closeUnlockPrompt(): Promise; + openPasswordRepromptPrompt( + senderWindowId: number, + promptData: { + action: string; + cipherId: string; + senderTabId: number; + } + ): Promise; + closePasswordRepromptPrompt(): Promise; } export { BrowserPopoutWindowService }; diff --git a/apps/browser/src/platform/popup/browser-popout-window.service.ts b/apps/browser/src/platform/popup/browser-popout-window.service.ts index bfec3e690ba..95be15cc20d 100644 --- a/apps/browser/src/platform/popup/browser-popout-window.service.ts +++ b/apps/browser/src/platform/popup/browser-popout-window.service.ts @@ -11,20 +11,48 @@ class BrowserPopoutWindowService implements BrowserPopupWindowServiceInterface { height: 800, }; - async openLoginPrompt(senderWindowId: number) { - await this.closeLoginPrompt(); - await this.openPopoutWindow( + async openUnlockPrompt(senderWindowId: number) { + await this.closeUnlockPrompt(); + await this.openSingleActionPopout( senderWindowId, "popup/index.html?uilocation=popout", - "loginPrompt" + "unlockPrompt" ); } - async closeLoginPrompt() { - await this.closeSingleActionPopout("loginPrompt"); + async closeUnlockPrompt() { + await this.closeSingleActionPopout("unlockPrompt"); } - private async openPopoutWindow( + async openPasswordRepromptPrompt( + senderWindowId: number, + { + cipherId, + senderTabId, + action, + }: { + cipherId: string; + senderTabId: number; + action: string; + } + ) { + await this.closePasswordRepromptPrompt(); + + const promptWindowPath = + "popup/index.html#/view-cipher" + + "?uilocation=popout" + + `&cipherId=${cipherId}` + + `&senderTabId=${senderTabId}` + + `&action=${action}`; + + await this.openSingleActionPopout(senderWindowId, promptWindowPath, "passwordReprompt"); + } + + async closePasswordRepromptPrompt() { + await this.closeSingleActionPopout("passwordReprompt"); + } + + private async openSingleActionPopout( senderWindowId: number, popupWindowURL: string, singleActionPopoutKey: string diff --git a/apps/browser/src/platform/services/browser-crypto.service.ts b/apps/browser/src/platform/services/browser-crypto.service.ts index 1018c270cb2..a6dce0f39e9 100644 --- a/apps/browser/src/platform/services/browser-crypto.service.ts +++ b/apps/browser/src/platform/services/browser-crypto.service.ts @@ -1,13 +1,32 @@ import { KeySuffixOptions } from "@bitwarden/common/enums"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; export class BrowserCryptoService extends CryptoService { - protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions) { - if (keySuffix === "biometric") { + override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + return await this.stateService.getBiometricUnlock({ userId: userId }); + } + return super.hasUserKeyStored(keySuffix, userId); + } + + /** + * Browser doesn't store biometric keys, so we retrieve them from the desktop and return + * if we successfully saved it into memory as the User Key + */ + protected override async getKeyFromStorage(keySuffix: KeySuffixOptions): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { await this.platformUtilService.authenticateBiometric(); - return (await this.getKey())?.keyB64; + const userKey = await this.stateService.getUserKey(); + if (userKey) { + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey.keyB64)) as UserKey; + } } - return await super.retrieveKeyFromStorage(keySuffix); + return await super.getKeyFromStorage(keySuffix); } } diff --git a/apps/browser/src/platform/services/browser-state.service.spec.ts b/apps/browser/src/platform/services/browser-state.service.spec.ts index 874e13b7d8f..d6bb83f7fb5 100644 --- a/apps/browser/src/platform/services/browser-state.service.spec.ts +++ b/apps/browser/src/platform/services/browser-state.service.spec.ts @@ -41,7 +41,8 @@ describe("Browser State Service", () => { logService = mock(); stateMigrationService = mock(); stateFactory = mock(); - useAccountCache = true; + // turn off account cache for tests + useAccountCache = false; state = new State(new GlobalState()); state.accounts[userId] = new Account({ diff --git a/apps/browser/src/platform/services/browser-state.service.ts b/apps/browser/src/platform/services/browser-state.service.ts index 37f50d6dc77..34fa1a1d0f3 100644 --- a/apps/browser/src/platform/services/browser-state.service.ts +++ b/apps/browser/src/platform/services/browser-state.service.ts @@ -1,5 +1,12 @@ import { BehaviorSubject } from "rxjs"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { StateMigrationService } from "@bitwarden/common/platform/abstractions/state-migration.service"; +import { + AbstractStorageService, + AbstractMemoryStorageService, +} from "@bitwarden/common/platform/abstractions/storage.service"; +import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { StorageOptions } from "@bitwarden/common/platform/models/domain/storage-options"; import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service"; @@ -26,14 +33,44 @@ export class BrowserStateService protected activeAccountSubject: BehaviorSubject; @sessionSync({ initializer: (b: boolean) => b }) protected activeAccountUnlockedSubject: BehaviorSubject; - @sessionSync({ - initializer: Account.fromJSON as any, // TODO: Remove this any when all any types are removed from Account - initializeAs: "record", - }) - protected accountDiskCache: BehaviorSubject>; protected accountDeserializer = Account.fromJSON; + constructor( + storageService: AbstractStorageService, + secureStorageService: AbstractStorageService, + memoryStorageService: AbstractMemoryStorageService, + logService: LogService, + stateMigrationService: StateMigrationService, + stateFactory: StateFactory, + useAccountCache = true + ) { + super( + storageService, + secureStorageService, + memoryStorageService, + logService, + stateMigrationService, + stateFactory, + useAccountCache + ); + + // TODO: This is a hack to fix having a disk cache on both the popup and + // the background page that can get out of sync. We need to work out the + // best way to handle caching with multiple instances of the state service. + if (useAccountCache) { + chrome.storage.onChanged.addListener((changes, namespace) => { + if (namespace === "local") { + for (const key of Object.keys(changes)) { + if (key !== "accountActivity" && this.accountDiskCache.value[key]) { + this.deleteDiskCache(key); + } + } + } + }); + } + } + async addAccount(account: Account) { // Apply browser overrides to default account values account = new Account(account); @@ -132,4 +169,17 @@ export class BrowserStateService this.reconcileOptions(options, await this.defaultInMemoryOptions()) ); } + + // Overriding the base class to prevent deleting the cache on save. We register a storage listener + // to delete the cache in the constructor above. + protected override async saveAccountToDisk( + account: Account, + options: StorageOptions + ): Promise { + const storageLocation = options.useSecureStorage + ? this.secureStorageService + : this.storageService; + + await storageLocation.save(`${options.userId}`, account, options); + } } diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 360637343bc..002337eb70b 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -1,14 +1,21 @@ import { Injectable, NgModule } from "@angular/core"; import { ActivatedRouteSnapshot, RouteReuseStrategy, RouterModule, Routes } from "@angular/router"; -import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard"; -import { LockGuard } from "@bitwarden/angular/auth/guards/lock.guard"; -import { UnauthGuard } from "@bitwarden/angular/auth/guards/unauth.guard"; +import { + redirectGuard, + AuthGuard, + lockGuard, + tdeDecryptionRequiredGuard, + UnauthGuard, +} from "@bitwarden/angular/auth/guards"; +import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EnvironmentComponent } from "../auth/popup/environment.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; +import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "../auth/popup/login-with-device.component"; import { LoginComponent } from "../auth/popup/login.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -50,8 +57,11 @@ import { TabsComponent } from "./tabs.component"; const routes: Routes = [ { path: "", - redirectTo: "home", pathMatch: "full", + children: [], // Children lets us have an empty component. + canActivate: [ + redirectGuard({ loggedIn: "/tabs/current", loggedOut: "/home", locked: "/lock" }), + ], }, { path: "vault", @@ -77,13 +87,19 @@ const routes: Routes = [ { path: "login-with-device", component: LoginWithDeviceComponent, - canActivate: [UnauthGuard], + canActivate: [], + data: { state: "login-with-device" }, + }, + { + path: "admin-approval-requested", + component: LoginWithDeviceComponent, + canActivate: [], data: { state: "login-with-device" }, }, { path: "lock", component: LockComponent, - canActivate: [LockGuard], + canActivate: [lockGuard()], data: { state: "lock" }, }, { @@ -98,6 +114,14 @@ const routes: Routes = [ canActivate: [UnauthGuard], data: { state: "2fa-options" }, }, + { + path: "login-initiated", + component: LoginDecryptionOptionsComponent, + canActivate: [ + tdeDecryptionRequiredGuard(), + canAccessFeature(FeatureFlag.TrustedDeviceEncryption), + ], + }, { path: "sso", component: SsoComponent, diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 485b283407e..815109c5499 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -12,12 +12,12 @@ import { IndividualConfig, ToastrService } from "ngx-toastr"; import { filter, concatMap, Subject, takeUntil } from "rxjs"; import Swal from "sweetalert2"; -import { DialogServiceAbstraction, SimpleDialogOptions } from "@bitwarden/angular/services/dialog"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; import { BrowserApi } from "../platform/browser/browser-api"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; @@ -50,7 +50,7 @@ export class AppComponent implements OnInit, OnDestroy { private ngZone: NgZone, private sanitizer: DomSanitizer, private platformUtilsService: PlatformUtilsService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index a367c4a152b..7e4e1512844 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -20,6 +20,7 @@ import { EnvironmentComponent } from "../auth/popup/environment.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; import { LockComponent } from "../auth/popup/lock.component"; +import { LoginDecryptionOptionsComponent } from "../auth/popup/login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "../auth/popup/login-with-device.component"; import { LoginComponent } from "../auth/popup/login.component"; import { RegisterComponent } from "../auth/popup/register.component"; @@ -122,6 +123,7 @@ import "../platform/popup/locales"; LockComponent, LoginComponent, LoginWithDeviceComponent, + LoginDecryptionOptionsComponent, OptionsComponent, GeneratorComponent, PasswordGeneratorHistoryComponent, diff --git a/apps/browser/src/popup/components/user-verification.component.html b/apps/browser/src/popup/components/user-verification.component.html index 8d7f1ed8706..25bd81cf394 100644 --- a/apps/browser/src/popup/components/user-verification.component.html +++ b/apps/browser/src/popup/components/user-verification.component.html @@ -1,4 +1,4 @@ - +
- +
- +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+

{{ "loggingInAs" | i18n }} {{ data.userEmail }}

+ {{ "notYou" | i18n }} +
+ +
+
diff --git a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..f64ec977ce7 --- /dev/null +++ b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,19 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; + +@Component({ + selector: "desktop-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + this.messagingService.send("redrawMenu"); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } +} diff --git a/apps/desktop/src/auth/login/login-with-device.component.html b/apps/desktop/src/auth/login/login-with-device.component.html index 29fb1034942..a81c92ad1d1 100644 --- a/apps/desktop/src/auth/login/login-with-device.component.html +++ b/apps/desktop/src/auth/login/login-with-device.component.html @@ -1,40 +1,72 @@
Bitwarden -

{{ "logInInitiated" | i18n }}

-
-
-
-
-

{{ "notificationSentDevice" | i18n }}

-

- {{ "fingerprintMatchInfo" | i18n }} -

-
+ +

{{ "loginInitiated" | i18n }}

-
-

{{ "fingerprintPhraseHeader" | i18n }}

- {{ fingerprintPhrase }} -
+
+
+
+
+

{{ "notificationSentDevice" | i18n }}

+

+ {{ "fingerprintMatchInfo" | i18n }} +

+
- +
+

{{ "fingerprintPhraseHeader" | i18n }}

+ {{ fingerprintPhrase }} +
-
-

- {{ "needAnotherOption" | i18n }} - - {{ "viewAllLoginOptions" | i18n }} - -

+ + +
+

+ {{ "needAnotherOption" | i18n }} + + {{ "viewAllLoginOptions" | i18n }} + +

+
-
+
+ + +

{{ "adminApprovalRequested" | i18n }}

+ +
+
+
+
+

{{ "adminApprovalRequestSentToAdmins" | i18n }}

+

{{ "youWillBeNotifiedOnceApproved" | i18n }}

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+ {{ fingerprintPhrase }} +
+ +
+

+ {{ "troubleLoggingIn" | i18n }} + + {{ "viewAllLoginOptions" | i18n }} + +

+
+
+
+
+
diff --git a/apps/desktop/src/auth/login/login-with-device.component.ts b/apps/desktop/src/auth/login/login-with-device.component.ts index 3b3ee83ae49..daca2d19936 100644 --- a/apps/desktop/src/auth/login/login-with-device.component.ts +++ b/apps/desktop/src/auth/login/login-with-device.component.ts @@ -1,3 +1,4 @@ +import { Location } from "@angular/common"; import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { Router } from "@angular/router"; @@ -5,7 +6,9 @@ import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwa import { ModalService } from "@bitwarden/angular/services/modal.service"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -50,7 +53,10 @@ export class LoginWithDeviceComponent private modalService: ModalService, syncService: SyncService, stateService: StateService, - loginService: LoginService + loginService: LoginService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction, + private location: Location ) { super( router, @@ -67,7 +73,9 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); super.onSuccessfulLogin = () => { @@ -100,7 +108,7 @@ export class LoginWithDeviceComponent super.ngOnDestroy(); } - goToLogin() { - this.router.navigate(["/login"]); + back() { + this.location.back(); } } diff --git a/apps/desktop/src/auth/login/login.component.ts b/apps/desktop/src/auth/login/login.component.ts index 00aa0300f61..45b330f6dae 100644 --- a/apps/desktop/src/auth/login/login.component.ts +++ b/apps/desktop/src/auth/login/login.component.ts @@ -7,8 +7,8 @@ import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; import { ModalService } from "@bitwarden/angular/services/modal.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; diff --git a/apps/desktop/src/auth/login/login.module.ts b/apps/desktop/src/auth/login/login.module.ts index 35ad83e9db0..9fb2b28915a 100644 --- a/apps/desktop/src/auth/login/login.module.ts +++ b/apps/desktop/src/auth/login/login.module.ts @@ -5,12 +5,18 @@ import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components import { SharedModule } from "../../app/shared/shared.module"; +import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "./login-with-device.component"; import { LoginComponent } from "./login.component"; @NgModule({ imports: [SharedModule, RouterModule], - declarations: [LoginComponent, LoginWithDeviceComponent, EnvironmentSelectorComponent], + declarations: [ + LoginComponent, + LoginWithDeviceComponent, + EnvironmentSelectorComponent, + LoginDecryptionOptionsComponent, + ], exports: [LoginComponent, LoginWithDeviceComponent], }) export class LoginModule {} diff --git a/apps/desktop/src/auth/register.component.ts b/apps/desktop/src/auth/register.component.ts index f4324f12379..db38fd4721c 100644 --- a/apps/desktop/src/auth/register.component.ts +++ b/apps/desktop/src/auth/register.component.ts @@ -4,7 +4,6 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -16,6 +15,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { DialogService } from "@bitwarden/components"; const BroadcasterSubscriptionId = "RegisterComponent"; @@ -40,7 +40,7 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit, private ngZone: NgZone, logService: LogService, auditService: AuditService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( formValidationErrorService, diff --git a/apps/desktop/src/auth/set-password.component.ts b/apps/desktop/src/auth/set-password.component.ts index b710e96b7ee..fedd70b3133 100644 --- a/apps/desktop/src/auth/set-password.component.ts +++ b/apps/desktop/src/auth/set-password.component.ts @@ -2,7 +2,6 @@ import { Component, NgZone, OnDestroy } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/components/set-password.component"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -16,6 +15,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; const BroadcasterSubscriptionId = "SetPasswordComponent"; @@ -41,7 +41,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On stateService: StateService, organizationApiService: OrganizationApiServiceAbstraction, organizationUserService: OrganizationUserService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( i18nService, diff --git a/apps/desktop/src/auth/sso.component.ts b/apps/desktop/src/auth/sso.component.ts index 75f380b0ed3..22badf9d69d 100644 --- a/apps/desktop/src/auth/sso.component.ts +++ b/apps/desktop/src/auth/sso.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -30,7 +31,8 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService: CryptoFunctionService, environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationServiceAbstraction, - logService: LogService + logService: LogService, + configService: ConfigServiceAbstraction ) { super( authService, @@ -43,11 +45,17 @@ export class SsoComponent extends BaseSsoComponent { cryptoFunctionService, environmentService, passwordGenerationService, - logService + logService, + configService ); - super.onSuccessfulLogin = () => { - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); + }; + this.redirectUri = "bitwarden://sso-callback"; this.clientId = "desktop"; } diff --git a/apps/desktop/src/auth/two-factor.component.ts b/apps/desktop/src/auth/two-factor.component.ts index ee3764027a5..bf3545820b9 100644 --- a/apps/desktop/src/auth/two-factor.component.ts +++ b/apps/desktop/src/auth/two-factor.component.ts @@ -1,7 +1,8 @@ -import { Component, ViewChild, ViewContainerRef } from "@angular/core"; +import { Component, Inject, ViewChild, ViewContainerRef } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component"; +import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -9,6 +10,7 @@ import { LoginService } from "@bitwarden/common/auth/abstractions/login.service" import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -43,7 +45,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { logService: LogService, twoFactorService: TwoFactorService, appIdService: AppIdService, - loginService: LoginService + loginService: LoginService, + configService: ConfigServiceAbstraction, + @Inject(WINDOW) protected win: Window ) { super( authService, @@ -51,18 +55,22 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService, apiService, platformUtilsService, - window, + win, environmentService, stateService, route, logService, twoFactorService, appIdService, - loginService + loginService, + configService ); - super.onSuccessfulLogin = () => { - this.loginService.clearValues(); - return syncService.fullSync(true); + super.onSuccessfulLogin = async () => { + syncService.fullSync(true); + }; + + super.onSuccessfulLoginTde = async () => { + syncService.fullSync(true); }; } diff --git a/apps/desktop/src/images/bwi-passkey.png b/apps/desktop/src/images/bwi-passkey.png new file mode 100644 index 00000000000..702be33446e Binary files /dev/null and b/apps/desktop/src/images/bwi-passkey.png differ diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 5913597ee88..9200627a107 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Permanent geskrapte item" }, - "restoreItem": { - "message": "Stel item terug" - }, - "restoreItemConfirmation": { - "message": "Is u seker u wil hierdie item terugstel?" - }, "restoredItem": { "message": "Teruggestelde item" }, diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index a65781e73d4..9469fee3f65 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "تم حذف العنصر بشكل دائم" }, - "restoreItem": { - "message": "استعادة العنصر" - }, - "restoreItemConfirmation": { - "message": "هل أنت متأكد من أنك تريد استعادة هذا العنصر؟" - }, "restoredItem": { "message": "العنصر المستعاد" }, diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 3d1608a5490..405e71d6308 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Element birdəfəlik silindi" }, - "restoreItem": { - "message": "Elementi bərpa et" - }, - "restoreItemConfirmation": { - "message": "Elementi bərpa etmək istədiyinizə əminsiniz?" - }, "restoredItem": { "message": "Element bərpa edildi" }, diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index c8f182caeb0..24c955a4983 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Элемент выдалены назаўсёды" }, - "restoreItem": { - "message": "Аднавіць элемент" - }, - "restoreItemConfirmation": { - "message": "Вы сапраўды хочаце аднавіць гэты элемент?" - }, "restoredItem": { "message": "Элемент адноўлены" }, @@ -2253,7 +2247,7 @@ "message": "Рэкамендаваныя налады абнаўлення" }, "loggingInOn": { - "message": "Logging in on" + "message": "Увайсці на" }, "usDomain": { "message": "bitwarden.com" diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 27a7a85fc24..192725c8dbc 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Записът е изтрит окончателно" }, - "restoreItem": { - "message": "Възстановяване на запис" - }, - "restoreItemConfirmation": { - "message": "Сигурни ли сте, че искате да възстановите записа?" - }, "restoredItem": { "message": "Записът е възстановен" }, diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 1c33383916c..1091afe5e1f 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "বস্তুটি স্থায়ীভাবে মুছে ফেলা হয়েছে" }, - "restoreItem": { - "message": "বস্তু পুনরুদ্ধার" - }, - "restoreItemConfirmation": { - "message": "আপনি কি নিশ্চিত যে আপনি এই বস্তুটি পুনরুদ্ধার করতে চান?" - }, "restoredItem": { "message": "বস্তু পুনরুদ্ধারকৃত" }, diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 61ea872f9aa..1e501600751 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index e098741ff5d..85123ba4b64 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Element suprimit definitivament" }, - "restoreItem": { - "message": "Restaura l'element" - }, - "restoreItemConfirmation": { - "message": "Esteu segur que voleu restaurar aquest element?" - }, "restoredItem": { "message": "Element restaurat" }, diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 8cef76fbe57..8792bfa4bbf 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Položka byla trvale smazána" }, - "restoreItem": { - "message": "Obnovit položku" - }, - "restoreItemConfirmation": { - "message": "Opravdu chcete tuto položku obnovit?" - }, "restoredItem": { "message": "Položka byla obnovena" }, diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 9c182c1aeeb..7d5076d8a4d 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Emne slettet permanent" }, - "restoreItem": { - "message": "Gendan emne" - }, - "restoreItemConfirmation": { - "message": "Sikker på, at du vil gendanne dette emne?" - }, "restoredItem": { "message": "Emne gendannet" }, diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index bf9ce7fe4a8..32b90bedf1f 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Eintrag dauerhaft gelöscht" }, - "restoreItem": { - "message": "Eintrag wiederherstellen" - }, - "restoreItemConfirmation": { - "message": "Soll dieser Eintrag wirklich wiederhergestellt werden?" - }, "restoredItem": { "message": "Eintrag wiederhergestellt" }, diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 43f20a5f21d..faf687baaa6 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -1393,7 +1393,7 @@ "message": "Ξεκλειδώστε με το Windows Hello" }, "additionalWindowsHelloSettings": { - "message": "Additional Windows Hello settings" + "message": "Πρόσθετες ρυθμίσεις του Windows Hello" }, "windowsHelloConsentMessage": { "message": "Επαληθεύστε για το Bitwarden." @@ -1408,13 +1408,13 @@ "message": "Ξεκλειδώστε το vault σας" }, "autoPromptWindowsHello": { - "message": "Ζητήστε από τα Windows Γεια σας κατά την έναρξη" + "message": "Εμφάνιση Windows Hello κατά την εκκίνηση της εφαρμογής" }, "autoPromptTouchId": { "message": "Ερώτηση για το Touch ID κατά την εκκίνηση" }, "requirePasswordOnStart": { - "message": "Require password or PIN on app start" + "message": "Να απαιτείται κωδικός πρόσβασης ή PIN κατά την εκκίνηση της εφαρμογής" }, "recommendedForSecurity": { "message": "Συνίσταται για ασφάλεια." @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Μόνιμα Διεγραμμένο Στοιχείο" }, - "restoreItem": { - "message": "Ανάκτηση Στοιχείου" - }, - "restoreItemConfirmation": { - "message": "Είστε βέβαιοι ότι θέλετε να ανακτήσετε αυτό το στοιχείο;" - }, "restoredItem": { "message": "Στοιχείο που έχει Ανακτηθεί" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index ac507762eda..6477cdfb13b 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1492,6 +1492,9 @@ "vaultTimeoutActionLogOutDesc": { "message": "Re-authentication is required to access your vault again." }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, "lock": { "message": "Lock", "description": "Verb form: to make secure or inaccesible by" @@ -1512,12 +1515,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, @@ -2112,8 +2109,8 @@ "logInWithAnotherDevice": { "message": "Log in with another device" }, - "logInInitiated": { - "message": "Log in initiated" + "loginInitiated": { + "message": "Login initiated" }, "notificationSentDevice": { "message": "A notification has been sent to your device." @@ -2252,6 +2249,34 @@ "windowsBiometricUpdateWarningTitle": { "message": "Recommended Settings Update" }, + "deviceApprovalRequired": { + "message": "Device approval required. Select an approval option below:" + }, + "rememberThisDevice": { + "message": "Remember this device" + }, + "uncheckIfPublicDevice": { + "message": "Uncheck if using a public device" + }, + "approveFromYourOtherDevice": { + "message": "Approve from your other device" + }, + "requestAdminApproval": { + "message": "Request admin approval" + }, + "approveWithMasterPassword": { + "message": "Approve with master password" + }, + "region": { + "message": "Region" + }, + "ssoIdentifierRequired": { + "message": "Organization SSO identifier is required." + }, + "eu": { + "message": "EU", + "description": "European Union" + }, "loggingInOn": { "message": "Logging in on" }, @@ -2267,6 +2292,130 @@ "accessDenied": { "message": "Access denied. You do not have permission to view this page." }, + "accountSuccessfullyCreated": { + "message": "Account successfully created!" + }, + "adminApprovalRequested": { + "message": "Admin approval requested" + }, + "adminApprovalRequestSentToAdmins": { + "message": "Your request has been sent to your admin." + }, + "youWillBeNotifiedOnceApproved": { + "message": "You will be notified once approved." + }, + "troubleLoggingIn": { + "message": "Trouble logging in?" + }, + "loginApproved": { + "message": "Login approved" + }, + "userEmailMissing": { + "message": "User email missing" + }, + "deviceTrusted": { + "message": "Device trusted" + }, + "inputRequired": { + "message": "Input is required." + }, + "required": { + "message": "required" + }, + "search": { + "message": "Search" + }, + "inputMinLength": { + "message": "Input must be at least $COUNT$ characters long.", + "placeholders": { + "count": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxLength": { + "message": "Input must not exceed $COUNT$ characters in length.", + "placeholders": { + "count": { + "content": "$1", + "example": "20" + } + } + }, + "inputForbiddenCharacters": { + "message": "The following characters are not allowed: $CHARACTERS$", + "placeholders": { + "characters": { + "content": "$1", + "example": "@, #, $, %" + } + } + }, + "inputMinValue": { + "message": "Input value must be at least $MIN$.", + "placeholders": { + "min": { + "content": "$1", + "example": "8" + } + } + }, + "inputMaxValue": { + "message": "Input value must not exceed $MAX$.", + "placeholders": { + "max": { + "content": "$1", + "example": "100" + } + } + }, + "multipleInputEmails": { + "message": "1 or more emails are invalid" + }, + "inputTrimValidator": { + "message": "Input must not contain only whitespace.", + "description": "Notification to inform the user that a form's input can't contain only whitespace." + }, + "inputEmail": { + "message": "Input is not an email address." + }, + "fieldsNeedAttention": { + "message": "$COUNT$ field(s) above need your attention.", + "placeholders": { + "count": { + "content": "$1", + "example": "4" + } + } + }, + "selectPlaceholder": { + "message": "-- Select --" + }, + "multiSelectPlaceholder": { + "message": "-- Type to filter --" + }, + "multiSelectLoading": { + "message": "Retrieving options..." + }, + "multiSelectNotFound": { + "message": "No items found" + }, + "multiSelectClearAll": { + "message": "Clear all" + }, + "plusNMore": { + "message": "+ $QUANTITY$ more", + "placeholders": { + "quantity": { + "content": "$1", + "example": "5" + } + } + }, + "submenu": { + "message": "Submenu" + }, "typePasskey": { "message": "Passkey" }, diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 428d6910662..8726c2787a0 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index d40c23fda48..afda50b77db 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Permanently deleted item" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Restored item" }, diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index e72cd6515ea..ee7ff21b8e2 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 9e31f09379a..0e959cb622c 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Elemento eliminado de forma permanente" }, - "restoreItem": { - "message": "Restaurar elemento" - }, - "restoreItemConfirmation": { - "message": "¿Estás seguro de que quieres restaurar este elemento?" - }, "restoredItem": { "message": "Elemento restaurado" }, diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 2b74589df25..b3b30715f44 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Kirje on jäädavalt kustutatud" }, - "restoreItem": { - "message": "Taasta kirje" - }, - "restoreItemConfirmation": { - "message": "Oled kindel, et soovid selle kirje taastada?" - }, "restoredItem": { "message": "Kirje on taastatud" }, diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index b2f553bbf35..248f50e42a5 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Elementua betirako ezabatua" }, - "restoreItem": { - "message": "Berreskuratu elementua" - }, - "restoreItemConfirmation": { - "message": "Ziur al zaude elementu hau berreskuratu nahi duzula?" - }, "restoredItem": { "message": "Elementua berreskuratua" }, diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 655d016c0f1..783b16f5329 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "مورد برای همیشه حذف شد" }, - "restoreItem": { - "message": "بازیابی مورد" - }, - "restoreItemConfirmation": { - "message": "آیا مطمئن هستید که می‌خواهید این مورد را بازیابی کنید؟" - }, "restoredItem": { "message": "مورد بازیابی شد" }, diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index ac4e0ad6c68..63d1208b397 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Kohde poistettiin pysyvästi" }, - "restoreItem": { - "message": "Palauta kohde" - }, - "restoreItemConfirmation": { - "message": "Haluatko varmasti palauttaa kohteen?" - }, "restoredItem": { "message": "Kohde palautettiin" }, diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 99719bd9c78..08194034bbc 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanenteng tinanggal" }, - "restoreItem": { - "message": "Ibalik ang item" - }, - "restoreItemConfirmation": { - "message": "Sigurado ka bang gusto mong ibalik ang item na ito?" - }, "restoredItem": { "message": "Item na nai-restore" }, diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 15c77a1a49b..93262111b75 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Élément supprimé définitivement" }, - "restoreItem": { - "message": "Restaurer l'élément" - }, - "restoreItemConfirmation": { - "message": "Êtes-vous sûr de vouloir restaurer cet élément ?" - }, "restoredItem": { "message": "Élément restauré" }, diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index e804caddc6f..1c0f7af6f4c 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "פריט שנמחק לצמיתות" }, - "restoreItem": { - "message": "שחזר פריט" - }, - "restoreItemConfirmation": { - "message": "האם אתה בטוח שברצונך לשחזר פריט זה?" - }, "restoredItem": { "message": "פריט ששוחזר" }, diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index c8e4e16e954..6c3c6768632 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 042af715e53..e9967830fb3 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Stavka trajno izbrisana" }, - "restoreItem": { - "message": "Vrati stavku" - }, - "restoreItemConfirmation": { - "message": "Sigurno želiš vratiti ovu stavku?" - }, "restoredItem": { "message": "Stavka vraćena" }, diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 74745499079..e6c86a99381 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Elem végleges törlése" }, - "restoreItem": { - "message": "Elem visszaállítása" - }, - "restoreItemConfirmation": { - "message": "Biztosan visszaállításra kerüljön ezt az elem?" - }, "restoredItem": { "message": "Visszaállított elem" }, diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 21bbdd12c36..751b2addd0a 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item Terhapus Permanen" }, - "restoreItem": { - "message": "Pulihkan Item" - }, - "restoreItemConfirmation": { - "message": "Apakah Anda yakin ingin memulihkan item ini?" - }, "restoredItem": { "message": "Item Yang Dipulihkan" }, diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 3e977e9f200..1153e28230f 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Elemento eliminato definitivamente" }, - "restoreItem": { - "message": "Ripristina elemento" - }, - "restoreItemConfirmation": { - "message": "Sei sicuro di voler ripristinare questo elemento?" - }, "restoredItem": { "message": "Elemento ripristinato" }, diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 155af2e5259..92973480a80 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "完全に削除されたアイテム" }, - "restoreItem": { - "message": "アイテムをリストア" - }, - "restoreItemConfirmation": { - "message": "このアイテムをリストアしますか?" - }, "restoredItem": { "message": "リストアされたアイテム" }, diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 24833fa4383..d77dc3aea1d 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "ಶಾಶ್ವತವಾಗಿ ಅಳಿಸಲಾದ ಐಟಂ" }, - "restoreItem": { - "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ" - }, - "restoreItemConfirmation": { - "message": "ಈ ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?" - }, "restoredItem": { "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗಿದೆ" }, diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 87dad22d31e..7973d5f7477 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "영구적으로 삭제된 항목" }, - "restoreItem": { - "message": "항목 복원" - }, - "restoreItemConfirmation": { - "message": "정말 이 항목을 복원하시겠습니까?" - }, "restoredItem": { "message": "복원된 항목" }, diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 961e3cf593c..96f175d6c6a 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Vienums neatgriezeniski izdzēsts" }, - "restoreItem": { - "message": "Atjaunot vienumu" - }, - "restoreItemConfirmation": { - "message": "Vai tiešām atjaunot šo vienumu?" - }, "restoredItem": { "message": "Vienums atjaunots" }, diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 63e8c9bfa2e..5de6adb4a19 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 94d2c8f37c5..31d3ad94c0c 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "എന്നെന്നേക്കുമായി നീക്കം ചെയ്ത ഇനം" }, - "restoreItem": { - "message": "ഇനം വീണ്ടെടുക്കുക" - }, - "restoreItemConfirmation": { - "message": "ഈ ഇനം വീണ്ടെടുക്കണമെന്ന് ഉറപ്പാണോ?" - }, "restoredItem": { "message": "വീണ്ടെടുത്ത ഇനങ്ങൾ" }, diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 69b25d3e763..2e516ccedf9 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 85683848236..230dd97fa8d 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Slett objektet permanent" }, - "restoreItem": { - "message": "Gjenopprett objekt" - }, - "restoreItemConfirmation": { - "message": "Er du sikker på at du vil gjenopprette dette objektet?" - }, "restoredItem": { "message": "Gjenopprettet objekt" }, diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 257c86c4c34..29b7a06d226 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Definitief verwijderd item" }, - "restoreItem": { - "message": "Item herstellen" - }, - "restoreItemConfirmation": { - "message": "Weet je zeker dat je dit item wilt herstellen?" - }, "restoredItem": { "message": "Hersteld item" }, diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index e80dc037e49..81ac6a7a123 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index d0a30e5e327..b9276d540fc 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 68c3bec739f..f33df082bcd 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Element został trwale usunięty" }, - "restoreItem": { - "message": "Przywróć element" - }, - "restoreItemConfirmation": { - "message": "Czy na pewno chcesz przywrócić ten element?" - }, "restoredItem": { "message": "Element został przywrócony" }, diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 6af040aa65a..e0653acefa2 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item Permanentemente Excluído" }, - "restoreItem": { - "message": "Restaurar Item" - }, - "restoreItemConfirmation": { - "message": "Você tem certeza que deseja restaurar esse item?" - }, "restoredItem": { "message": "Item Restaurado" }, diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 523af403436..5714aaf142b 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item eliminado permanentemente" }, - "restoreItem": { - "message": "Restaurar item" - }, - "restoreItemConfirmation": { - "message": "Tem a certeza de que pretende restaurar este item?" - }, "restoredItem": { "message": "Item restaurado" }, @@ -2125,7 +2119,7 @@ "message": "Frase de impressão digital" }, "needAnotherOption": { - "message": "O início de sessão com o dispositivo deve ser ativado nas definições da aplicação Bitwarden. Necessita de outra opção?" + "message": "O início de sessão com o dispositivo deve ser ativado nas definições da aplicação Bitwarden. Precisa de outra opção?" }, "viewAllLoginOptions": { "message": "Ver todas as opções de início de sessão" diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 2ea79931dfd..bdd645da39f 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Articol șters permanent" }, - "restoreItem": { - "message": "Restaurare articol" - }, - "restoreItemConfirmation": { - "message": "Sigur doriți să restabiliți acest articol?" - }, "restoredItem": { "message": "Articol restabilit" }, diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 4ee6c0146a2..4326a105fe0 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Элемент удален навсегда" }, - "restoreItem": { - "message": "Восстановить элемент" - }, - "restoreItemConfirmation": { - "message": "Вы уверены, что хотите восстановить этот элемент?" - }, "restoredItem": { "message": "Элемент восстановлен" }, diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index dd521ed5e5d..20268275344 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 752ef8e44fb..384f4474a55 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Položka natrvalo odstránená" }, - "restoreItem": { - "message": "Obnoviť položku" - }, - "restoreItemConfirmation": { - "message": "Naozaj chcete obnoviť túto položku?" - }, "restoredItem": { "message": "Obnovená položka" }, diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 2dd3e77b911..4574e72ce8b 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index e77a89fe2c7..87ec155c15f 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Трајно избрисана ставка" }, - "restoreItem": { - "message": "Врати ставку" - }, - "restoreItemConfirmation": { - "message": "Да ли сте сигурни да желите да вратите ову ставку?" - }, "restoredItem": { "message": "Ставка враћена" }, diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 01fe1731e00..20b78cbf5cd 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -33,7 +33,7 @@ "message": "Samlingar" }, "searchVault": { - "message": "Sök i valvet" + "message": "Sök i valv" }, "addItem": { "message": "Lägg till objekt" @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Raderade objekt permanent" }, - "restoreItem": { - "message": "Återställ objekt" - }, - "restoreItemConfirmation": { - "message": "Är du säker på att du vill återställa detta objekt?" - }, "restoredItem": { "message": "Återställde objekt" }, diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index ede96425e9c..48da8c3453e 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 7221191a142..1b7bdba09b6 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, - "restoreItem": { - "message": "Restore item" - }, - "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" - }, "restoredItem": { "message": "Item restored" }, diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 340dfd753ab..3c323d9d106 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Kayıt kalıcı olarak silindi" }, - "restoreItem": { - "message": "Kaydı geri yükle" - }, - "restoreItemConfirmation": { - "message": "Bu ögeyi geri yüklemek istediğinizden emin misiniz?" - }, "restoredItem": { "message": "Kayıt geri yüklendi" }, diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 5b9afdc1000..29b9b31f783 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Запис остаточно видалено" }, - "restoreItem": { - "message": "Відновити запис" - }, - "restoreItemConfirmation": { - "message": "Ви дійсно хочете відновити цей запис?" - }, "restoredItem": { "message": "Запис відновлено" }, @@ -2253,7 +2247,7 @@ "message": "Оновлення рекомендованих налаштувань" }, "loggingInOn": { - "message": "Logging in on" + "message": "Увійти на" }, "usDomain": { "message": "bitwarden.com" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 6f69be27586..8f21ac75264 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "Đã xóa vĩnh viễn mục" }, - "restoreItem": { - "message": "Khôi phục mục" - }, - "restoreItemConfirmation": { - "message": "Bạn có chắc chắn muốn khôi phục mục này không?" - }, "restoredItem": { "message": "Mục đã được khôi phục" }, diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 8e03d9521fe..a44284a79df 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "项目已永久删除" }, - "restoreItem": { - "message": "恢复项目" - }, - "restoreItemConfirmation": { - "message": "您确定要恢复此项目吗?" - }, "restoredItem": { "message": "项目已恢复" }, diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index bf0ce480427..b13dd8f7a74 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -1512,12 +1512,6 @@ "permanentlyDeletedItem": { "message": "項目已永久刪除" }, - "restoreItem": { - "message": "還原項目" - }, - "restoreItemConfirmation": { - "message": "您確定要恢復此項目嗎?" - }, "restoredItem": { "message": "項目已還原" }, diff --git a/apps/desktop/src/main/menu/menu.account.ts b/apps/desktop/src/main/menu/menu.account.ts index 10a6d6d77d5..5060a4934c5 100644 --- a/apps/desktop/src/main/menu/menu.account.ts +++ b/apps/desktop/src/main/menu/menu.account.ts @@ -15,14 +15,15 @@ export class AccountMenu implements IMenubarMenu { } get items(): MenuItemConstructorOptions[] { - return [ - this.premiumMembership, - this.changeMasterPassword, - this.twoStepLogin, - this.fingerprintPhrase, - this.separator, - this.deleteAccount, - ]; + const items = [this.premiumMembership]; + if (this._hasMasterPassword) { + items.push(this.changeMasterPassword); + } + items.push(this.twoStepLogin); + items.push(this.fingerprintPhrase); + items.push(this.separator); + items.push(this.deleteAccount); + return items; } private readonly _i18nService: I18nService; @@ -30,19 +31,22 @@ export class AccountMenu implements IMenubarMenu { private readonly _webVaultUrl: string; private readonly _window: BrowserWindow; private readonly _isLocked: boolean; + private readonly _hasMasterPassword: boolean; constructor( i18nService: I18nService, messagingService: MessagingService, webVaultUrl: string, window: BrowserWindow, - isLocked: boolean + isLocked: boolean, + hasMasterPassword: boolean ) { this._i18nService = i18nService; this._messagingService = messagingService; this._webVaultUrl = webVaultUrl; this._window = window; this._isLocked = isLocked; + this._hasMasterPassword = hasMasterPassword; } private get premiumMembership(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.bitwarden.ts b/apps/desktop/src/main/menu/menu.bitwarden.ts index 3c3a16702ee..6873f679de7 100644 --- a/apps/desktop/src/main/menu/menu.bitwarden.ts +++ b/apps/desktop/src/main/menu/menu.bitwarden.ts @@ -52,9 +52,10 @@ export class BitwardenMenu extends FirstMenu implements IMenubarMenu { updater: UpdaterMain, window: BrowserWindow, accounts: { [userId: string]: MenuAccount }, - isLocked: boolean + isLocked: boolean, + isLockable: boolean ) { - super(i18nService, messagingService, updater, window, accounts, isLocked); + super(i18nService, messagingService, updater, window, accounts, isLocked, isLockable); } private get aboutBitwarden(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index 618acdc7fe1..173b6066aba 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -51,9 +51,10 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { updater: UpdaterMain, window: BrowserWindow, accounts: { [userId: string]: MenuAccount }, - isLocked: boolean + isLocked: boolean, + isLockable: boolean ) { - super(i18nService, messagingService, updater, window, accounts, isLocked); + super(i18nService, messagingService, updater, window, accounts, isLocked, isLockable); } private get addNewLogin(): MenuItemConstructorOptions { diff --git a/apps/desktop/src/main/menu/menu.first.ts b/apps/desktop/src/main/menu/menu.first.ts index 805956f2632..b164e280d0b 100644 --- a/apps/desktop/src/main/menu/menu.first.ts +++ b/apps/desktop/src/main/menu/menu.first.ts @@ -9,33 +9,24 @@ import { UpdaterMain } from "../updater.main"; import { MenuAccount } from "./menu.updater"; export class FirstMenu { - protected readonly _i18nService: I18nService; - protected readonly _updater: UpdaterMain; - protected readonly _messagingService: MessagingService; - protected readonly _accounts: { [userId: string]: MenuAccount }; - protected readonly _window: BrowserWindow; - protected readonly _isLocked: boolean; - constructor( - i18nService: I18nService, - messagingService: MessagingService, - updater: UpdaterMain, - window: BrowserWindow, - accounts: { [userId: string]: MenuAccount }, - isLocked: boolean - ) { - this._i18nService = i18nService; - this._updater = updater; - this._messagingService = messagingService; - this._window = window; - this._accounts = accounts; - this._isLocked = isLocked; - } + protected readonly _i18nService: I18nService, + protected readonly _messagingService: MessagingService, + protected readonly _updater: UpdaterMain, + protected readonly _window: BrowserWindow, + protected readonly _accounts: { [userId: string]: MenuAccount }, + protected readonly _isLocked: boolean, + protected readonly _isLockable: boolean + ) {} protected get hasAccounts(): boolean { return this._accounts != null && Object.keys(this._accounts).length > 0; } + protected get hasLockableAccounts(): boolean { + return this._accounts != null && Object.values(this._accounts).some((a) => a.isLockable); + } + protected get checkForUpdates(): MenuItemConstructorOptions { return { id: "checkForUpdates", @@ -66,23 +57,29 @@ export class FirstMenu { id: "lock", label: this.localize("lockVault"), submenu: this.lockSubmenu, - enabled: this.hasAccounts, + enabled: this.hasLockableAccounts, }; } protected get lockSubmenu(): MenuItemConstructorOptions[] { const value: MenuItemConstructorOptions[] = []; for (const userId in this._accounts) { - if (userId == null) { + if (!userId) { + continue; + } + + const account = this._accounts[userId]; + + if (account == null || !account.isLockable) { continue; } value.push({ - label: this._accounts[userId].email, - id: `lockNow_${this._accounts[userId].userId}`, - click: () => this.sendMessage("lockVault", { userId: this._accounts[userId].userId }), - enabled: !this._accounts[userId].isLocked, - visible: this._accounts[userId].isAuthenticated, + label: account.email, + id: `lockNow_${account.userId}`, + click: () => this.sendMessage("lockVault", { userId: account.userId }), + enabled: !account.isLocked, + visible: account.isAuthenticated, }); } return value; diff --git a/apps/desktop/src/main/menu/menu.updater.ts b/apps/desktop/src/main/menu/menu.updater.ts index 75454a56f86..170804cae25 100644 --- a/apps/desktop/src/main/menu/menu.updater.ts +++ b/apps/desktop/src/main/menu/menu.updater.ts @@ -1,5 +1,4 @@ export class MenuUpdateRequest { - hideChangeMasterPassword: boolean; activeUserId: string; accounts: { [userId: string]: MenuAccount }; } @@ -7,6 +6,8 @@ export class MenuUpdateRequest { export class MenuAccount { isAuthenticated: boolean; isLocked: boolean; + isLockable: boolean; userId: string; email: string; + hasMasterPassword: boolean; } diff --git a/apps/desktop/src/main/menu/menubar.ts b/apps/desktop/src/main/menu/menubar.ts index 6ac28a1c8db..c3f37ecbffb 100644 --- a/apps/desktop/src/main/menu/menubar.ts +++ b/apps/desktop/src/main/menu/menubar.ts @@ -62,6 +62,10 @@ export class Menubar { isLocked = updateRequest.accounts[updateRequest.activeUserId]?.isLocked ?? true; } + const isLockable = !isLocked && updateRequest?.accounts[updateRequest.activeUserId]?.isLockable; + const hasMasterPassword = + updateRequest?.accounts[updateRequest.activeUserId]?.hasMasterPassword ?? false; + this.items = [ new FileMenu( i18nService, @@ -69,11 +73,19 @@ export class Menubar { updaterMain, windowMain.win, updateRequest?.accounts, - isLocked + isLocked, + isLockable ), new EditMenu(i18nService, messagingService, isLocked), new ViewMenu(i18nService, messagingService, isLocked), - new AccountMenu(i18nService, messagingService, webVaultUrl, windowMain.win, isLocked), + new AccountMenu( + i18nService, + messagingService, + webVaultUrl, + windowMain.win, + isLocked, + hasMasterPassword + ), new WindowMenu(i18nService, messagingService, windowMain), new HelpMenu( i18nService, @@ -91,7 +103,8 @@ export class Menubar { updaterMain, windowMain.win, updateRequest?.accounts, - isLocked + isLocked, + isLockable ), ], ...this.items, diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index 6faac0c684c..4ea28c74c5f 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -236,7 +236,7 @@ export class WindowMain { case "light": return "#ededed"; case "dark": - return "#222222"; + return "#15181e"; case "nord": return "#3b4252"; } diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index a918154c431..7906eec92f4 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2023.7.2", + "version": "2023.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2023.7.2", + "version": "2023.8.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index fe011c02e59..75951b6df0d 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2023.7.2", + "version": "2023.8.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/platform/services/electron-crypto.service.spec.ts b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts new file mode 100644 index 00000000000..92f9391b131 --- /dev/null +++ b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts @@ -0,0 +1,91 @@ +import { mock, mockReset } from "jest-mock-extended"; + +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; + +import { ElectronCryptoService } from "./electron-crypto.service"; +import { ElectronStateService } from "./electron-state.service.abstraction"; + +describe("electronCryptoService", () => { + let electronCryptoService: ElectronCryptoService; + + const cryptoFunctionService = mock(); + const encryptService = mock(); + const platformUtilService = mock(); + const logService = mock(); + const stateService = mock(); + + const mockUserId = "mock user id"; + + beforeEach(() => { + mockReset(cryptoFunctionService); + mockReset(encryptService); + mockReset(platformUtilService); + mockReset(logService); + mockReset(stateService); + + electronCryptoService = new ElectronCryptoService( + cryptoFunctionService, + encryptService, + platformUtilService, + logService, + stateService + ); + }); + + it("instantiates", () => { + expect(electronCryptoService).not.toBeFalsy(); + }); + + describe("setUserKey", () => { + let mockUserKey: UserKey; + + beforeEach(() => { + const mockRandomBytes = new Uint8Array(64) as CsprngArray; + mockUserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey; + }); + + describe("Biometric Key refresh", () => { + it("sets an Biometric key if getBiometricUnlock is true and the platform supports secure storage", async () => { + stateService.getBiometricUnlock.mockResolvedValue(true); + platformUtilService.supportsSecureStorage.mockReturnValue(true); + stateService.getBiometricRequirePasswordOnStart.mockResolvedValue(false); + + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setUserKeyBiometric).toHaveBeenCalledWith( + expect.objectContaining({ key: expect.any(String), clientEncKeyHalf: null }), + { + userId: mockUserId, + } + ); + }); + + it("clears the Biometric key if getBiometricUnlock is false or the platform does not support secure storage", async () => { + stateService.getBiometricUnlock.mockResolvedValue(true); + platformUtilService.supportsSecureStorage.mockReturnValue(false); + + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setUserKeyBiometric).toHaveBeenCalledWith(null, { + userId: mockUserId, + }); + }); + + it("clears the old deprecated Biometric key whenever a User Key is set", async () => { + await electronCryptoService.setUserKey(mockUserKey, mockUserId); + + expect(stateService.setCryptoMasterKeyBiometric).toHaveBeenCalledWith(null, { + userId: mockUserId, + }); + }); + }); + }); +}); diff --git a/apps/desktop/src/platform/services/electron-crypto.service.ts b/apps/desktop/src/platform/services/electron-crypto.service.ts index 1fb90e52ca4..e21d2001977 100644 --- a/apps/desktop/src/platform/services/electron-crypto.service.ts +++ b/apps/desktop/src/platform/services/electron-crypto.service.ts @@ -4,7 +4,12 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt. import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { + MasterKey, + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CryptoService } from "@bitwarden/common/platform/services/crypto.service"; import { CsprngString } from "@bitwarden/common/types/csprng"; @@ -21,36 +26,80 @@ export class ElectronCryptoService extends CryptoService { super(cryptoFunctionService, encryptService, platformUtilsService, logService, stateService); } - protected override async storeKey(key: SymmetricCryptoKey, userId?: string) { - await super.storeKey(key, userId); + override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3474) + const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId }); + return oldKey || (await this.stateService.hasUserKeyBiometric({ userId: userId })); + } + return super.hasUserKeyStored(keySuffix, userId); + } + + override async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + this.stateService.setUserKeyBiometric(null, { userId: userId }); + this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId); + return; + } + super.clearStoredUserKey(keySuffix, userId); + } + + protected override async storeAdditionalKeys(key: UserKey, userId?: string) { + await super.storeAdditionalKeys(key, userId); const storeBiometricKey = await this.shouldStoreKey(KeySuffixOptions.Biometric, userId); if (storeBiometricKey) { await this.storeBiometricKey(key, userId); } else { - await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + await this.stateService.setUserKeyBiometric(null, { userId: userId }); } + await this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId); } - protected async storeBiometricKey(key: SymmetricCryptoKey, userId?: string): Promise { + protected override async getKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string + ): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + await this.migrateBiometricKeyIfNeeded(userId); + const userKey = await this.stateService.getUserKeyBiometric({ userId: userId }); + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey)) as UserKey; + } + return await super.getKeyFromStorage(keySuffix, userId); + } + + protected async storeBiometricKey(key: UserKey, userId?: string): Promise { let clientEncKeyHalf: CsprngString = null; if (await this.stateService.getBiometricRequirePasswordOnStart({ userId })) { clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userId); } - await this.stateService.setCryptoMasterKeyBiometric( + await this.stateService.setUserKeyBiometric( { key: key.keyB64, clientEncKeyHalf }, { userId: userId } ); } + protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string): Promise { + if (keySuffix === KeySuffixOptions.Biometric) { + const biometricUnlock = await this.stateService.getBiometricUnlock({ userId: userId }); + return biometricUnlock && this.platformUtilService.supportsSecureStorage(); + } + return await super.shouldStoreKey(keySuffix, userId); + } + + protected override async clearAllStoredUserKeys(userId?: string): Promise { + await this.stateService.setUserKeyBiometric(null, { userId: userId }); + super.clearAllStoredUserKeys(userId); + } + private async getBiometricEncryptionClientKeyHalf(userId?: string): Promise { try { let biometricKey = await this.stateService .getBiometricEncryptionClientKeyHalf({ userId }) .then((result) => result?.decrypt(null /* user encrypted */)) .then((result) => result as CsprngString); - const userKey = await this.getKeyForUserEncryption(); + const userKey = await this.getUserKeyWithLegacySupport(); if (biometricKey == null && userKey != null) { const keyBytes = await this.cryptoFunctionService.randomBytes(32); biometricKey = Utils.fromBufferToUtf8(keyBytes) as CsprngString; @@ -63,4 +112,34 @@ export class ElectronCryptoService extends CryptoService { return null; } } + + // --LEGACY METHODS-- + // We previously used the master key for additional keys, but now we use the user key. + // These methods support migrating the old keys to the new ones. + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3475) + + override async clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string) { + if (keySuffix === KeySuffixOptions.Biometric) { + await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + } + + super.clearDeprecatedKeys(keySuffix, userId); + } + + private async migrateBiometricKeyIfNeeded(userId?: string) { + if (await this.stateService.hasCryptoMasterKeyBiometric({ userId })) { + const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId }); + // decrypt + const masterKey = new SymmetricCryptoKey(Utils.fromB64ToArray(oldBiometricKey)) as MasterKey; + let encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey(); + encUserKey = encUserKey ?? (await this.stateService.getMasterKeyEncryptedUserKey()); + if (!encUserKey) { + throw new Error("No user key found during biometric migration"); + } + const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey)); + // migrate + await this.storeBiometricKey(userKey, userId); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId }); + } + } } diff --git a/apps/desktop/src/platform/services/electron-state.service.ts b/apps/desktop/src/platform/services/electron-state.service.ts index 334cab96698..0503aeb52a3 100644 --- a/apps/desktop/src/platform/services/electron-state.service.ts +++ b/apps/desktop/src/platform/services/electron-state.service.ts @@ -98,6 +98,10 @@ export class ElectronStateService options ); + if (b64DeviceKey == null) { + return null; + } + return new SymmetricCryptoKey(Utils.fromB64ToArray(b64DeviceKey)) as DeviceKey; } diff --git a/apps/desktop/src/scss/box.scss b/apps/desktop/src/scss/box.scss index 2b720eac69c..9dbdc80a5bf 100644 --- a/apps/desktop/src/scss/box.scss +++ b/apps/desktop/src/scss/box.scss @@ -504,3 +504,26 @@ } } } + +.details { + .inner-content { + .box-content-row-flex:not([type="button"]) { + @media (max-width: 875px) { + flex-direction: column; + align-items: start; + } + + .action-buttons { + @media (max-width: 875px) { + margin-left: 0; + } + } + + .row-btn:first-of-type { + @media (max-width: 875px) { + margin-left: -8px; + } + } + } + } +} diff --git a/apps/desktop/src/scss/pages.scss b/apps/desktop/src/scss/pages.scss index 5b3ad4a8583..fda75e834f3 100644 --- a/apps/desktop/src/scss/pages.scss +++ b/apps/desktop/src/scss/pages.scss @@ -5,7 +5,8 @@ #lock-page, #sso-page, #set-password-page, -#remove-password-page { +#remove-password-page, +#login-decryption-options-page { display: flex; justify-content: center; align-items: center; @@ -53,7 +54,8 @@ #hint-page, #two-factor-page, #lock-page, -#update-temp-password-page { +#update-temp-password-page, +#login-decryption-options-page { .content { width: 325px; transition: width 0.25s linear; @@ -189,7 +191,8 @@ } #login-page, -#login-with-device-page { +#login-with-device-page, +#login-decryption-options-page { flex-direction: column; justify-content: unset; padding-top: 20px; @@ -273,3 +276,14 @@ } } } + +#login-decryption-options-page { + .standard-bottom-margin { + margin-bottom: 20px; + } + + #rememberThisDeviceHintText { + font-size: $font-size-small; + color: $text-muted; + } +} diff --git a/apps/desktop/src/scss/plugins.scss b/apps/desktop/src/scss/plugins.scss index 1bb171bb804..aed093d53be 100644 --- a/apps/desktop/src/scss/plugins.scss +++ b/apps/desktop/src/scss/plugins.scss @@ -5,11 +5,17 @@ .toast-container { .toast-close-button { + @include themify($themes) { + color: themed("toastTextColor"); + } font-size: 18px; margin-right: 4px; } .ngx-toastr { + @include themify($themes) { + color: themed("toastTextColor"); + } align-items: center; background-image: none !important; border-radius: $border-radius; diff --git a/apps/desktop/src/scss/variables.scss b/apps/desktop/src/scss/variables.scss index 439eff2f2f0..b99881134d5 100644 --- a/apps/desktop/src/scss/variables.scss +++ b/apps/desktop/src/scss/variables.scss @@ -66,7 +66,6 @@ $themes: ( listItemBackgroundColor: $background-color, listItemBackgroundHoverColor: $list-item-hover, listItemBorderColor: $border-color, - groupingsActiveColor: darken($background-color-alt, 5%), disabledIconColor: $list-icon-color, headingColor: $gray-light, headingButtonColor: lighten($gray-light, 30%), @@ -96,6 +95,7 @@ $themes: ( passwordCountText: #212529, calloutBorderColor: $border-color-dark, calloutBackgroundColor: $background-color, + toastTextColor: #ffffff, accountSwitcherBackgroundColor: $background-color, accountSwitcherTextColor: #ffffff, svgSuffix: "-light.svg", @@ -104,60 +104,60 @@ $themes: ( ), dark: ( textColor: #ffffff, - borderColor: #2f2f2f, - backgroundColor: #363636, + borderColor: #15181e, + backgroundColor: #1f242e, borderColorAlt: #6e788a, - backgroundColorAlt: #3d3d3d, + backgroundColorAlt: #2f343d, // Ensure the `window.main.ts` is updated with this value - backgroundColorAlt2: #222222, - scrollbarColor: #4d4d4d, - scrollbarHoverColor: #5f5f5f, - boxBackgroundColor: #363636, - boxBackgroundHoverColor: #3f3f3f, - boxBorderColor: #2f2f2f, - headerBackgroundColor: #363636, - headerBorderColor: #272727, - headerInputBackgroundColor: #222222, - headerInputBackgroundFocusColor: #1d1d1d, + backgroundColorAlt2: #15181e, + scrollbarColor: #6e788a, + scrollbarHoverColor: #8d94a5, + boxBackgroundColor: #2f343d, + boxBackgroundHoverColor: #3c424e, + boxBorderColor: #4c525f, + headerBackgroundColor: #2f343d, + headerBorderColor: #15181e, + headerInputBackgroundColor: #3c424e, + headerInputBackgroundFocusColor: #4c525f, headerInputColor: #ffffff, - headerInputPlaceholderColor: #707070, - listItemBackgroundColor: #363636, - listItemBackgroundHoverColor: #3c3c3c, - listItemBorderColor: #2f2f2f, - groupingsActiveColor: #292929, - disabledIconColor: #c7c7cd, - headingColor: #a3a3a3, - headingButtonColor: #a3a3a3, + headerInputPlaceholderColor: #bac0ce, + listItemBackgroundColor: #1f242e, + listItemBackgroundHoverColor: #2f343d, + listItemBorderColor: #4c525f, + disabledIconColor: #6e7689, + headingColor: #bac0ce, + headingButtonColor: #bac0ce, headingButtonHoverColor: #ffffff, - labelColor: #a3a3a3, - mutedColor: #a3a3a3, - totpStrokeColor: #cacaca, - boxRowButtonColor: #cacaca, + labelColor: #bac0ce, + mutedColor: #bac0ce, + totpStrokeColor: #4c525f, + boxRowButtonColor: #bac0ce, boxRowButtonHoverColor: #ffffff, - inputBorderColor: #222222, - inputBackgroundColor: #363636, - inputPlaceholderColor: #707070, - buttonBackgroundColor: #363636, - buttonBorderColor: #1f1f1f, - buttonColor: #e0e0e0, - buttonPrimaryColor: #46ace7, - buttonDangerColor: #ff3e24, - primaryColor: #52bdfb, - primaryAccentColor: #3ea1da, - dangerColor: #ff3e24, - successColor: $brand-success, - infoColor: $brand-info, - warningColor: $brand-warning, + inputBorderColor: #4c525f, + inputBackgroundColor: #2f343d, + inputPlaceholderColor: #bac0ce, + buttonBackgroundColor: #272b32, + buttonBorderColor: #4c525f, + buttonColor: #bac0ce, + buttonPrimaryColor: #6f9df1, + buttonDangerColor: #ff8d85, + primaryColor: #6f9df1, + primaryAccentColor: #6f9df1, + dangerColor: #ff8d85, + successColor: #52e07c, + infoColor: #a4b0c6, + warningColor: #ffeb66, logoSuffix: "white", - passwordNumberColor: #52bdfb, - passwordSpecialColor: #ff7c70, + passwordNumberColor: #6f9df1, + passwordSpecialColor: #ff8d85, passwordCountText: #ffffff, - calloutBorderColor: #2f2f2f, - calloutBackgroundColor: #363636, - accountSwitcherBackgroundColor: #2f2f2f, + calloutBorderColor: #4c525f, + calloutBackgroundColor: #3c424e, + toastTextColor: #1f242e, + accountSwitcherBackgroundColor: #2f343d, accountSwitcherTextColor: #ffffff, svgSuffix: "-dark.svg", - hrColor: #a3a3a3, + hrColor: #bac0ce, codeColor: $code-color, ), nord: ( @@ -182,7 +182,6 @@ $themes: ( listItemBackgroundColor: $nord2, listItemBackgroundHoverColor: $nord3, listItemBorderColor: $nord1, - groupingsActiveColor: $nord3, disabledIconColor: $nord5, headingColor: $nord4, headingButtonColor: $nord5, @@ -212,6 +211,7 @@ $themes: ( passwordCountText: $nord5, calloutBorderColor: $nord1, calloutBackgroundColor: $nord2, + toastTextColor: #ffffff, accountSwitcherBackgroundColor: $nord0, accountSwitcherTextColor: $nord5, svgSuffix: "-dark.svg", diff --git a/apps/desktop/src/services/electron-dialog.service.ts b/apps/desktop/src/services/electron-dialog.service.ts deleted file mode 100644 index e49e39e811b..00000000000 --- a/apps/desktop/src/services/electron-dialog.service.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ipcRenderer } from "electron"; - -import { - DialogService, - SimpleDialogOptions, - SimpleDialogType, -} from "@bitwarden/angular/services/dialog"; - -// Electron supports a limited set of dialog types -// https://www.electronjs.org/docs/latest/api/dialog#dialogshowmessageboxbrowserwindow-options -const electronTypeMap: Record = { - [SimpleDialogType.PRIMARY]: "info", - [SimpleDialogType.SUCCESS]: "info", - [SimpleDialogType.INFO]: "info", - [SimpleDialogType.WARNING]: "warning", - [SimpleDialogType.DANGER]: "error", -}; - -export class ElectronDialogService extends DialogService { - async openSimpleDialog(options: SimpleDialogOptions) { - const defaultCancel = - options.cancelButtonText === undefined - ? options.acceptButtonText == null - ? "no" - : "cancel" - : null; - - return this.legacyShowDialog( - this.translate(options.content), - this.translate(options.title), - this.translate(options.acceptButtonText, "yes"), - this.translate(options.cancelButtonText, defaultCancel), - options.type - ); - } - - private async legacyShowDialog( - body: string, - title?: string, - confirmText?: string, - cancelText?: string, - type?: SimpleDialogType - ) { - const buttons = [confirmText == null ? this.i18nService.t("ok") : confirmText]; - if (cancelText != null) { - buttons.push(cancelText); - } - - const result = await ipcRenderer.invoke("showMessageBox", { - type: electronTypeMap[type] ?? "none", - title: title, - message: title, - detail: body, - buttons: buttons, - cancelId: buttons.length === 2 ? 1 : null, - defaultId: 0, - noLink: true, - }); - - return Promise.resolve(result.response === 0); - } -} diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts index 644dfe8f906..9f5f1d460df 100644 --- a/apps/desktop/src/services/native-message-handler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -8,7 +8,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/platform/services/state.service"; @@ -144,7 +144,9 @@ export class NativeMessageHandlerService { } private async handleEncryptedMessage(message: EncryptedMessage) { - message.encryptedCommand = EncString.fromJSON(message.encryptedCommand.toString()); + message.encryptedCommand = EncString.fromJSON( + message.encryptedCommand.toString() as EncryptedString + ); const decryptedCommandData = await this.decryptPayload(message); const { command } = decryptedCommandData; diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 7647410fd75..3928778f313 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -136,14 +136,23 @@ export class NativeMessagingService { }); } - const key = await this.cryptoService.getKeyFromStorage( + const userKey = await this.cryptoService.getUserKeyFromStorage( KeySuffixOptions.Biometric, message.userId ); + const masterKey = await this.cryptoService.getMasterKey(message.userId); - if (key != null) { + if (userKey != null) { + // we send the master key still for backwards compatibility + // with older browser extensions + // TODO: Remove after 2023.10 release (https://bitwarden.atlassian.net/browse/PM-3472) this.send( - { command: "biometricUnlock", response: "unlocked", keyB64: key.keyB64 }, + { + command: "biometricUnlock", + response: "unlocked", + keyB64: masterKey?.keyB64, + userKeyB64: userKey.keyB64, + }, appId ); } else { diff --git a/apps/desktop/src/vault/app/accounts/premium.component.ts b/apps/desktop/src/vault/app/accounts/premium.component.ts index fd275d6ed7d..b4dff821cc8 100644 --- a/apps/desktop/src/vault/app/accounts/premium.component.ts +++ b/apps/desktop/src/vault/app/accounts/premium.component.ts @@ -1,6 +1,5 @@ import { Component } from "@angular/core"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -8,6 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-premium", @@ -20,7 +20,7 @@ export class PremiumComponent extends BasePremiumComponent { apiService: ApiService, logService: LogService, stateService: StateService, - dialogService: DialogServiceAbstraction, + dialogService: DialogService, environmentService: EnvironmentService ) { super( diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index aa7eba32838..8a90d6a7e1a 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -1,7 +1,6 @@ import { Component, NgZone, OnChanges, OnDestroy, ViewChild } from "@angular/core"; import { NgForm } from "@angular/forms"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/vault/components/add-edit.component"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; @@ -18,6 +17,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service"; +import { DialogService } from "@bitwarden/components"; const BroadcasterSubscriptionId = "AddEditComponent"; @@ -45,7 +45,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges, logService: LogService, organizationService: OrganizationService, sendApiService: SendApiService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( cipherService, diff --git a/apps/desktop/src/vault/app/vault/attachments.component.ts b/apps/desktop/src/vault/app/vault/attachments.component.ts index edee15fc4e5..bcb1704c0d0 100644 --- a/apps/desktop/src/vault/app/vault/attachments.component.ts +++ b/apps/desktop/src/vault/app/vault/attachments.component.ts @@ -1,6 +1,5 @@ import { Component } from "@angular/core"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -10,6 +9,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-vault-attachments", @@ -25,7 +25,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { logService: LogService, stateService: StateService, fileDownloadService: FileDownloadService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( cipherService, diff --git a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts b/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts index d9035ad7416..a4055441599 100644 --- a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts @@ -1,13 +1,13 @@ import { Component } from "@angular/core"; import { FormBuilder } from "@angular/forms"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-folder-add-edit", @@ -20,7 +20,7 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, logService: LogService, - dialogService: DialogServiceAbstraction, + dialogService: DialogService, formBuilder: FormBuilder ) { super( diff --git a/apps/desktop/src/vault/app/vault/vault.component.ts b/apps/desktop/src/vault/app/vault/vault.component.ts index f6c3780412b..5270141f8a8 100644 --- a/apps/desktop/src/vault/app/vault/vault.component.ts +++ b/apps/desktop/src/vault/app/vault/vault.component.ts @@ -11,7 +11,6 @@ import { ActivatedRoute, Router } from "@angular/router"; import { first } from "rxjs/operators"; import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -29,6 +28,7 @@ import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-repromp import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +import { DialogService } from "@bitwarden/components"; import { SearchBarService } from "../../../app/layout/search/search-bar.service"; import { GeneratorComponent } from "../../../app/tools/generator.component"; @@ -102,7 +102,7 @@ export class VaultComponent implements OnInit, OnDestroy { private stateService: StateService, private searchBarService: SearchBarService, private apiService: ApiService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { @@ -692,7 +692,7 @@ export class VaultComponent implements OnInit, OnDestroy { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "unsavedChangesTitle" }, content: { key: "unsavedChangesConfirmation" }, - type: SimpleDialogType.WARNING, + type: "warning", }); return !confirmed; } diff --git a/apps/desktop/src/vault/app/vault/view.component.ts b/apps/desktop/src/vault/app/vault/view.component.ts index 07fd92e0320..b6bf63d6e64 100644 --- a/apps/desktop/src/vault/app/vault/view.component.ts +++ b/apps/desktop/src/vault/app/vault/view.component.ts @@ -7,7 +7,6 @@ import { Output, } from "@angular/core"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ViewComponent as BaseViewComponent } from "@bitwarden/angular/vault/components/view.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; @@ -26,6 +25,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { DialogService } from "@bitwarden/components"; const BroadcasterSubscriptionId = "ViewComponent"; @@ -55,7 +55,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges { logService: LogService, stateService: StateService, fileDownloadService: FileDownloadService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( cipherService, diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index f34ba7cfae4..d587997f9e6 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -14,7 +14,8 @@ "@bitwarden/auth": ["../../libs/auth/src"], "@bitwarden/common/*": ["../../libs/common/src/*"], "@bitwarden/components": ["../../libs/components/src"], - "@bitwarden/exporter/*": ["../../libs/exporter/src/*"] + "@bitwarden/exporter/*": ["../../libs/exporter/src/*"], + "@bitwarden/vault": ["../../libs/vault/src"] }, "useDefineForClassFields": false }, diff --git a/apps/web/package.json b/apps/web/package.json index ca3f8aeb7ac..51686ed2ec8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2023.7.1", + "version": "2023.8.0", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts b/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts index 10d39d110e4..2d18ff56cfc 100644 --- a/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/is-paid-org.guard.ts @@ -1,9 +1,9 @@ import { Injectable } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { DialogService } from "@bitwarden/components"; @Injectable({ providedIn: "root", @@ -13,7 +13,7 @@ export class IsPaidOrgGuard implements CanActivate { private router: Router, private organizationService: OrganizationService, private messagingService: MessagingService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { @@ -31,7 +31,7 @@ export class IsPaidOrgGuard implements CanActivate { content: { key: "notAvailableForFreeOrganization" }, acceptButtonText: { key: "ok" }, cancelButtonText: null, - type: SimpleDialogType.INFO, + type: "info", }); return false; } else { diff --git a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts index e27efc43dbd..afcb315e5ac 100644 --- a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts @@ -3,7 +3,6 @@ import { Component, Inject, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { EventResponse } from "@bitwarden/common/models/response/event.response"; @@ -13,7 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { TableDataSource } from "@bitwarden/components"; +import { DialogService, TableDataSource } from "@bitwarden/components"; import { EventService } from "../../../core"; import { SharedModule } from "../../../shared"; @@ -186,7 +185,7 @@ export class EntityEventsComponent implements OnInit { * @param config Configuration for the dialog */ export const openEntityEventsDialog = ( - dialogService: DialogServiceAbstraction, + dialogService: DialogService, config: DialogConfig ) => { return dialogService.open(EntityEventsComponent, config); diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 11b32f430be..d2f4fb1d20d 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -3,7 +3,6 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angula import { FormBuilder, Validators } from "@angular/forms"; import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil } from "rxjs"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; @@ -14,6 +13,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data"; import { Collection } from "@bitwarden/common/vault/models/domain/collection"; import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response"; +import { DialogService } from "@bitwarden/components"; import { InternalGroupService as GroupService, GroupView } from "../core"; import { @@ -64,7 +64,7 @@ export enum GroupAddEditDialogResultType { * @param config Configuration for the dialog */ export const openGroupAddEditDialog = ( - dialogService: DialogServiceAbstraction, + dialogService: DialogService, config: DialogConfig ) => { return dialogService.open( @@ -181,7 +181,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { private logService: LogService, private formBuilder: FormBuilder, private changeDetectorRef: ChangeDetectorRef, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) { this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info; } @@ -273,7 +273,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { const confirmed = await this.dialogService.openSimpleDialog({ title: this.group.name, content: { key: "deleteGroupConfirmation" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { return false; 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 82e5acba213..f2167c0e5f3 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 { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; @@ -32,6 +31,7 @@ import { CollectionResponse, } from "@bitwarden/common/vault/models/response/collection.response"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; +import { DialogService } from "@bitwarden/components"; import { InternalGroupService as GroupService, GroupView } from "../core"; @@ -127,7 +127,7 @@ export class GroupsComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private i18nService: I18nService, private modalService: ModalService, - private dialogService: DialogServiceAbstraction, + private dialogService: DialogService, private platformUtilsService: PlatformUtilsService, private searchService: SearchService, private logService: LogService, @@ -236,7 +236,7 @@ export class GroupsComponent implements OnInit, OnDestroy { const confirmed = await this.dialogService.openSimpleDialog({ title: groupRow.details.name, content: { key: "deleteGroupConfirmation" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { return false; @@ -269,7 +269,7 @@ export class GroupsComponent implements OnInit, OnDestroy { placeholders: [groupsToDelete.length.toString()], }, content: deleteMessage, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { return false; diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts index 31ced8aee10..ae754faabe7 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-confirm.component.ts @@ -7,6 +7,7 @@ import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enum 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 { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { BulkUserDetails } from "./bulk-status.component"; @@ -98,7 +99,7 @@ export class BulkConfirmComponent implements OnInit { ); } - protected getCryptoKey() { + protected getCryptoKey(): Promise { return this.cryptoService.getOrgKey(this.organizationId); } diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts index 9bb8fcbb100..b76b640513c 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts @@ -1,11 +1,10 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject, OnInit } from "@angular/core"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { TableDataSource } from "@bitwarden/components"; +import { DialogService, TableDataSource } from "@bitwarden/components"; import { OrganizationUserView } from "../../../core"; @@ -44,7 +43,7 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit { this.dialogRef.close(); }; - static open(dialogService: DialogServiceAbstraction, data: BulkEnableSecretsManagerDialogData) { + static open(dialogService: DialogService, data: BulkEnableSecretsManagerDialogData) { return dialogService.open( BulkEnableSecretsManagerDialogComponent, { data } diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index d6c0f9ffc6f..1d1a156269b 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -3,7 +3,6 @@ import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { combineLatest, of, shareReplay, Subject, switchMap, takeUntil } from "rxjs"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { @@ -15,6 +14,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; +import { DialogService } from "@bitwarden/components"; import { flagEnabled } from "../../../../../../utils/flags"; import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service"; @@ -134,7 +134,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { private groupService: GroupService, private userService: UserAdminService, private organizationUserService: OrganizationUserService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { @@ -389,7 +389,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { let confirmed = await this.dialogService.openSimpleDialog({ title: { key: "removeUserIdAccess", placeholders: [this.params.name] }, content: { key: message }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { @@ -426,7 +426,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { title: { key: "revokeUserId", placeholders: [this.params.name] }, content: { key: "revokeUserConfirmation" }, acceptButtonText: { key: "revokeAccess" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { @@ -496,7 +496,7 @@ export class MemberDialogComponent implements OnInit, OnDestroy { key: "removeOrgUserNoMasterPasswordDesc", placeholders: [this.params.name], }, - type: SimpleDialogType.WARNING, + type: "warning", }); } } @@ -557,7 +557,7 @@ function mapToGroupAccessSelections(groups: string[]): AccessItemValue[] { * @param config Configuration for the dialog */ export function openUserAddEditDialog( - dialogService: DialogServiceAbstraction, + dialogService: DialogService, config: DialogConfig ) { return dialogService.open(MemberDialogComponent, config); diff --git a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts index 55f935c575e..7d0fae37e0a 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts @@ -10,7 +10,6 @@ import { import { Subject, takeUntil } from "rxjs"; import zxcvbn from "zxcvbn"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { PasswordStrengthComponent } from "@bitwarden/angular/shared/components/password-strength/password-strength.component"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationUserResetPasswordRequest } from "@bitwarden/common/abstractions/organization-user/requests"; @@ -23,8 +22,12 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { + SymmetricCryptoKey, + UserKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-reset-password", @@ -54,7 +57,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { private cryptoService: CryptoService, private logService: LogService, private organizationUserService: OrganizationUserService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { @@ -140,7 +143,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { const result = await this.dialogService.openSimpleDialog({ title: { key: "weakMasterPassword" }, content: { key: "weakMasterPasswordDesc" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!result) { @@ -171,26 +174,32 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { orgSymKey ); - // Decrypt User's Reset Password Key to get EncKey + // Decrypt User's Reset Password Key to get UserKey const decValue = await this.cryptoService.rsaDecrypt(resetPasswordKey, decPrivateKey); - const userEncKey = new SymmetricCryptoKey(decValue); + const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey; - // Create new key and hash new password - const newKey = await this.cryptoService.makeKey( + // Create new master key and hash new password + const newMasterKey = await this.cryptoService.makeMasterKey( this.newPassword, this.email.trim().toLowerCase(), kdfType, new KdfConfig(kdfIterations, kdfMemory, kdfParallelism) ); - const newPasswordHash = await this.cryptoService.hashPassword(this.newPassword, newKey); + const newMasterKeyHash = await this.cryptoService.hashMasterKey( + this.newPassword, + newMasterKey + ); - // Create new encKey for the User - const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey); + // Create new encrypted user key for the User + const newUserKey = await this.cryptoService.encryptUserKeyWithMasterKey( + newMasterKey, + existingUserKey + ); // Create request const request = new OrganizationUserResetPasswordRequest(); - request.key = newEncKey[1].encryptedString; - request.newMasterPasswordHash = newPasswordHash; + request.key = newUserKey[1].encryptedString; + request.newMasterPasswordHash = newMasterKeyHash; // Change user's password return this.organizationUserService.putOrganizationUserResetPassword( diff --git a/apps/web/src/app/admin-console/organizations/members/people.component.ts b/apps/web/src/app/admin-console/organizations/members/people.component.ts index 8c4d565c85f..e696609d170 100644 --- a/apps/web/src/app/admin-console/organizations/members/people.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/people.component.ts @@ -16,12 +16,6 @@ import { import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; -import { - DialogServiceAbstraction, - SimpleDialogCloseType, - SimpleDialogOptions, - SimpleDialogType, -} from "@bitwarden/angular/services/dialog"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; @@ -55,6 +49,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data"; import { Collection } from "@bitwarden/common/vault/models/domain/collection"; import { CollectionDetailsResponse } from "@bitwarden/common/vault/models/response/collection.response"; +import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; import { flagEnabled } from "../../../../utils/flags"; import { openEntityEventsDialog } from "../../../admin-console/organizations/manage/entity-events.component"; @@ -125,7 +120,7 @@ export class PeopleComponent private organizationService: OrganizationService, private organizationApiService: OrganizationApiServiceAbstraction, private organizationUserService: OrganizationUserService, - dialogService: DialogServiceAbstraction, + dialogService: DialogService, private router: Router, private groupService: GroupService, private collectionService: CollectionService @@ -359,7 +354,7 @@ export class PeopleComponent : "freeOrgInvLimitReachedNoManageBilling", this.organization.seats ), - type: SimpleDialogType.PRIMARY, + type: "primary", }; if (this.organization.canEditSubscription) { @@ -371,12 +366,12 @@ export class PeopleComponent const simpleDialog = this.dialogService.openSimpleDialogRef(orgUpgradeSimpleDialogOpts); - firstValueFrom(simpleDialog.closed).then((result: SimpleDialogCloseType | undefined) => { + firstValueFrom(simpleDialog.closed).then((result: boolean | undefined) => { if (!result) { return; } - if (result == SimpleDialogCloseType.ACCEPT && this.organization.canEditSubscription) { + if (result && this.organization.canEditSubscription) { this.router.navigate(["/organizations", this.organization.id, "billing", "subscription"], { queryParams: { upgrade: true }, }); @@ -581,7 +576,7 @@ export class PeopleComponent placeholders: [this.userNamePipe.transform(user)], }, content: { key: content }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { @@ -600,7 +595,7 @@ export class PeopleComponent title: { key: "revokeAccess", placeholders: [this.userNamePipe.transform(user)] }, content: this.revokeWarningMessage(), acceptButtonText: { key: "revokeAccess" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { @@ -676,7 +671,7 @@ export class PeopleComponent key: "removeOrgUserNoMasterPasswordDesc", placeholders: [this.userNamePipe.transform(user)], }, - type: SimpleDialogType.WARNING, + type: "warning", }); } } diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts index dca1b18530a..c4ca1dc241f 100644 --- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; -import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard"; +import { AuthGuard } from "@bitwarden/angular/auth/guards"; import { canAccessOrgAdmin, canAccessGroupsTab, diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index f8628ed1f79..8900e626a3c 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -3,7 +3,6 @@ import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, lastValueFrom, Subject, switchMap, takeUntil, from } from "rxjs"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -15,6 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DialogService } from "@bitwarden/components"; import { ApiKeyComponent } from "../../../settings/api-key.component"; import { PurgeVaultComponent } from "../../../settings/purge-vault.component"; @@ -75,7 +75,7 @@ export class AccountComponent { private router: Router, private organizationService: OrganizationService, private organizationApiService: OrganizationApiServiceAbstraction, - private dialogService: DialogServiceAbstraction, + private dialogService: DialogService, private formBuilder: FormBuilder ) {} diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts index f52115c268f..389514d35f2 100644 --- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts @@ -3,7 +3,6 @@ import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { combineLatest, Subject, takeUntil } from "rxjs"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -15,6 +14,7 @@ import { Verification } from "@bitwarden/common/types/verification"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { DialogService } from "@bitwarden/components"; import { UserVerificationModule } from "../../../../auth/shared/components/user-verification"; import { SharedModule } from "../../../../shared/shared.module"; @@ -168,7 +168,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy { * @param config Configuration for the dialog */ export function openDeleteOrganizationDialog( - dialogService: DialogServiceAbstraction, + dialogService: DialogService, config: DialogConfig ) { return dialogService.open( diff --git a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts index e7e81431cb9..c3a687a7f95 100644 --- a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts @@ -3,7 +3,6 @@ import { ActivatedRoute, Router } from "@angular/router"; import { lastValueFrom, Observable, Subject } from "rxjs"; import { first, map, takeUntil } from "rxjs/operators"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -14,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; import { OrganizationPlansComponent } from "../../../billing/settings/organization-plans.component"; import { @@ -62,7 +62,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy { private syncService: SyncService, private validationService: ValidationService, private organizationService: OrganizationService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { diff --git a/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts b/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts index 35d445cb41c..9ea1442fd13 100644 --- a/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts +++ b/apps/web/src/app/admin-console/organizations/tools/import-export/org-export.component.ts @@ -2,7 +2,6 @@ import { Component } from "@angular/core"; import { UntypedFormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @@ -12,6 +11,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; import { VaultExportServiceAbstraction } from "@bitwarden/exporter/vault-export"; import { ExportComponent } from "../../../../tools/import-export/export.component"; @@ -34,7 +34,7 @@ export class OrganizationExportComponent extends ExportComponent { userVerificationService: UserVerificationService, formBuilder: UntypedFormBuilder, fileDownloadService: FileDownloadService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( cryptoService, 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 3e1067a1da8..3c9b5858e04 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 { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { canAccessVaultTab, @@ -17,6 +16,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; import { ImportServiceAbstraction } from "@bitwarden/importer"; import { ImportComponent } from "../../../../tools/import-export/import.component"; @@ -44,7 +44,7 @@ export class OrganizationImportComponent extends ImportComponent { logService: LogService, modalService: ModalService, syncService: SyncService, - dialogService: DialogServiceAbstraction, + dialogService: DialogService, folderService: FolderService, collectionService: CollectionService, formBuilder: FormBuilder @@ -91,7 +91,7 @@ export class OrganizationImportComponent extends ImportComponent { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "warning" }, content: { key: "importWarning", placeholders: [this.organization.name] }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts index a533967d8f5..535d7d375ab 100644 --- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts @@ -58,8 +58,8 @@ export class EnrollMasterPasswordReset { const publicKey = Utils.fromB64ToArray(orgKeys.publicKey); // RSA Encrypt user's encKey.key with organization public key - const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); + const userKey = await this.cryptoService.getUserKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey); keyString = encryptedKey.encryptedString; toastStringRef = "enrollPasswordResetSuccess"; diff --git a/apps/web/src/app/admin-console/settings/sponsoring-org-row.component.ts b/apps/web/src/app/admin-console/settings/sponsoring-org-row.component.ts index aa1bcf0b4fc..715fc6eba08 100644 --- a/apps/web/src/app/admin-console/settings/sponsoring-org-row.component.ts +++ b/apps/web/src/app/admin-console/settings/sponsoring-org-row.component.ts @@ -2,12 +2,12 @@ import { formatDate } from "@angular/common"; import { Component, EventEmitter, Input, Output, OnInit } from "@angular/core"; import { firstValueFrom } from "rxjs"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "[sponsoring-org-row]", @@ -32,7 +32,7 @@ export class SponsoringOrgRowComponent implements OnInit { private i18nService: I18nService, private logService: LogService, private platformUtilsService: PlatformUtilsService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} async ngOnInit() { @@ -73,7 +73,7 @@ export class SponsoringOrgRowComponent implements OnInit { title: `${this.i18nService.t("remove")} ${this.sponsoringOrg.familySponsorshipFriendlyName}?`, content: { key: "revokeSponsorshipConfirmation" }, acceptButtonText: { key: "remove" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 9a22ea20b41..3066dbe093c 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -7,12 +7,11 @@ import { IndividualConfig, ToastrService } from "ngx-toastr"; import { Subject, takeUntil } from "rxjs"; import Swal from "sweetalert2"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; @@ -27,6 +26,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; import { PolicyListService } from "./admin-console/core/policy-list.service"; import { @@ -82,7 +82,7 @@ export class AppComponent implements OnDestroy, OnInit { protected policyListService: PolicyListService, private keyConnectorService: KeyConnectorService, private configService: ConfigServiceAbstraction, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) {} ngOnInit() { @@ -145,7 +145,7 @@ export class AppComponent implements OnDestroy, OnInit { title: { key: "upgradeOrganization" }, content: { key: "upgradeOrganizationDesc" }, acceptButtonText: { key: "upgradeOrganization" }, - type: SimpleDialogType.INFO, + type: "info", }); if (upgradeConfirmed) { this.router.navigate([ @@ -162,7 +162,7 @@ export class AppComponent implements OnDestroy, OnInit { title: { key: "premiumRequired" }, content: { key: "premiumRequiredDesc" }, acceptButtonText: { key: "upgrade" }, - type: SimpleDialogType.SUCCESS, + type: "success", }); if (premiumConfirmed) { this.router.navigate(["settings/subscription/premium"]); @@ -174,7 +174,7 @@ export class AppComponent implements OnDestroy, OnInit { title: { key: "emailVerificationRequired" }, content: { key: "emailVerificationRequiredDesc" }, acceptButtonText: { key: "learnMore" }, - type: SimpleDialogType.INFO, + type: "info", }); if (emailVerificationConfirmed) { this.platformUtilsService.launchUri( diff --git a/apps/web/src/app/auth/accept-organization.component.ts b/apps/web/src/app/auth/accept-organization.component.ts index bdc3511f760..ef2dda138dd 100644 --- a/apps/web/src/app/auth/accept-organization.component.ts +++ b/apps/web/src/app/auth/accept-organization.component.ts @@ -18,6 +18,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrgKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { BaseAcceptComponent } from "../common/base.accept.component"; @@ -108,16 +109,14 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { const request = new OrganizationUserAcceptInitRequest(); request.token = qParams.token; - const [encryptedOrgShareKey, orgShareKey] = await this.cryptoService.makeShareKey(); - const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair( - orgShareKey - ); + const [encryptedOrgKey, orgKey] = await this.cryptoService.makeOrgKey(); + const [orgPublicKey, encryptedOrgPrivateKey] = await this.cryptoService.makeKeyPair(orgKey); const collection = await this.cryptoService.encrypt( this.i18nService.t("defaultCollection"), - orgShareKey + orgKey ); - request.key = encryptedOrgShareKey.encryptedString; + request.key = encryptedOrgKey.encryptedString; request.keys = new OrganizationKeysRequest( orgPublicKey, encryptedOrgPrivateKey.encryptedString @@ -141,8 +140,8 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { const publicKey = Utils.fromB64ToArray(response.publicKey); // RSA Encrypt user's encKey.key with organization public key - const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey); + const userKey = await this.cryptoService.getUserKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey); // Add reset password key to accept request request.resetPasswordKey = encryptedKey.encryptedString; diff --git a/apps/web/src/app/auth/lock.component.ts b/apps/web/src/app/auth/lock.component.ts index 27e531c327a..bd6ede32f49 100644 --- a/apps/web/src/app/auth/lock.component.ts +++ b/apps/web/src/app/auth/lock.component.ts @@ -2,13 +2,13 @@ import { Component, NgZone } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -17,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; +import { DialogService } from "@bitwarden/components"; import { RouterService } from "../core"; @@ -38,13 +39,14 @@ export class LockComponent extends BaseLockComponent { stateService: StateService, apiService: ApiService, logService: LogService, - keyConnectorService: KeyConnectorService, ngZone: NgZone, policyApiService: PolicyApiServiceAbstraction, policyService: InternalPolicyService, passwordStrengthService: PasswordStrengthServiceAbstraction, - dialogService: DialogServiceAbstraction, - route: ActivatedRoute + route: ActivatedRoute, + dialogService: DialogService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + userVerificationService: UserVerificationService ) { super( router, @@ -58,13 +60,14 @@ export class LockComponent extends BaseLockComponent { stateService, apiService, logService, - keyConnectorService, ngZone, policyApiService, policyService, passwordStrengthService, route, - dialogService + dialogService, + deviceTrustCryptoService, + userVerificationService ); } diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html new file mode 100644 index 00000000000..ed59cc12388 --- /dev/null +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html @@ -0,0 +1,105 @@ +
+
+
+ +
+ + +

+ + {{ "loading" | i18n }} +

+
+ +
+ +

{{ "loginInitiated" | i18n }}

+ +

+ {{ "deviceApprovalRequired" | i18n }} +

+ +
+ + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + +
+ +
+ + + + + +
+
+ + +

{{ "loggedInExclamation" | i18n }}

+ +
+ + + {{ "rememberThisDevice" | i18n }} + {{ "uncheckIfPublicDevice" | i18n }} + +
+ + +
+ +
+ +
+

{{ "loggingInAs" | i18n }} {{ data.userEmail }}

+ {{ "notYou" | i18n }} +
+
+
+
diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts new file mode 100644 index 00000000000..2c97bd227f9 --- /dev/null +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -0,0 +1,21 @@ +import { Component } from "@angular/core"; + +import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component"; +@Component({ + selector: "web-login-decryption-options", + templateUrl: "login-decryption-options.component.html", +}) +export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } + } + + createUserAction = async (): Promise => { + return this.createUser(); + }; +} diff --git a/apps/web/src/app/auth/login/login-with-device.component.html b/apps/web/src/app/auth/login/login-with-device.component.html index f190f8f5c6e..80811ac8b64 100644 --- a/apps/web/src/app/auth/login/login-with-device.component.html +++ b/apps/web/src/app/auth/login/login-with-device.component.html @@ -5,42 +5,71 @@ >
-

- {{ "loginOrCreateNewAccount" | i18n }} -

-
-

{{ "logInInitiated" | i18n }}

+ +

+ {{ "loginOrCreateNewAccount" | i18n }} +

-
-

{{ "notificationSentDevice" | i18n }}

+
+

{{ "loginInitiated" | i18n }}

-

- {{ "fingerprintMatchInfo" | i18n }} -

+
+

{{ "notificationSentDevice" | i18n }}

+ +

+ {{ "fingerprintMatchInfo" | i18n }} +

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+

+ {{ fingerprintPhrase }} +

+
+ + + +
+ +
+ {{ "loginWithDeviceEnabledNote" | i18n }} + {{ "viewAllLoginOptions" | i18n }} +
+ + +
+

{{ "adminApprovalRequested" | i18n }}

-
-

{{ "fingerprintPhraseHeader" | i18n }}

-

- {{ fingerprintPhrase }} -

+
+

{{ "adminApprovalRequestSentToAdmins" | i18n }}

+

{{ "youWillBeNotifiedOnceApproved" | i18n }}

+
+ +
+

{{ "fingerprintPhraseHeader" | i18n }}

+

+ {{ fingerprintPhrase }} +

+
+ +
+ +
+ {{ "troubleLoggingIn" | i18n }} + {{ "viewAllLoginOptions" | i18n }} +
- - - -
- -
- {{ "loginWithDeviceEnabledNote" | i18n }} - {{ "viewAllLoginOptions" | i18n }} -
-
+
diff --git a/apps/web/src/app/auth/login/login-with-device.component.ts b/apps/web/src/app/auth/login/login-with-device.component.ts index c5e73bf04b1..ff66bdb886b 100644 --- a/apps/web/src/app/auth/login/login-with-device.component.ts +++ b/apps/web/src/app/auth/login/login-with-device.component.ts @@ -4,7 +4,9 @@ import { Router } from "@angular/router"; import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuthRequestCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-crypto.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; @@ -41,7 +43,9 @@ export class LoginWithDeviceComponent anonymousHubService: AnonymousHubService, validationService: ValidationService, stateService: StateService, - loginService: LoginService + loginService: LoginService, + deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, + authReqCryptoService: AuthRequestCryptoServiceAbstraction ) { super( router, @@ -58,7 +62,9 @@ export class LoginWithDeviceComponent anonymousHubService, validationService, stateService, - loginService + loginService, + deviceTrustCryptoService, + authReqCryptoService ); } } diff --git a/apps/web/src/app/auth/login/login.component.ts b/apps/web/src/app/auth/login/login.component.ts index e3d0c35c981..bcddfcf173d 100644 --- a/apps/web/src/app/auth/login/login.component.ts +++ b/apps/web/src/app/auth/login/login.component.ts @@ -6,7 +6,6 @@ import { first } from "rxjs/operators"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; @@ -14,6 +13,7 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { LoginService } from "@bitwarden/common/auth/abstractions/login.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; diff --git a/apps/web/src/app/auth/login/login.module.ts b/apps/web/src/app/auth/login/login.module.ts index b01fa015888..2a074ca2a61 100644 --- a/apps/web/src/app/auth/login/login.module.ts +++ b/apps/web/src/app/auth/login/login.module.ts @@ -4,12 +4,13 @@ import { CheckboxModule } from "@bitwarden/components"; import { SharedModule } from "../../../app/shared"; +import { LoginDecryptionOptionsComponent } from "./login-decryption-options/login-decryption-options.component"; import { LoginWithDeviceComponent } from "./login-with-device.component"; import { LoginComponent } from "./login.component"; @NgModule({ imports: [SharedModule, CheckboxModule], - declarations: [LoginComponent, LoginWithDeviceComponent], - exports: [LoginComponent, LoginWithDeviceComponent], + declarations: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent], + exports: [LoginComponent, LoginWithDeviceComponent, LoginDecryptionOptionsComponent], }) export class LoginModule {} diff --git a/apps/web/src/app/auth/recover-two-factor.component.ts b/apps/web/src/app/auth/recover-two-factor.component.ts index 06816d85460..2d6140780a0 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.ts @@ -35,7 +35,7 @@ export class RecoverTwoFactorComponent { request.recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase(); request.email = this.email.trim().toLowerCase(); const key = await this.authService.makePreloginKey(this.masterPassword, request.email); - request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key); + request.masterPasswordHash = await this.cryptoService.hashMasterKey(this.masterPassword, key); this.formPromise = this.apiService.postTwoFactorRecover(request); await this.formPromise; this.platformUtilsService.showToast( diff --git a/apps/web/src/app/auth/register-form/register-form.component.html b/apps/web/src/app/auth/register-form/register-form.component.html index 44a53fb6e40..9a5220c57d4 100644 --- a/apps/web/src/app/auth/register-form/register-form.component.html +++ b/apps/web/src/app/auth/register-form/register-form.component.html @@ -123,8 +123,7 @@
-
-
+
- - - {{ "logIn" | i18n }} -
+

+ {{ "alreadyHaveAccount" | i18n }} + {{ "logIn" | i18n }} +

diff --git a/apps/web/src/app/auth/register-form/register-form.component.ts b/apps/web/src/app/auth/register-form/register-form.component.ts index 887f5562481..0a494ec333e 100644 --- a/apps/web/src/app/auth/register-form/register-form.component.ts +++ b/apps/web/src/app/auth/register-form/register-form.component.ts @@ -4,7 +4,6 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -18,6 +17,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-register-form", @@ -46,7 +46,7 @@ export class RegisterFormComponent extends BaseRegisterComponent { environmentService: EnvironmentService, logService: LogService, auditService: AuditService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( formValidationErrorService, diff --git a/apps/web/src/app/auth/set-password.component.ts b/apps/web/src/app/auth/set-password.component.ts index fcce84a9c2a..49320ced5d8 100644 --- a/apps/web/src/app/auth/set-password.component.ts +++ b/apps/web/src/app/auth/set-password.component.ts @@ -2,7 +2,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/components/set-password.component"; -import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -15,6 +14,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-set-password", @@ -36,7 +36,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { stateService: StateService, organizationApiService: OrganizationApiServiceAbstraction, organizationUserService: OrganizationUserService, - dialogService: DialogServiceAbstraction + dialogService: DialogService ) { super( i18nService, diff --git a/apps/web/src/app/settings/change-password.component.html b/apps/web/src/app/auth/settings/change-password.component.html similarity index 94% rename from apps/web/src/app/settings/change-password.component.html rename to apps/web/src/app/auth/settings/change-password.component.html index 3321b8f9dfe..7088cb5a686 100644 --- a/apps/web/src/app/settings/change-password.component.html +++ b/apps/web/src/app/auth/settings/change-password.component.html @@ -89,12 +89,12 @@ -
-
- -
- - + +
+ +
+ + +
+
+ + +
-
- - -
-
+
diff --git a/apps/web/src/app/settings/preferences.component.ts b/apps/web/src/app/settings/preferences.component.ts index 7a9b3c9a1c6..9412a3e8b66 100644 --- a/apps/web/src/app/settings/preferences.component.ts +++ b/apps/web/src/app/settings/preferences.component.ts @@ -1,11 +1,10 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; -import { concatMap, filter, map, Observable, Subject, takeUntil, tap } from "rxjs"; +import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs"; -import { DialogServiceAbstraction, SimpleDialogType } from "@bitwarden/angular/services/dialog"; import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { ThemeType } from "@bitwarden/common/enums"; @@ -15,6 +14,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DialogService } from "@bitwarden/components"; @Component({ selector: "app-preferences", @@ -24,6 +24,8 @@ export class PreferencesComponent implements OnInit { // For use in template protected readonly VaultTimeoutAction = VaultTimeoutAction; + protected availableVaultTimeoutActions$: Observable; + vaultTimeoutPolicyCallout: Observable<{ timeout: { hours: number; minutes: number }; action: VaultTimeoutAction; @@ -55,7 +57,7 @@ export class PreferencesComponent implements OnInit { private messagingService: MessagingService, private themingService: AbstractThemingService, private settingsService: SettingsService, - private dialogService: DialogServiceAbstraction + private dialogService: DialogService ) { this.vaultTimeoutOptions = [ { name: i18nService.t("oneMinute"), value: 1 }, @@ -89,6 +91,9 @@ export class PreferencesComponent implements OnInit { } async ngOnInit() { + this.availableVaultTimeoutActions$ = + this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(); + this.vaultTimeoutPolicyCallout = this.policyService.get$(PolicyType.MaximumVaultTimeout).pipe( filter((policy) => policy != null), map((policy) => { @@ -117,7 +122,7 @@ export class PreferencesComponent implements OnInit { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "vaultTimeoutLogOutConfirmationTitle" }, content: { key: "vaultTimeoutLogOutConfirmation" }, - type: SimpleDialogType.WARNING, + type: "warning", }); if (!confirmed) { @@ -133,7 +138,9 @@ export class PreferencesComponent implements OnInit { .subscribe(); const initialFormValues = { vaultTimeout: await this.vaultTimeoutSettingsService.getVaultTimeout(), - vaultTimeoutAction: await this.vaultTimeoutSettingsService.getVaultTimeoutAction(), + vaultTimeoutAction: await firstValueFrom( + this.vaultTimeoutSettingsService.vaultTimeoutAction$() + ), enableFavicons: !(await this.settingsService.getDisableFavicon()), enableFullWidth: await this.stateService.getEnableFullWidth(), theme: await this.stateService.getTheme(), diff --git a/apps/web/src/app/settings/security-keys.component.ts b/apps/web/src/app/settings/security-keys.component.ts index 887ba3d5879..2a01f6d0106 100644 --- a/apps/web/src/app/settings/security-keys.component.ts +++ b/apps/web/src/app/settings/security-keys.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { ApiKeyComponent } from "./api-key.component"; @@ -20,14 +20,14 @@ export class SecurityKeysComponent implements OnInit { showChangeKdf = true; constructor( - private keyConnectorService: KeyConnectorService, + private userVerificationService: UserVerificationService, private stateService: StateService, private modalService: ModalService, private apiService: ApiService ) {} async ngOnInit() { - this.showChangeKdf = !(await this.keyConnectorService.getUsesKeyConnector()); + this.showChangeKdf = await this.userVerificationService.hasMasterPassword(); } async viewUserApiKey() { diff --git a/apps/web/src/app/settings/security-routing.module.ts b/apps/web/src/app/settings/security-routing.module.ts index 1d47a0d7750..d08d4be16fb 100644 --- a/apps/web/src/app/settings/security-routing.module.ts +++ b/apps/web/src/app/settings/security-routing.module.ts @@ -1,9 +1,9 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; +import { ChangePasswordComponent } from "../auth/settings/change-password.component"; import { TwoFactorSetupComponent } from "../auth/settings/two-factor-setup.component"; -import { ChangePasswordComponent } from "./change-password.component"; import { SecurityKeysComponent } from "./security-keys.component"; import { SecurityComponent } from "./security.component"; diff --git a/apps/web/src/app/settings/security.component.ts b/apps/web/src/app/settings/security.component.ts index 1c70f85edaf..3237a2e6a28 100644 --- a/apps/web/src/app/settings/security.component.ts +++ b/apps/web/src/app/settings/security.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; -import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; @Component({ selector: "app-security", @@ -9,9 +9,9 @@ import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-con export class SecurityComponent { showChangePassword = true; - constructor(private keyConnectorService: KeyConnectorService) {} + constructor(private userVerificationService: UserVerificationService) {} async ngOnInit() { - this.showChangePassword = !(await this.keyConnectorService.getUsesKeyConnector()); + this.showChangePassword = await this.userVerificationService.hasMasterPassword(); } } diff --git a/apps/web/src/app/settings/update-key.component.html b/apps/web/src/app/settings/update-key.component.html index b39a5eb7e1a..7b94a6dca04 100644 --- a/apps/web/src/app/settings/update-key.component.html +++ b/apps/web/src/app/settings/update-key.component.html @@ -1,4 +1,4 @@ -