1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

Merged branch with master and fixed conflicts

This commit is contained in:
gbubemismith
2023-08-23 22:45:39 -04:00
628 changed files with 16595 additions and 7728 deletions

View File

@@ -156,7 +156,10 @@
{ {
"files": ["libs/components/src/**/*.ts"], "files": ["libs/components/src/**/*.ts"],
"rules": { "rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/components/*", "src/**/*"] }] "no-restricted-imports": [
"error",
{ "patterns": ["@bitwarden/components/*", "src/**/*", "@bitwarden/angular/*"] }
]
} }
}, },
{ {
@@ -176,6 +179,12 @@
"rules": { "rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }]
} }
},
{
"files": ["libs/vault/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/vault/*", "src/**/*"] }]
}
} }
] ]
} }

3
.github/CODEOWNERS vendored
View File

@@ -4,7 +4,7 @@
# The following owners will be the default owners for everything in the repo. # The following owners will be the default owners for everything in the repo.
# Unless a later match takes precedence # Unless a later match takes precedence
* @bitwarden/team-leads-eng * @bitwarden/tech-leads
## Secrets Manager team files ## ## Secrets Manager team files ##
bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev 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 apps/web/src/app/vault @bitwarden/team-vault-dev
libs/angular/src/vault @bitwarden/team-vault-dev libs/angular/src/vault @bitwarden/team-vault-dev
libs/common/src/vault @bitwarden/team-vault-dev libs/common/src/vault @bitwarden/team-vault-dev
libs/vault @bitwarden/team-vault-dev
## Admin Console team files ## ## Admin Console team files ##
apps/browser/src/admin-console @bitwarden/team-admin-console-dev apps/browser/src/admin-console @bitwarden/team-admin-console-dev

View File

@@ -2,10 +2,7 @@
./apps/browser/src/safari/desktop/Assets.xcassets/AccentColor.colorset ./apps/browser/src/safari/desktop/Assets.xcassets/AccentColor.colorset
./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset ./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset
./apps/browser/src/safari/desktop/Base.lproj ./apps/browser/src/safari/desktop/Base.lproj
./apps/browser/src/services/vaultTimeout
./apps/browser/store/windows/Assets ./apps/browser/store/windows/Assets
./libs/common/src/abstractions/vaultTimeout
./libs/common/src/services/vaultTimeout
./bitwarden_license/README.md ./bitwarden_license/README.md
./libs/angular/src/directives/cipherListVirtualScroll.directive.ts ./libs/angular/src/directives/cipherListVirtualScroll.directive.ts
./libs/angular/src/scss/webfonts/Open_Sans-italic-700.woff ./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/linkedFieldOption.decorator.ts
./libs/common/src/misc/serviceUtils.ts ./libs/common/src/misc/serviceUtils.ts
./libs/common/src/misc/serviceUtils.spec.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/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/common/src/services/anonymousHub.service.ts
./libs/auth/README.md ./libs/auth/README.md
./libs/vault/README.md
./README.md ./README.md
./LICENSE_BITWARDEN.txt ./LICENSE_BITWARDEN.txt
./CONTRIBUTING.md ./CONTRIBUTING.md
@@ -78,5 +72,4 @@
./apps/browser/src/safari/safari/SafariWebExtensionHandler.swift ./apps/browser/src/safari/safari/SafariWebExtensionHandler.swift
./apps/browser/src/safari/safari/Info.plist ./apps/browser/src/safari/safari/Info.plist
./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
./apps/browser/src/services/vaultTimeout/vaultTimeout.service.ts
./SECURITY.md ./SECURITY.md

View File

@@ -31,6 +31,9 @@ on:
description: "Custom image tag extension" description: "Custom image tag extension"
required: false required: false
env:
_AZ_REGISTRY: bitwardenprod.azurecr.io
jobs: jobs:
cloc: cloc:
name: CLOC name: CLOC
@@ -65,8 +68,7 @@ jobs:
build-artifacts: build-artifacts:
name: Build artifacts name: Build artifacts
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: needs: setup
- setup
env: env:
_VERSION: ${{ needs.setup.outputs.version }} _VERSION: ${{ needs.setup.outputs.version }}
strategy: strategy:
@@ -146,13 +148,10 @@ jobs:
matrix: matrix:
include: include:
- artifact_name: cloud-QA - artifact_name: cloud-QA
registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
image_name: web-qa-cloud image_name: web-qa-cloud
- artifact_name: ee - artifact_name: ee
registries: [bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
image_name: web-ee image_name: web-ee
- artifact_name: selfhosted-COMMERCIAL - artifact_name: selfhosted-COMMERCIAL
registries: [bitwarden, bitwardenprod.azurecr.io, bitwardenqa.azurecr.io]
image_name: web image_name: web
env: env:
_VERSION: ${{ needs.setup.outputs.version }} _VERSION: ${{ needs.setup.outputs.version }}
@@ -174,15 +173,7 @@ jobs:
fi fi
########## ACRs ########## ########## ACRs ##########
- name: Login to Azure - QA - name: Login to Prod Azure
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
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
with: with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
@@ -190,6 +181,18 @@ jobs:
- name: Log into Prod container registry - name: Log into Prod container registry
run: az acr login -n bitwardenprod 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 - name: Download ${{ matrix.artifact_name }} artifact
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
with: with:
@@ -218,37 +221,17 @@ jobs:
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT 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 ########## ########## Build Image ##########
- name: Extract artifact - name: Extract artifact
working-directory: apps/web working-directory: apps/web
run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
- name: Login to Azure - name: Generate image full name
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 id: image-name
with: env:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
PROJECT_NAME: ${{ matrix.image_name }}
- name: Retrieve github PAT secrets run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
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: Build Docker image - name: Build Docker image
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1 uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4.1.1
@@ -257,21 +240,10 @@ jobs:
file: apps/web/Dockerfile file: apps/web/Dockerfile
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ steps.tag-list.outputs.tags }} tags: ${{ steps.image-name.outputs.name }}
secrets: | secrets: |
"GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" "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 - name: Log out of Docker
run: docker logout run: docker logout
@@ -279,8 +251,7 @@ jobs:
crowdin-push: crowdin-push:
name: Crowdin Push name: Crowdin Push
if: github.ref == 'refs/heads/master' if: github.ref == 'refs/heads/master'
needs: needs: build-artifacts
- build-artifacts
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout repo - name: Checkout repo

View File

@@ -15,10 +15,13 @@ on:
- Redeploy - Redeploy
- Dry Run - Dry Run
env:
_AZ_REGISTRY: bitwardenprod.azurecr.io
jobs: jobs:
setup: setup:
name: Setup name: Setup
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
outputs: outputs:
release_version: ${{ steps.version.outputs.version }} release_version: ${{ steps.version.outputs.version }}
tag_version: ${{ steps.version.outputs.tag }} tag_version: ${{ steps.version.outputs.tag }}
@@ -46,10 +49,9 @@ jobs:
monorepo: true monorepo: true
monorepo-project: web monorepo-project: web
self-host: self-host:
name: Release self-host docker name: Release self-host docker
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
needs: setup needs: setup
env: env:
_BRANCH_NAME: ${{ github.ref_name }} _BRANCH_NAME: ${{ github.ref_name }}
@@ -67,42 +69,6 @@ jobs:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 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 ########## ########## ACR ##########
- name: Login to Azure - PROD Subscription - name: Login to Azure - PROD Subscription
uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7
@@ -112,38 +78,46 @@ jobs:
- name: Login to Azure ACR - name: Login to Azure ACR
run: az acr login -n bitwardenprod run: az acr login -n bitwardenprod
- name: Tag version - name: Pull branch image
env:
REGISTRY: bitwardenprod.azurecr.io
run: | run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker tag bitwarden/web:latest $REGISTRY/web:$_RELEASE_VERSION docker pull $_AZ_REGISTRY/web:latest
docker tag bitwarden/web:latest $REGISTRY/web-sh:$_RELEASE_VERSION
else 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 fi
- name: Push version - name: Push version
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
REGISTRY: bitwardenprod.azurecr.io
run: | run: |
docker push $REGISTRY/web:$_RELEASE_VERSION if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker push $_AZ_REGISTRY/web:dryrun
docker push $REGISTRY/web-sh:$_RELEASE_VERSION 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 - name: Log out of Docker
run: docker logout run: docker logout
ghpages-deploy: ghpages-deploy:
name: Deploy to GitHub Pages name: Create Deploy PR for GitHub Pages
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
needs: needs: setup
- setup
env: env:
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }} _TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
@@ -233,7 +207,7 @@ jobs:
release: release:
name: Create GitHub Release name: Create GitHub Release
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
needs: needs:
- setup - setup
- self-host - self-host

View File

@@ -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
}
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bitwarden/browser", "name": "@bitwarden/browser",
"version": "2023.7.1", "version": "2023.8.0",
"scripts": { "scripts": {
"build": "webpack", "build": "webpack",
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack", "build:mv3": "cross-env MANIFEST_VERSION=3 webpack",

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "استعادة العنصر" "message": "استعادة العنصر"
}, },
"restoreItemConfirmation": {
"message": "هل أنت متأكد من أنك تريد استعادة هذا العنصر؟"
},
"restoredItem": { "restoredItem": {
"message": "تم استعادة العنصر" "message": "تم استعادة العنصر"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Elementi bərpa et" "message": "Elementi bərpa et"
}, },
"restoreItemConfirmation": {
"message": "Elementi bərpa etmək istədiyinizə əminsiniz?"
},
"restoredItem": { "restoredItem": {
"message": "Element bərpa edildi" "message": "Element bərpa edildi"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Аднавіць элемент" "message": "Аднавіць элемент"
}, },
"restoreItemConfirmation": {
"message": "Вы сапраўды хочаце аднавіць гэты элемент?"
},
"restoredItem": { "restoredItem": {
"message": "Элемент адноўлены" "message": "Элемент адноўлены"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Възстановяване на запис" "message": "Възстановяване на запис"
}, },
"restoreItemConfirmation": {
"message": "Сигурни ли сте, че искате да възстановите записа?"
},
"restoredItem": { "restoredItem": {
"message": "Записът е възстановен" "message": "Записът е възстановен"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "বস্তু পুনরুদ্ধার" "message": "বস্তু পুনরুদ্ধার"
}, },
"restoreItemConfirmation": {
"message": "আপনি কি নিশ্চিত যে আপনি এই বস্তুটি পুনরুদ্ধার করতে চান?"
},
"restoredItem": { "restoredItem": {
"message": "বস্তু পুনরুদ্ধারকৃত" "message": "বস্তু পুনরুদ্ধারকৃত"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restaura l'element" "message": "Restaura l'element"
}, },
"restoreItemConfirmation": {
"message": "Esteu segur que voleu restaurar aquest element?"
},
"restoredItem": { "restoredItem": {
"message": "Element restaurat" "message": "Element restaurat"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Obnovit položku" "message": "Obnovit položku"
}, },
"restoreItemConfirmation": {
"message": "Opravdu chcete tuto položku obnovit?"
},
"restoredItem": { "restoredItem": {
"message": "Položka byla obnovena" "message": "Položka byla obnovena"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Adfer yr eitem" "message": "Adfer yr eitem"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Gendan element" "message": "Gendan element"
}, },
"restoreItemConfirmation": {
"message": "Er du sikker på, at du vil gendanne dette element?"
},
"restoredItem": { "restoredItem": {
"message": "Element gendannet" "message": "Element gendannet"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Eintrag wiederherstellen" "message": "Eintrag wiederherstellen"
}, },
"restoreItemConfirmation": {
"message": "Soll dieser Eintrag wirklich wiederhergestellt werden?"
},
"restoredItem": { "restoredItem": {
"message": "Eintrag wiederhergestellt" "message": "Eintrag wiederhergestellt"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Ανάκτηση Στοιχείου" "message": "Ανάκτηση Στοιχείου"
}, },
"restoreItemConfirmation": {
"message": "Είστε βέβαιοι ότι θέλετε να ανακτήσετε αυτό το στοιχείο;"
},
"restoredItem": { "restoredItem": {
"message": "Στοιχείο που έχει Ανακτηθεί" "message": "Στοιχείο που έχει Ανακτηθεί"
}, },

View File

@@ -338,6 +338,9 @@
"other": { "other": {
"message": "Other" "message": "Other"
}, },
"unlockMethodNeededToChangeTimeoutActionDesc": {
"message": "Set up an unlock method to change your vault timeout action."
},
"rateExtension": { "rateExtension": {
"message": "Rate the extension" "message": "Rate the extension"
}, },
@@ -1449,9 +1452,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },
@@ -1608,6 +1608,12 @@
"biometricsNotSupportedDesc": { "biometricsNotSupportedDesc": {
"message": "Browser biometrics is not supported on this device." "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": { "nativeMessaginPermissionErrorTitle": {
"message": "Permission not provided" "message": "Permission not provided"
}, },
@@ -2158,8 +2164,8 @@
"notificationSentDevice": { "notificationSentDevice": {
"message": "A notification has been sent to your device." "message": "A notification has been sent to your device."
}, },
"logInInitiated": { "loginInitiated": {
"message": "Log in initiated" "message": "Login initiated"
}, },
"exposedMasterPassword": { "exposedMasterPassword": {
"message": "Exposed Master Password" "message": "Exposed Master Password"
@@ -2245,6 +2251,31 @@
"opensInANewWindow": { "opensInANewWindow": {
"message": "Opens in a new window" "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": { "usDomain": {
"message": "bitwarden.com" "message": "bitwarden.com"
}, },
@@ -2260,6 +2291,134 @@
"display": { "display": {
"message": "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": { "loginPasskey": {
"message": "This login uses a passkey" "message": "This login uses a passkey"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Restored item" "message": "Restored item"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restaurar elemento" "message": "Restaurar elemento"
}, },
"restoreItemConfirmation": {
"message": "¿Estás seguro de que quieres restaurar este elemento?"
},
"restoredItem": { "restoredItem": {
"message": "Elemento restaurado" "message": "Elemento restaurado"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Taasta kirje" "message": "Taasta kirje"
}, },
"restoreItemConfirmation": {
"message": "Oled kindel, et soovid selle kirje taastada?"
},
"restoredItem": { "restoredItem": {
"message": "Kirje on taastatud" "message": "Kirje on taastatud"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Berreskuratu elementua" "message": "Berreskuratu elementua"
}, },
"restoreItemConfirmation": {
"message": "Ziur zaude elementu hau berreskuratu nahi duzula?"
},
"restoredItem": { "restoredItem": {
"message": "Elementua berreskuratua" "message": "Elementua berreskuratua"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "بازیابی مورد" "message": "بازیابی مورد"
}, },
"restoreItemConfirmation": {
"message": "آیا مطمئن هستید که می‌خواهید این مورد را بازیابی کنید؟"
},
"restoredItem": { "restoredItem": {
"message": "مورد بازیابی شد" "message": "مورد بازیابی شد"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Palauta kohde" "message": "Palauta kohde"
}, },
"restoreItemConfirmation": {
"message": "Haluatko varmasti palauttaa kohteen?"
},
"restoredItem": { "restoredItem": {
"message": "Kohde palautettiin" "message": "Kohde palautettiin"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Ibalik ang item" "message": "Ibalik ang item"
}, },
"restoreItemConfirmation": {
"message": "Sigurado ka bang nais mong ibalik ang item na ito?"
},
"restoredItem": { "restoredItem": {
"message": "Item na nai-restore" "message": "Item na nai-restore"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restaurer l'élément" "message": "Restaurer l'élément"
}, },
"restoreItemConfirmation": {
"message": "Êtes-vous sûr de vouloir restaurer cet élément ?"
},
"restoredItem": { "restoredItem": {
"message": "Élément restauré" "message": "Élément restauré"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "שחזר פריט" "message": "שחזר פריט"
}, },
"restoreItemConfirmation": {
"message": "האם אתה בטוח שברצונך לשחזר פריט זה?"
},
"restoredItem": { "restoredItem": {
"message": "פריט ששוחזר" "message": "פריט ששוחזר"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "आइटम बहाल करें" "message": "आइटम बहाल करें"
}, },
"restoreItemConfirmation": {
"message": "क्या आप सुनिश्चित हैं कि आप इस आइटम को बहाल करना चाहते हैं?"
},
"restoredItem": { "restoredItem": {
"message": "बहाल आइटम" "message": "बहाल आइटम"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Vrati stavku" "message": "Vrati stavku"
}, },
"restoreItemConfirmation": {
"message": "Sigurno želiš vratiti ovu stavku?"
},
"restoredItem": { "restoredItem": {
"message": "Stavka vraćena" "message": "Stavka vraćena"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Elem visszaállítása" "message": "Elem visszaállítása"
}, },
"restoreItemConfirmation": {
"message": "Biztosan visszaállításra kerüljön ezt az elem?"
},
"restoredItem": { "restoredItem": {
"message": "Visszaállított elem" "message": "Visszaállított elem"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Pulihkan Item" "message": "Pulihkan Item"
}, },
"restoreItemConfirmation": {
"message": "Apakah Anda yakin ingin memulihkan item ini?"
},
"restoredItem": { "restoredItem": {
"message": "Item Yang Dipulihkan" "message": "Item Yang Dipulihkan"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Ripristina elemento" "message": "Ripristina elemento"
}, },
"restoreItemConfirmation": {
"message": "Sei sicuro di voler ripristinare questo elemento?"
},
"restoredItem": { "restoredItem": {
"message": "Elemento ripristinato" "message": "Elemento ripristinato"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "アイテムをリストア" "message": "アイテムをリストア"
}, },
"restoreItemConfirmation": {
"message": "このアイテムをリストアしますか?"
},
"restoredItem": { "restoredItem": {
"message": "リストアされたアイテム" "message": "リストアされたアイテム"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ" "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಿ"
}, },
"restoreItemConfirmation": {
"message": "ಈ ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?"
},
"restoredItem": { "restoredItem": {
"message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗಿದೆ" "message": "ಐಟಂ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲಾಗಿದೆ"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "항목 복원" "message": "항목 복원"
}, },
"restoreItemConfirmation": {
"message": "정말 이 항목을 복원하시겠습니까?"
},
"restoredItem": { "restoredItem": {
"message": "복원된 항목" "message": "복원된 항목"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Atkurti elementą" "message": "Atkurti elementą"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Elementas atkurtas" "message": "Elementas atkurtas"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Atjaunot vienumu" "message": "Atjaunot vienumu"
}, },
"restoreItemConfirmation": {
"message": "Jūs tiešām atjaunot šo vienumu?"
},
"restoredItem": { "restoredItem": {
"message": "Vienums atjaunots" "message": "Vienums atjaunots"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "ഇനം വീണ്ടെടുക്കുക " "message": "ഇനം വീണ്ടെടുക്കുക "
}, },
"restoreItemConfirmation": {
"message": "ഈ ഇനം വീണ്ടെടുക്കണമെന്ന് ഉറപ്പാണോ?"
},
"restoredItem": { "restoredItem": {
"message": "വീണ്ടെടുത്ത ഇനം" "message": "വീണ്ടെടുത്ത ഇനം"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Gjenopprett objekt" "message": "Gjenopprett objekt"
}, },
"restoreItemConfirmation": {
"message": "Er du sikker på at du vil gjenopprette dette elementet?"
},
"restoredItem": { "restoredItem": {
"message": "Gjenopprettet objekt" "message": "Gjenopprettet objekt"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Item herstellen" "message": "Item herstellen"
}, },
"restoreItemConfirmation": {
"message": "Weet je zeker dat je dit item wilt herstellen?"
},
"restoredItem": { "restoredItem": {
"message": "Hersteld item" "message": "Hersteld item"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Przywróć element" "message": "Przywróć element"
}, },
"restoreItemConfirmation": {
"message": "Czy na pewno chcesz przywrócić ten element?"
},
"restoredItem": { "restoredItem": {
"message": "Element został przywrócony" "message": "Element został przywrócony"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restaurar Item" "message": "Restaurar Item"
}, },
"restoreItemConfirmation": {
"message": "Você tem certeza que deseja restaurar esse item?"
},
"restoredItem": { "restoredItem": {
"message": "Item Restaurado" "message": "Item Restaurado"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restaurar item" "message": "Restaurar item"
}, },
"restoreItemConfirmation": {
"message": "Tem a certeza de que pretende restaurar este item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restaurado" "message": "Item restaurado"
}, },
@@ -2129,7 +2126,7 @@
"message": "Iniciar sessão com o dispositivo" "message": "Iniciar sessão com o dispositivo"
}, },
"loginWithDeviceEnabledInfo": { "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": { "fingerprintPhraseHeader": {
"message": "Frase de impressão digital" "message": "Frase de impressão digital"

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restabilire articol" "message": "Restabilire articol"
}, },
"restoreItemConfirmation": {
"message": "Sigur doriți să restabiliți acest articol?"
},
"restoredItem": { "restoredItem": {
"message": "Articol restabilit" "message": "Articol restabilit"
}, },

View File

@@ -631,7 +631,7 @@
"message": "Обновить" "message": "Обновить"
}, },
"notificationUnlockDesc": { "notificationUnlockDesc": {
"message": "Разблокируйте свое хранилище Bitwarden для завершения запроса автозаполнения." "message": "Разблокируйте свое хранилище Bitwarden чтобы выполнить автозаполнение."
}, },
"notificationUnlock": { "notificationUnlock": {
"message": "Разблокировать" "message": "Разблокировать"
@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Восстановить элемент" "message": "Восстановить элемент"
}, },
"restoreItemConfirmation": {
"message": "Вы уверены, что хотите восстановить этот элемент?"
},
"restoredItem": { "restoredItem": {
"message": "Элемент восстановлен" "message": "Элемент восстановлен"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "අයිතමය යළි පිහිටුවන්න" "message": "අයිතමය යළි පිහිටුවන්න"
}, },
"restoreItemConfirmation": {
"message": "ඔබට මෙම අයිතමය යථා තත්වයට පත් කිරීමට අවශ්ය බව ඔබට විශ්වාසද?"
},
"restoredItem": { "restoredItem": {
"message": "ප්රතිෂ්ඨාපනය අයිතමය" "message": "ප්රතිෂ්ඨාපනය අයිතමය"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Obnoviť položku" "message": "Obnoviť položku"
}, },
"restoreItemConfirmation": {
"message": "Naozaj chcete obnoviť túto položku?"
},
"restoredItem": { "restoredItem": {
"message": "Obnovená položka" "message": "Obnovená položka"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Obnovi element" "message": "Obnovi element"
}, },
"restoreItemConfirmation": {
"message": "Ste prepričani, da želite obnoviti ta element?"
},
"restoredItem": { "restoredItem": {
"message": "Element obnovljen" "message": "Element obnovljen"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Врати ставку" "message": "Врати ставку"
}, },
"restoreItemConfirmation": {
"message": "Да ли сте сигурни да желите да вратите ову ставку?"
},
"restoredItem": { "restoredItem": {
"message": "Ставка враћена" "message": "Ставка враћена"
}, },

View File

@@ -11,7 +11,7 @@
"description": "Extension description" "description": "Extension description"
}, },
"loginOrCreateNewAccount": { "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": { "createAccount": {
"message": "Skapa konto" "message": "Skapa konto"
@@ -342,7 +342,7 @@
"message": "Betygsätt tillägget" "message": "Betygsätt tillägget"
}, },
"rateExtensionDesc": { "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": { "browserNotSupportClipboard": {
"message": "Din webbläsare har inte stöd för att enkelt kopiera till urklipp. Kopiera till urklipp manuellt istället." "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": { "invalidMasterPassword": {
"message": "Felaktigt huvudlösenord" "message": "Ogiltigt huvudlösenord"
}, },
"vaultTimeout": { "vaultTimeout": {
"message": "Valvets tidsgräns" "message": "Valvets tidsgräns"
@@ -736,7 +736,7 @@
"message": "Kopiera verifieringskod" "message": "Kopiera verifieringskod"
}, },
"attachments": { "attachments": {
"message": "Bifogade filer" "message": "Bilagor"
}, },
"deleteAttachment": { "deleteAttachment": {
"message": "Radera bilaga" "message": "Radera bilaga"
@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Återställ objekt" "message": "Återställ objekt"
}, },
"restoreItemConfirmation": {
"message": "Är du säker på att du vill återställa detta objekt?"
},
"restoredItem": { "restoredItem": {
"message": "Återställde objekt" "message": "Återställde objekt"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Restore item" "message": "Restore item"
}, },
"restoreItemConfirmation": {
"message": "Are you sure you want to restore this item?"
},
"restoredItem": { "restoredItem": {
"message": "Item restored" "message": "Item restored"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "กู้คืนรายการ" "message": "กู้คืนรายการ"
}, },
"restoreItemConfirmation": {
"message": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนรายการนี้"
},
"restoredItem": { "restoredItem": {
"message": "คืนค่ารายการแล้ว" "message": "คืนค่ารายการแล้ว"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Kaydı geri yükle" "message": "Kaydı geri yükle"
}, },
"restoreItemConfirmation": {
"message": "Bu kaydı geri yüklemek istediğinizden emin misiniz?"
},
"restoredItem": { "restoredItem": {
"message": "Kayıt geri yüklendi" "message": "Kayıt geri yüklendi"
}, },

View File

@@ -631,10 +631,10 @@
"message": "Оновити" "message": "Оновити"
}, },
"notificationUnlockDesc": { "notificationUnlockDesc": {
"message": "Unlock your Bitwarden vault to complete the auto-fill request." "message": "Розблокуйте своє сховище Bitwarden, щоб завершити запит автозаповнення."
}, },
"notificationUnlock": { "notificationUnlock": {
"message": "Unlock" "message": "Розблокувати"
}, },
"enableContextMenuItem": { "enableContextMenuItem": {
"message": "Показувати в контекстному меню" "message": "Показувати в контекстному меню"
@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Відновити запис" "message": "Відновити запис"
}, },
"restoreItemConfirmation": {
"message": "Ви дійсно хочете відновити цей запис?"
},
"restoredItem": { "restoredItem": {
"message": "Запис відновлено" "message": "Запис відновлено"
}, },
@@ -2228,7 +2225,7 @@
} }
}, },
"loggingInOn": { "loggingInOn": {
"message": "Logging in on" "message": "Увійти на"
}, },
"opensInANewWindow": { "opensInANewWindow": {
"message": "Відкривається у новому вікні" "message": "Відкривається у новому вікні"

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "Khôi phục mục" "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": { "restoredItem": {
"message": "Mục đã được khôi phục" "message": "Mục đã được khôi phục"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "恢复项目" "message": "恢复项目"
}, },
"restoreItemConfirmation": {
"message": "您确定要恢复此项目吗?"
},
"restoredItem": { "restoredItem": {
"message": "项目已恢复" "message": "项目已恢复"
}, },

View File

@@ -1446,9 +1446,6 @@
"restoreItem": { "restoreItem": {
"message": "還原項目" "message": "還原項目"
}, },
"restoreItemConfirmation": {
"message": "您確定要還原此項目嗎?"
},
"restoredItem": { "restoredItem": {
"message": "項目已還原" "message": "項目已還原"
}, },

View File

@@ -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<AuthRequestCryptoServiceAbstraction> {
return factory(
cache,
"authRequestCryptoService",
opts,
async () => new AuthRequestCryptoServiceImplementation(await cryptoServiceFactory(cache, opts))
);
}

View File

@@ -52,6 +52,14 @@ import {
PasswordStrengthServiceInitOptions, PasswordStrengthServiceInitOptions,
} from "../../../tools/background/service_factories/password-strength-service.factory"; } 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 { import {
keyConnectorServiceFactory, keyConnectorServiceFactory,
KeyConnectorServiceInitOptions, KeyConnectorServiceInitOptions,
@@ -75,7 +83,9 @@ export type AuthServiceInitOptions = AuthServiceFactoyOptions &
I18nServiceInitOptions & I18nServiceInitOptions &
EncryptServiceInitOptions & EncryptServiceInitOptions &
PolicyServiceInitOptions & PolicyServiceInitOptions &
PasswordStrengthServiceInitOptions; PasswordStrengthServiceInitOptions &
DeviceTrustCryptoServiceInitOptions &
AuthRequestCryptoServiceInitOptions;
export function authServiceFactory( export function authServiceFactory(
cache: { authService?: AbstractAuthService } & CachedServices, cache: { authService?: AbstractAuthService } & CachedServices,
@@ -101,7 +111,9 @@ export function authServiceFactory(
await i18nServiceFactory(cache, opts), await i18nServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts), await encryptServiceFactory(cache, opts),
await passwordStrengthServiceFactory(cache, opts), await passwordStrengthServiceFactory(cache, opts),
await policyServiceFactory(cache, opts) await policyServiceFactory(cache, opts),
await deviceTrustCryptoServiceFactory(cache, opts),
await authRequestCryptoServiceFactory(cache, opts)
) )
); );
} }

View File

@@ -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<DeviceTrustCryptoServiceAbstraction> {
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)
)
);
}

View File

@@ -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<UserVerificationApiServiceAbstraction> {
return factory(
cache,
"userVerificationApiService",
opts,
async () => new UserVerificationApiService(await apiServiceFactory(cache, opts))
);
}

View File

@@ -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<AbstractUserVerificationService> {
return factory(
cache,
"userVerificationService",
opts,
async () =>
new UserVerificationService(
await stateServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts),
await userVerificationApiServiceFactory(cache, opts)
)
);
}

View File

@@ -5,14 +5,20 @@
<span class="title">{{ "verifyIdentity" | i18n }}</span> <span class="title">{{ "verifyIdentity" | i18n }}</span>
</h1> </h1>
<div class="right"> <div class="right">
<button type="submit" *ngIf="!hideInput">{{ "unlock" | i18n }}</button> <button type="submit" *ngIf="pinEnabled || masterPasswordEnabled">
{{ "unlock" | i18n }}
</button>
</div> </div>
</header> </header>
<main tabindex="-1"> <main tabindex="-1">
<div class="box"> <div class="box">
<div class="box-content"> <div class="box-content">
<div class="box-content-row box-content-row-flex" appBoxRow *ngIf="!hideInput"> <div
<div class="row-main" *ngIf="pinLock"> class="box-content-row box-content-row-flex"
appBoxRow
*ngIf="pinEnabled || masterPasswordEnabled"
>
<div class="row-main" *ngIf="pinEnabled">
<label for="pin">{{ "pin" | i18n }}</label> <label for="pin">{{ "pin" | i18n }}</label>
<input <input
id="pin" id="pin"
@@ -24,7 +30,7 @@
appInputVerbatim appInputVerbatim
/> />
</div> </div>
<div class="row-main" *ngIf="!pinLock"> <div class="row-main" *ngIf="masterPasswordEnabled && !pinEnabled">
<label for="masterPassword">{{ "masterPass" | i18n }}</label> <label for="masterPassword">{{ "masterPass" | i18n }}</label>
<input <input
id="masterPassword" id="masterPassword"

View File

@@ -2,14 +2,14 @@ import { Component, NgZone } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.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 { 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 { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
@@ -19,6 +19,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { DialogService } from "@bitwarden/components";
import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors"; import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors";
@@ -44,13 +45,14 @@ export class LockComponent extends BaseLockComponent {
stateService: StateService, stateService: StateService,
apiService: ApiService, apiService: ApiService,
logService: LogService, logService: LogService,
keyConnectorService: KeyConnectorService,
ngZone: NgZone, ngZone: NgZone,
policyApiService: PolicyApiServiceAbstraction, policyApiService: PolicyApiServiceAbstraction,
policyService: InternalPolicyService, policyService: InternalPolicyService,
passwordStrengthService: PasswordStrengthServiceAbstraction, passwordStrengthService: PasswordStrengthServiceAbstraction,
private authService: AuthService, private authService: AuthService,
dialogService: DialogServiceAbstraction dialogService: DialogService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
userVerificationService: UserVerificationService
) { ) {
super( super(
router, router,
@@ -64,12 +66,13 @@ export class LockComponent extends BaseLockComponent {
stateService, stateService,
apiService, apiService,
logService, logService,
keyConnectorService,
ngZone, ngZone,
policyApiService, policyApiService,
policyService, policyService,
passwordStrengthService, passwordStrengthService,
dialogService dialogService,
deviceTrustCryptoService,
userVerificationService
); );
this.successRoute = "/tabs/current"; this.successRoute = "/tabs/current";
this.isInitialLockScreen = (window as any).previousPopupUrl == null; this.isInitialLockScreen = (window as any).previousPopupUrl == null;
@@ -81,7 +84,7 @@ export class LockComponent extends BaseLockComponent {
(await this.stateService.getDisableAutoBiometricsPrompt()) ?? true; (await this.stateService.getDisableAutoBiometricsPrompt()) ?? true;
window.setTimeout(async () => { window.setTimeout(async () => {
document.getElementById(this.pinLock ? "pin" : "masterPassword").focus(); document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus();
if ( if (
this.biometricLock && this.biometricLock &&
!disableAutoBiometricsPrompt && !disableAutoBiometricsPrompt &&
@@ -93,7 +96,7 @@ export class LockComponent extends BaseLockComponent {
}, 100); }, 100);
} }
async unlockBiometric(): Promise<boolean> { override async unlockBiometric(): Promise<boolean> {
if (!this.biometricLock) { if (!this.biometricLock) {
return; return;
} }

View File

@@ -0,0 +1,108 @@
<div id="login-initiated">
<header>
<h1 class="margin-auto">
<span class="title">{{ "loginInitiated" | i18n }}</span>
</h1>
</header>
<div class="content login-page">
<div class="full-loading-spinner" *ngIf="loading">
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
</div>
<ng-container *ngIf="!loading">
<ng-container *ngIf="data.state == State.ExistingUserUntrustedDevice">
<div class="standard-x-margin">
<p class="lead">{{ "loginInitiated" | i18n }}</p>
<h6 class="mb-20px">{{ "deviceApprovalRequired" | i18n }}</h6>
</div>
<form
id="rememberDeviceForm"
class="mb-20px standard-x-margin"
[formGroup]="rememberDeviceForm"
>
<div>
<input
type="checkbox"
id="rememberDevice"
name="rememberDevice"
formControlName="rememberDevice"
/>
<label for="rememberDevice">
{{ "rememberThisDevice" | i18n }}
</label>
<p id="rememberThisDeviceHintText">{{ "uncheckIfPublicDevice" | i18n }}</p>
</div>
</form>
<div class="box mb-20px">
<button
*ngIf="data.showApproveFromOtherDeviceBtn"
(click)="approveFromOtherDevice()"
type="button"
class="btn primary block"
>
<b>{{ "approveFromYourOtherDevice" | i18n }}</b>
</button>
<button
*ngIf="data.showReqAdminApprovalBtn"
(click)="requestAdminApproval()"
type="button"
class="btn block btn-top-margin"
>
{{ "requestAdminApproval" | i18n }}
</button>
<button
*ngIf="data.showApproveWithMasterPasswordBtn"
type="button"
class="btn block btn-top-margin"
(click)="approveWithMasterPassword()"
>
{{ "approveWithMasterPassword" | i18n }}
</button>
</div>
</ng-container>
<ng-container *ngIf="data.state == State.NewUser">
<div class="standard-x-margin">
<p class="lead">{{ "loginInitiated" | i18n }}</p>
</div>
<form
id="rememberDeviceForm"
class="mb-20px standard-x-margin"
[formGroup]="rememberDeviceForm"
>
<div>
<input
type="checkbox"
id="rememberDevice"
name="rememberDevice"
formControlName="rememberDevice"
/>
<label for="rememberDevice">
{{ "rememberThisDevice" | i18n }}
</label>
<p id="rememberThisDeviceHintText">{{ "uncheckIfPublicDevice" | i18n }}</p>
</div>
</form>
<div class="box mb-20px">
<button (click)="createUser()" type="button" class="btn primary block">
<b>{{ "continue" | i18n }}</b>
</button>
</div>
</ng-container>
<hr class="muted-hr mx-5px mb-20px" />
<div class="small mx-5px">
<p class="no-margin">{{ "loggingInAs" | i18n }} {{ data.userEmail }}</p>
<a tabindex="0" role="button" style="cursor: pointer" (click)="logOut()">{{
"notYou" | i18n
}}</a>
</div>
</ng-container>
</div>
</div>

View File

@@ -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<void> {
try {
await super.createUser();
await this.router.navigate(["/tabs/vault"]);
} catch (error) {
this.validationService.showError(error);
}
}
}

View File

@@ -5,32 +5,57 @@
</h1> </h1>
</header> </header>
<div class="content login-page"> <div class="content login-page">
<div> <ng-container *ngIf="state == StateEnum.StandardAuthRequest">
<p class="lead">{{ "logInInitiated" | i18n }}</p>
<div> <div>
<p>{{ "notificationSentDevice" | i18n }}</p> <p class="lead">{{ "loginInitiated" | i18n }}</p>
<p> <div>
{{ "fingerprintMatchInfo" | i18n }} <p>{{ "notificationSentDevice" | i18n }}</p>
</p>
<p>
{{ "fingerprintMatchInfo" | i18n }}
</p>
</div>
<div>
<b class="fingerprint-phrase-header">{{ "fingerprintPhraseHeader" | i18n }}</b>
<p class="fingerprint-text">
<code>{{ fingerprintPhrase }}</code>
</p>
</div>
<div class="resend-notification" *ngIf="showResendNotification">
<a (click)="startPasswordlessLogin()">{{ "resendNotification" | i18n }}</a>
</div>
<div class="footer">
{{ "loginWithDeviceEnabledInfo" | i18n }}
<a href="#" (click)="back()">{{ "viewAllLoginOptions" | i18n }}</a>
</div>
</div> </div>
</ng-container>
<ng-container *ngIf="state == StateEnum.AdminAuthRequest">
<div> <div>
<b class="fingerprint-phrase-header">{{ "fingerprintPhraseHeader" | i18n }}</b> <p class="lead">{{ "adminApprovalRequested" | i18n }}</p>
<p class="fingerprint-text">
<code>{{ fingerprintPhrase }}</code>
</p>
</div>
<div class="resend-notification" *ngIf="showResendNotification"> <div>
<a (click)="startPasswordlessLogin()">{{ "resendNotification" | i18n }}</a> <p>{{ "adminApprovalRequestSentToAdmins" | i18n }}</p>
</div> <p>{{ "youWillBeNotifiedOnceApproved" | i18n }}</p>
</div>
<div class="footer"> <div>
{{ "loginWithDeviceEnabledInfo" | i18n }} <b class="fingerprint-phrase-header">{{ "fingerprintPhraseHeader" | i18n }}</b>
<a routerLink="/login">{{ "viewAllLoginOptions" | i18n }}</a> <p class="fingerprint-text">
<code>{{ fingerprintPhrase }}</code>
</p>
</div>
<div class="footer">
{{ "troubleLoggingIn" | i18n }}
<a routerLink="/login-initiated">{{ "viewAllLoginOptions" | i18n }}</a>
</div>
</div> </div>
</div> </ng-container>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,13 @@
import { Location } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component"; import { LoginWithDeviceComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-with-device.component";
import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service"; import { AnonymousHubService } from "@bitwarden/common/abstractions/anonymousHub.service";
import { ApiService } from "@bitwarden/common/abstractions/api.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 { 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 { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@@ -42,7 +45,10 @@ export class LoginWithDeviceComponent
validationService: ValidationService, validationService: ValidationService,
stateService: StateService, stateService: StateService,
loginService: LoginService, loginService: LoginService,
syncService: SyncService syncService: SyncService,
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
authReqCryptoService: AuthRequestCryptoServiceAbstraction,
private location: Location
) { ) {
super( super(
router, router,
@@ -59,10 +65,16 @@ export class LoginWithDeviceComponent
anonymousHubService, anonymousHubService,
validationService, validationService,
stateService, stateService,
loginService loginService,
deviceTrustCryptoService,
authReqCryptoService
); );
super.onSuccessfulLogin = async () => { super.onSuccessfulLogin = async () => {
await syncService.fullSync(true); await syncService.fullSync(true);
}; };
} }
protected back() {
this.location.back();
}
} }

View File

@@ -4,8 +4,8 @@ import { ActivatedRoute, Router } from "@angular/router";
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; 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 { 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 { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";

View File

@@ -4,7 +4,6 @@ import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components";
@Component({ @Component({
selector: "app-register", selector: "app-register",
@@ -38,7 +38,7 @@ export class RegisterComponent extends BaseRegisterComponent {
environmentService: EnvironmentService, environmentService: EnvironmentService,
logService: LogService, logService: LogService,
auditService: AuditService, auditService: AuditService,
dialogService: DialogServiceAbstraction dialogService: DialogService
) { ) {
super( super(
formValidationErrorService, formValidationErrorService,

View File

@@ -1,2 +1 @@
export { LockGuardService } from "./lock-guard.service";
export { UnauthGuardService } from "./unauth-guard.service"; export { UnauthGuardService } from "./unauth-guard.service";

View File

@@ -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";
}

View File

@@ -1,6 +1,6 @@
import { Injectable } from "@angular/core"; 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() @Injectable()
export class UnauthGuardService extends BaseUnauthGuardService { export class UnauthGuardService extends BaseUnauthGuardService {

View File

@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { SetPasswordComponent as BaseSetPasswordComponent } from "@bitwarden/angular/components/set-password.component"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.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"; 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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
@Component({ @Component({
selector: "app-set-password", selector: "app-set-password",
@@ -36,7 +36,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent {
route: ActivatedRoute, route: ActivatedRoute,
organizationApiService: OrganizationApiServiceAbstraction, organizationApiService: OrganizationApiServiceAbstraction,
organizationUserService: OrganizationUserService, organizationUserService: OrganizationUserService,
dialogService: DialogServiceAbstraction dialogService: DialogService
) { ) {
super( super(
i18nService, i18nService,

View File

@@ -1,11 +1,12 @@
import { Component } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { SsoComponent as BaseSsoComponent } from "@bitwarden/angular/auth/components/sso.component"; 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 { 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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; 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 { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -35,7 +36,8 @@ export class SsoComponent extends BaseSsoComponent {
syncService: SyncService, syncService: SyncService,
environmentService: EnvironmentService, environmentService: EnvironmentService,
logService: LogService, logService: LogService,
private vaultTimeoutService: VaultTimeoutService configService: ConfigServiceAbstraction,
@Inject(WINDOW) private win: Window
) { ) {
super( super(
authService, authService,
@@ -48,7 +50,8 @@ export class SsoComponent extends BaseSsoComponent {
cryptoFunctionService, cryptoFunctionService,
environmentService, environmentService,
passwordGenerationService, passwordGenerationService,
logService logService,
configService
); );
const url = this.environmentService.getWebVaultUrl(); const url = this.environmentService.getWebVaultUrl();
@@ -57,15 +60,22 @@ export class SsoComponent extends BaseSsoComponent {
this.clientId = "browser"; this.clientId = "browser";
super.onSuccessfulLogin = async () => { 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 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) { if ((await this.authService.getAuthStatus()) !== AuthenticationStatus.Unlocked) {
BrowserApi.reloadOpenWindows(); BrowserApi.reloadOpenWindows();
} }
const thisWindow = window.open("", "_self"); this.win.close();
thisWindow.close(); };
super.onSuccessfulLoginTde = async () => {
syncService.fullSync(true);
};
super.onSuccessfulLoginTdeNavigate = async () => {
this.win.close();
}; };
} }
} }

View File

@@ -1,9 +1,9 @@
import { Component } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular/auth/components/two-factor.component"; 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 { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.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 { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.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 { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
import { PopupUtilsService } from "../../popup/services/popup-utils.service"; import { PopupUtilsService } from "../../popup/services/popup-utils.service";
@@ -48,7 +50,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
twoFactorService: TwoFactorService, twoFactorService: TwoFactorService,
appIdService: AppIdService, appIdService: AppIdService,
loginService: LoginService, loginService: LoginService,
private dialogService: DialogServiceAbstraction configService: ConfigServiceAbstraction,
private dialogService: DialogService,
@Inject(WINDOW) protected win: Window
) { ) {
super( super(
authService, authService,
@@ -56,19 +60,28 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
i18nService, i18nService,
apiService, apiService,
platformUtilsService, platformUtilsService,
window, win,
environmentService, environmentService,
stateService, stateService,
route, route,
logService, logService,
twoFactorService, twoFactorService,
appIdService, appIdService,
loginService loginService,
configService
); );
super.onSuccessfulLogin = () => { super.onSuccessfulLogin = async () => {
this.loginService.clearValues(); syncService.fullSync(true);
return syncService.fullSync(true);
}; };
super.onSuccessfulLoginTde = async () => {
syncService.fullSync(true);
};
super.onSuccessfulLoginTdeNavigate = async () => {
this.win.close();
};
super.successRoute = "/tabs/vault"; super.successRoute = "/tabs/vault";
// FIXME: Chromium 110 has broken WebAuthn support in extensions via an iframe // FIXME: Chromium 110 has broken WebAuthn support in extensions via an iframe
this.webAuthnNewTab = true; this.webAuthnNewTab = true;
@@ -107,7 +120,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
const confirmed = await this.dialogService.openSimpleDialog({ const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" }, title: { key: "warning" },
content: { key: "popup2faCloseMessage" }, content: { key: "popup2faCloseMessage" },
type: SimpleDialogType.WARNING, type: "warning",
}); });
if (confirmed) { if (confirmed) {
this.popupUtilsService.popOut(window); 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 // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.queryParams.pipe(first()).subscribe(async (qParams) => { this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
if (qParams.sso === "true") { 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 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 // This call is executed by the service that lives in the background script so it will continue
// the sync even if this tab closes. // 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 // Force sidebars (FF && Opera) to reload while exempting current window
// because we are just going to close the 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 // 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) // on the web vault screen which tells them to continue in the browser extension (sidebar or popup)
BrowserApi.closeBitwardenExtensionTab(); BrowserApi.closeBitwardenExtensionTab();
return syncPromise;
}; };
} }
}); });

View File

@@ -2,6 +2,10 @@ import {
TotpServiceInitOptions, TotpServiceInitOptions,
totpServiceFactory, totpServiceFactory,
} from "../../../auth/background/service-factories/totp-service.factory"; } from "../../../auth/background/service-factories/totp-service.factory";
import {
UserVerificationServiceInitOptions,
userVerificationServiceFactory,
} from "../../../auth/background/service-factories/user-verification-service.factory";
import { import {
EventCollectionServiceInitOptions, EventCollectionServiceInitOptions,
eventCollectionServiceFactory, eventCollectionServiceFactory,
@@ -38,7 +42,8 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions &
TotpServiceInitOptions & TotpServiceInitOptions &
EventCollectionServiceInitOptions & EventCollectionServiceInitOptions &
LogServiceInitOptions & LogServiceInitOptions &
SettingsServiceInitOptions; SettingsServiceInitOptions &
UserVerificationServiceInitOptions;
export function autofillServiceFactory( export function autofillServiceFactory(
cache: { autofillService?: AbstractAutoFillService } & CachedServices, cache: { autofillService?: AbstractAutoFillService } & CachedServices,
@@ -55,7 +60,8 @@ export function autofillServiceFactory(
await totpServiceFactory(cache, opts), await totpServiceFactory(cache, opts),
await eventCollectionServiceFactory(cache, opts), await eventCollectionServiceFactory(cache, opts),
await logServiceFactory(cache, opts), await logServiceFactory(cache, opts),
await settingsServiceFactory(cache, opts) await settingsServiceFactory(cache, opts),
await userVerificationServiceFactory(cache, opts)
) )
); );
} }

View File

@@ -1,6 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; 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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
@@ -13,6 +14,7 @@ describe("CipherContextMenuHandler", () => {
let mainContextMenuHandler: MockProxy<MainContextMenuHandler>; let mainContextMenuHandler: MockProxy<MainContextMenuHandler>;
let authService: MockProxy<AuthService>; let authService: MockProxy<AuthService>;
let cipherService: MockProxy<CipherService>; let cipherService: MockProxy<CipherService>;
let userVerificationService: MockProxy<UserVerificationService>;
let sut: CipherContextMenuHandler; let sut: CipherContextMenuHandler;
@@ -20,10 +22,17 @@ describe("CipherContextMenuHandler", () => {
mainContextMenuHandler = mock(); mainContextMenuHandler = mock();
authService = mock(); authService = mock();
cipherService = mock(); cipherService = mock();
userVerificationService = mock();
userVerificationService.hasMasterPassword.mockResolvedValue(true);
jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue(); jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue();
sut = new CipherContextMenuHandler(mainContextMenuHandler, authService, cipherService); sut = new CipherContextMenuHandler(
mainContextMenuHandler,
authService,
cipherService,
userVerificationService
);
}); });
afterEach(() => jest.resetAllMocks()); afterEach(() => jest.resetAllMocks());
@@ -83,11 +92,11 @@ describe("CipherContextMenuHandler", () => {
}; };
cipherService.getAllDecryptedForUrl.mockResolvedValue([ cipherService.getAllDecryptedForUrl.mockResolvedValue([
null, null, // invalid cipher
undefined, undefined, // invalid cipher
{ type: CipherType.Card }, { type: CipherType.Card }, // invalid cipher
{ type: CipherType.Login, reprompt: CipherRepromptType.Password }, { type: CipherType.Login, reprompt: CipherRepromptType.Password }, // invalid cipher
realCipher, realCipher, // valid cipher
] as any[]); ] as any[]);
await sut.update("https://test.com"); await sut.update("https://test.com");
@@ -96,7 +105,7 @@ describe("CipherContextMenuHandler", () => {
expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com");
expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(1); expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(2);
expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith(
"Test Cipher (Test Username)", "Test Cipher (Test Username)",
@@ -105,5 +114,61 @@ describe("CipherContextMenuHandler", () => {
realCipher 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
);
});
}); });
}); });

View File

@@ -1,10 +1,10 @@
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; 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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state"; import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; 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 { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@@ -12,6 +12,7 @@ import {
authServiceFactory, authServiceFactory,
AuthServiceInitOptions, AuthServiceInitOptions,
} from "../../auth/background/service-factories/auth-service.factory"; } 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 { Account } from "../../models/account";
import { CachedServices } from "../../platform/background/service-factories/factory-options"; import { CachedServices } from "../../platform/background/service-factories/factory-options";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
@@ -38,7 +39,8 @@ export class CipherContextMenuHandler {
constructor( constructor(
private mainContextMenuHandler: MainContextMenuHandler, private mainContextMenuHandler: MainContextMenuHandler,
private authService: AuthService, private authService: AuthService,
private cipherService: CipherService private cipherService: CipherService,
private userVerificationService: UserVerificationService
) {} ) {}
static async create(cachedServices: CachedServices) { static async create(cachedServices: CachedServices) {
@@ -77,7 +79,8 @@ export class CipherContextMenuHandler {
return new CipherContextMenuHandler( return new CipherContextMenuHandler(
await MainContextMenuHandler.mv3Create(cachedServices), await MainContextMenuHandler.mv3Create(cachedServices),
await authServiceFactory(cachedServices, serviceOptions), await authServiceFactory(cachedServices, serviceOptions),
await cipherServiceFactory(cachedServices, serviceOptions) await cipherServiceFactory(cachedServices, serviceOptions),
await userVerificationServiceFactory(cachedServices, serviceOptions)
); );
} }
@@ -180,7 +183,7 @@ export class CipherContextMenuHandler {
if ( if (
cipher == null || cipher == null ||
cipher.type !== CipherType.Login || cipher.type !== CipherType.Login ||
cipher.reprompt !== CipherRepromptType.None (await this.userVerificationService.hasMasterPasswordAndMasterKeyHash())
) { ) {
return; return;
} }

View File

@@ -203,17 +203,45 @@ export class ContextMenuClickedHandler {
if (tab == null) { if (tab == null) {
return; 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; break;
case COPY_USERNAME_ID: case COPY_USERNAME_ID:
this.copyToClipboard({ text: cipher.login.username, tab: tab }); this.copyToClipboard({ text: cipher.login.username, tab: tab });
break; break;
case COPY_PASSWORD_ID: case COPY_PASSWORD_ID:
this.copyToClipboard({ text: cipher.login.password, tab: tab }); if (cipher.reprompt !== CipherRepromptType.None) {
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); 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; break;
case COPY_VERIFICATIONCODE_ID: 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; break;
} }
} }

View File

@@ -28,6 +28,7 @@ window.addEventListener(
const forwardCommands = [ const forwardCommands = [
"promptForLogin", "promptForLogin",
"passwordReprompt",
"addToLockedVaultPendingNotifications", "addToLockedVaultPendingNotifications",
"unlockCompleted", "unlockCompleted",
"addedCipher", "addedCipher",

View File

@@ -1,6 +1,7 @@
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { TotpService } from "@bitwarden/common/abstractions/totp.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 { EventType, FieldType, UriMatchType } from "@bitwarden/common/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -45,7 +46,8 @@ export default class AutofillService implements AutofillServiceInterface {
private totpService: TotpService, private totpService: TotpService,
private eventCollectionService: EventCollectionService, private eventCollectionService: EventCollectionService,
private logService: LogService, private logService: LogService,
private settingsService: SettingsService private settingsService: SettingsService,
private userVerificationService: UserVerificationService
) {} ) {}
getFormsWithPasswordFields(pageDetails: AutofillPageDetails): FormData[] { 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; return null;
} }

View File

@@ -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 { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

View File

@@ -1,5 +1,5 @@
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; 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 { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";

View File

@@ -1,27 +1,33 @@
import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.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 { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service"; import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.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/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; 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 { 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 { 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 { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.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 { 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 { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.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 { 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 { 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 { 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 { 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 { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service"; import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.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 { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service";
import { ApiService } from "@bitwarden/common/services/api.service"; import { ApiService } from "@bitwarden/common/services/api.service";
import { AuditService } from "@bitwarden/common/services/audit.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 { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/services/notifications.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service";
import { SearchService } from "@bitwarden/common/services/search.service"; import { SearchService } from "@bitwarden/common/services/search.service";
import { TotpService } from "@bitwarden/common/services/totp.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 { import {
PasswordGenerationService, PasswordGenerationService,
PasswordGenerationServiceAbstraction, PasswordGenerationServiceAbstraction,
@@ -136,7 +143,7 @@ import { PopupUtilsService } from "../popup/services/popup-utils.service";
import { BrowserSendService } from "../services/browser-send.service"; import { BrowserSendService } from "../services/browser-send.service";
import { BrowserSettingsService } from "../services/browser-settings.service"; import { BrowserSettingsService } from "../services/browser-settings.service";
import { BrowserFido2UserInterfaceService } from "../services/fido2/browser-fido2-user-interface.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 { BrowserFolderService } from "../vault/services/browser-folder.service";
import { VaultFilterService } from "../vault/services/vault-filter.service"; import { VaultFilterService } from "../vault/services/vault-filter.service";
@@ -164,7 +171,7 @@ export default class MainBackground {
cipherService: CipherServiceAbstraction; cipherService: CipherServiceAbstraction;
folderService: InternalFolderServiceAbstraction; folderService: InternalFolderServiceAbstraction;
collectionService: CollectionServiceAbstraction; collectionService: CollectionServiceAbstraction;
vaultTimeoutService: VaultTimeoutServiceAbstraction; vaultTimeoutService: VaultTimeoutService;
vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction; vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction;
syncService: SyncServiceAbstraction; syncService: SyncServiceAbstraction;
passwordGenerationService: PasswordGenerationServiceAbstraction; passwordGenerationService: PasswordGenerationServiceAbstraction;
@@ -207,6 +214,10 @@ export default class MainBackground {
cipherContextMenuHandler: CipherContextMenuHandler; cipherContextMenuHandler: CipherContextMenuHandler;
configService: ConfigServiceAbstraction; configService: ConfigServiceAbstraction;
configApiService: ConfigApiServiceAbstraction; configApiService: ConfigApiServiceAbstraction;
devicesApiService: DevicesApiServiceAbstraction;
devicesService: DevicesServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction;
authRequestCryptoService: AuthRequestCryptoServiceAbstraction;
popupUtilsService: PopupUtilsService; popupUtilsService: PopupUtilsService;
browserPopoutWindowService: BrowserPopoutWindowService; browserPopoutWindowService: BrowserPopoutWindowService;
@@ -399,6 +410,23 @@ export default class MainBackground {
that.runtimeBackground.processMessage(message, that as any); 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.authService = new AuthService(
this.cryptoService, this.cryptoService,
this.apiService, this.apiService,
@@ -414,14 +442,26 @@ export default class MainBackground {
this.i18nService, this.i18nService,
this.encryptService, this.encryptService,
this.passwordStrengthService, 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.vaultTimeoutSettingsService = new VaultTimeoutSettingsService(
this.cryptoService, this.cryptoService,
this.tokenService, this.tokenService,
this.policyService, this.policyService,
this.stateService this.stateService,
this.userVerificationService
); );
this.vaultTimeoutService = new VaultTimeoutService( this.vaultTimeoutService = new VaultTimeoutService(
@@ -432,7 +472,6 @@ export default class MainBackground {
this.platformUtilsService, this.platformUtilsService,
this.messagingService, this.messagingService,
this.searchService, this.searchService,
this.keyConnectorService,
this.stateService, this.stateService,
this.authService, this.authService,
this.vaultTimeoutSettingsService, this.vaultTimeoutSettingsService,
@@ -483,13 +522,15 @@ export default class MainBackground {
this.eventUploadService this.eventUploadService
); );
this.totpService = new TotpService(this.cryptoFunctionService, this.logService); this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
this.autofillService = new AutofillService( this.autofillService = new AutofillService(
this.cipherService, this.cipherService,
this.stateService, this.stateService,
this.totpService, this.totpService,
this.eventCollectionService, this.eventCollectionService,
this.logService, this.logService,
this.settingsService this.settingsService,
this.userVerificationService
); );
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService); this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
this.exportService = new VaultExportService( this.exportService = new VaultExportService(
@@ -511,15 +552,6 @@ export default class MainBackground {
this.authService, this.authService,
this.messagingService 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.configApiService = new ConfigApiService(this.apiService, this.authService);
this.configService = new ConfigService( this.configService = new ConfigService(
@@ -528,6 +560,7 @@ export default class MainBackground {
this.authService, this.authService,
this.environmentService this.environmentService
); );
this.browserPopoutWindowService = new BrowserPopoutWindowService(); this.browserPopoutWindowService = new BrowserPopoutWindowService();
this.popupUtilsService = new PopupUtilsService(this.isPrivateMode); this.popupUtilsService = new PopupUtilsService(this.isPrivateMode);
@@ -665,7 +698,8 @@ export default class MainBackground {
this.cipherContextMenuHandler = new CipherContextMenuHandler( this.cipherContextMenuHandler = new CipherContextMenuHandler(
this.mainContextMenuHandler, this.mainContextMenuHandler,
this.authService, this.authService,
this.cipherService this.cipherService,
this.userVerificationService
); );
} }
} }
@@ -675,7 +709,7 @@ export default class MainBackground {
await this.stateService.init(); await this.stateService.init();
await (this.vaultTimeoutService as VaultTimeoutService).init(true); await this.vaultTimeoutService.init(true);
await (this.i18nService as BrowserI18nService).init(); await (this.i18nService as BrowserI18nService).init();
await (this.eventUploadService as EventUploadService).init(true); await (this.eventUploadService as EventUploadService).init(true);
await this.runtimeBackground.init(); await this.runtimeBackground.init();

View File

@@ -10,7 +10,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; 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"; import { BrowserApi } from "../platform/browser/browser-api";
@@ -42,6 +46,7 @@ type ReceiveMessage = {
// Unlock key // Unlock key
keyB64?: string; keyB64?: string;
userKeyB64?: string;
}; };
type ReceiveMessageOuter = { type ReceiveMessageOuter = {
@@ -320,16 +325,55 @@ export class NativeMessagingBackground {
} }
if (message.response === "unlocked") { if (message.response === "unlocked") {
await this.cryptoService.setKey( try {
new SymmetricCryptoKey(Utils.fromB64ToArray(message.keyB64)) 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 // Verify key is correct by attempting to decrypt a secret
try { try {
await this.cryptoService.getFingerprint(await this.stateService.getUserId()); await this.cryptoService.getFingerprint(await this.stateService.getUserId());
} catch (e) { } catch (e) {
this.logService.error("Unable to verify key: " + e); this.logService.error("Unable to verify key: " + e);
await this.cryptoService.clearKey(); await this.cryptoService.clearKeys();
this.showWrongUserDialog(); this.showWrongUserDialog();
// Exit early // Exit early

View File

@@ -79,6 +79,8 @@ export default class RuntimeBackground {
} }
async processMessage(msg: any, sender: chrome.runtime.MessageSender) { async processMessage(msg: any, sender: chrome.runtime.MessageSender) {
const cipherId = msg.data?.cipherId;
switch (msg.command) { switch (msg.command) {
case "loggedIn": case "loggedIn":
case "unlocked": { case "unlocked": {
@@ -86,7 +88,7 @@ export default class RuntimeBackground {
if (this.lockedVaultPendingNotifications?.length > 0) { if (this.lockedVaultPendingNotifications?.length > 0) {
item = this.lockedVaultPendingNotifications.pop(); item = this.lockedVaultPendingNotifications.pop();
await this.browserPopoutWindowService.closeLoginPrompt(); await this.browserPopoutWindowService.closeUnlockPrompt();
} }
await this.main.refreshBadge(); await this.main.refreshBadge();
@@ -126,13 +128,22 @@ export default class RuntimeBackground {
break; break;
case "promptForLogin": case "promptForLogin":
case "bgReopenPromptForLogin": 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; break;
case "openAddEditCipher": { case "openAddEditCipher": {
const addEditCipherUrl = const addEditCipherUrl =
msg.data?.cipherId == null cipherId == null
? "popup/index.html#/edit-cipher" ? "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); BrowserApi.openBitwardenExtensionTab(addEditCipherUrl, true);
break; break;

View File

@@ -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<DevicesApiServiceAbstraction> {
return factory(
cache,
"devicesApiService",
opts,
async () => new DevicesApiServiceImplementation(await apiServiceFactory(cache, opts))
);
}

View File

@@ -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 { import {
authServiceFactory, authServiceFactory,
AuthServiceInitOptions, AuthServiceInitOptions,
} from "../../auth/background/service-factories/auth-service.factory"; } from "../../auth/background/service-factories/auth-service.factory";
import {
keyConnectorServiceFactory,
KeyConnectorServiceInitOptions,
} from "../../auth/background/service-factories/key-connector-service.factory";
import { import {
CryptoServiceInitOptions, CryptoServiceInitOptions,
cryptoServiceFactory, cryptoServiceFactory,
@@ -29,7 +25,7 @@ import {
StateServiceInitOptions, StateServiceInitOptions,
stateServiceFactory, stateServiceFactory,
} from "../../platform/background/service-factories/state-service.factory"; } 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 { import {
cipherServiceFactory, cipherServiceFactory,
CipherServiceInitOptions, CipherServiceInitOptions,
@@ -64,7 +60,6 @@ export type VaultTimeoutServiceInitOptions = VaultTimeoutServiceFactoryOptions &
PlatformUtilsServiceInitOptions & PlatformUtilsServiceInitOptions &
MessagingServiceInitOptions & MessagingServiceInitOptions &
SearchServiceInitOptions & SearchServiceInitOptions &
KeyConnectorServiceInitOptions &
StateServiceInitOptions & StateServiceInitOptions &
AuthServiceInitOptions & AuthServiceInitOptions &
VaultTimeoutSettingsServiceInitOptions; VaultTimeoutSettingsServiceInitOptions;
@@ -86,7 +81,6 @@ export function vaultTimeoutServiceFactory(
await platformUtilsServiceFactory(cache, opts), await platformUtilsServiceFactory(cache, opts),
await messagingServiceFactory(cache, opts), await messagingServiceFactory(cache, opts),
await searchServiceFactory(cache, opts), await searchServiceFactory(cache, opts),
await keyConnectorServiceFactory(cache, opts),
await stateServiceFactory(cache, opts), await stateServiceFactory(cache, opts),
await authServiceFactory(cache, opts), await authServiceFactory(cache, opts),
await vaultTimeoutSettingsServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts),

Some files were not shown because too many files have changed in this diff Show More