diff --git a/.eslintignore b/.eslintignore index f34b9683158..e1fbda38133 100644 --- a/.eslintignore +++ b/.eslintignore @@ -27,3 +27,5 @@ apps/cli/config/config.js tailwind.config.js libs/components/tailwind.config.base.js libs/components/tailwind.config.js + +scripts/*.js diff --git a/.github/renovate.json b/.github/renovate.json index 62559235cd0..81dea677317 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -11,22 +11,45 @@ ":separateMajorReleases" ], "prConcurrentLimit": 3, - "enabledManagers": ["npm"], + "enabledManagers": ["cargo", "github-actions", "npm"], "packageRules": [ { - "groupName": "{{manager}}", - "matchPackagePatterns": ["*"], + "groupName": "cargo minor", + "matchManagers": ["cargo"], "matchUpdateTypes": ["minor", "patch"] }, { - "packageNames": ["typescript"], + "groupName": "gh minor", + "matchManagers": ["github-actions"], + "matchUpdateTypes": ["minor", "patch"] + }, + { + "groupName": "npm minor", + "matchManagers": ["npm"], + "matchUpdateTypes": ["minor", "patch"] + }, + { + "matchPackageNames": ["typescript"], "matchUpdateTypes": ["major", "minor"], "enabled": false }, { - "packageNames": ["typescript"], + "matchPackageNames": ["typescript"], "matchUpdateTypes": "patch" + }, + { + "groupName": "jest", + "matchPackageNames": ["@types/jest", "jest", "ts-jest", "jest-preset-angular"], + "matchUpdateTypes": "major" } ], - "ignoreDeps": ["bootstrap", "electron-builder", "electron", "node-ipc", "regedit", "zone.js"] + "ignoreDeps": [ + "@types/koa-bodyparser", + "bootstrap", + "electron-builder", + "electron", + "node-ipc", + "regedit", + "zone.js" + ] } diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 2d35cdca35b..d02bfa12d03 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -25,14 +25,6 @@ ./libs/angular/src/services/theming/themeBuilder.ts ./libs/angular/src/interfaces/selectOptions.ts ./libs/components/src/stories/Introduction.stories.mdx -./libs/common/spec/web/services/webCryptoFunction.service.spec.ts -./libs/common/spec/shared/interceptConsole.ts -./libs/common/spec/models/domain/symmetricCryptoKey.spec.ts -./libs/common/spec/models/domain/encArrayBuffer.spec.ts -./libs/common/spec/matchers/toEqualBuffer.spec.ts -./libs/common/spec/matchers/toEqualBuffer.ts -./libs/common/spec/services/stateMigration.service.spec.ts -./libs/common/spec/services/consoleLog.service.spec.ts ./libs/common/src/misc/nodeUtils.ts ./libs/common/src/misc/linkedFieldOption.decorator.ts ./libs/common/src/misc/serviceUtils.ts diff --git a/.github/workflows/automatic-issue-responses.yml b/.github/workflows/automatic-issue-responses.yml index badc10d3342..90d561c9221 100644 --- a/.github/workflows/automatic-issue-responses.yml +++ b/.github/workflows/automatic-issue-responses.yml @@ -14,7 +14,7 @@ jobs: # Feature request - if: github.event.label.name == 'feature-request' name: Feature request - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | We use GitHub issues as a place to track bugs and other development related issues. The [Bitwarden Community Forums](https://community.bitwarden.com/) has a [Feature Requests](https://community.bitwarden.com/c/feature-requests) section for submitting, voting for, and discussing requests like this one. @@ -25,7 +25,7 @@ jobs: # Intended behavior - if: github.event.label.name == 'intended-behavior' name: Intended behaviour - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | Your issue appears to be describing the intended behavior of the software. If you want this to be changed, it would be a feature request. @@ -38,7 +38,7 @@ jobs: # Customer support request - if: github.event.label.name == 'customer-support' name: Customer Support request - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | We use GitHub issues as a place to track bugs and other development related issues. Your issue appears to be a support request, or would otherwise be better handled by our dedicated Customer Success team. @@ -49,14 +49,14 @@ jobs: # Resolved - if: github.event.label.name == 'resolved' name: Resolved - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | We’ve closed this issue, as it appears the original problem has been resolved. If this happens again or continues to be a problem, please respond to this issue with any additional detail to assist with reproduction and root cause analysis. # Stale - if: github.event.label.name == 'stale' name: Stale - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | As we haven’t heard from you about this problem in some time, this issue will now be closed. diff --git a/.github/workflows/automatic-pull-request-responses.yml b/.github/workflows/automatic-pull-request-responses.yml index e994c2f59b8..261cd5c2555 100644 --- a/.github/workflows/automatic-pull-request-responses.yml +++ b/.github/workflows/automatic-pull-request-responses.yml @@ -14,11 +14,11 @@ jobs: # Translation PR / Crowdin - if: github.event.label.name == 'translation-pr' name: Translation-PR - uses: peter-evans/close-issue@849549ba7c3a595a064c4b2c56f206ee78f93515 # v2.0.0 + uses: peter-evans/close-issue@276d7966e389d888f011539a86c8920025ea0626 # v3.0.1 with: comment: | We really appreciate you taking the time to improve our translations. - + However we use a translation tool called [Crowdin](https://crowdin.com/) to help manage our localization efforts across many different languages. Our translations can only be updated using Crowdin, so we'll have to close this PR, but would really appreciate if you'd consider joining our awesome translation community over at Crowdin. - + More information can be found in the [localization section](https://contributing.bitwarden.com/contributing/#localization-l10n) of our [Contribution Guidelines](https://contributing.bitwarden.com/contributing/) diff --git a/.github/workflows/brew-bump-cli.yml b/.github/workflows/brew-bump-cli.yml index f06517cec62..c5f7b126fe2 100644 --- a/.github/workflows/brew-bump-cli.yml +++ b/.github/workflows/brew-bump-cli.yml @@ -17,19 +17,19 @@ jobs: runs-on: macos-11 steps: - name: Login to Azure - uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "brew-bump-workflow-pat" - name: Update Homebrew formula - uses: dawidd6/action-homebrew-bump-formula@dd221ff435f42fa8102b5871bb1929af9d76476c + uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0 with: # Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }} diff --git a/.github/workflows/brew-bump-desktop.yml b/.github/workflows/brew-bump-desktop.yml index 32999bba4ab..876180931c6 100644 --- a/.github/workflows/brew-bump-desktop.yml +++ b/.github/workflows/brew-bump-desktop.yml @@ -17,19 +17,19 @@ jobs: runs-on: macos-11 steps: - name: Login to Azure - uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "brew-bump-workflow-pat" - name: Update Homebrew cask - uses: macauley/action-homebrew-bump-cask@445c42390d790569d938f9068d01af39ca030feb + uses: macauley/action-homebrew-bump-cask@445c42390d790569d938f9068d01af39ca030feb # v1.0.0 with: # Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }} diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index a551fb9d5a4..a48d8b7880b 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up cloc run: | @@ -79,7 +79,7 @@ jobs: working-directory: apps/browser steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Testing locales - extName length run: | @@ -119,10 +119,10 @@ jobs: working-directory: apps/browser steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3.3.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -173,56 +173,56 @@ jobs: working-directory: ./ - name: Upload Opera artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-opera-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-opera.zip if-no-files-found: error - name: Upload Opera MV3 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-opera-mv3.zip if-no-files-found: error - name: Upload Chrome artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-chrome-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-chrome.zip if-no-files-found: error - name: Upload Chrome MV3 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-chrome-mv3.zip if-no-files-found: error - name: Upload Firefox artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-firefox-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-firefox.zip if-no-files-found: error - name: Upload Edge artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-edge-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-edge.zip if-no-files-found: error - name: Upload Edge MV3 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-edge-mv3.zip if-no-files-found: error - name: Upload browser source - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip @@ -230,7 +230,7 @@ jobs: - name: Upload coverage artifact if: false - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: coverage-${{ env._BUILD_NUMBER }}.zip path: apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip @@ -246,10 +246,10 @@ jobs: _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3.3.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -330,7 +330,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-safari.zip @@ -345,22 +345,22 @@ jobs: - build-safari steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" - name: Upload Sources - uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9 + uses: crowdin/github-action@102b5aa21783a64027193ef802a616140a1ca102 # v1.8.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -408,7 +408,7 @@ jobs: fi - name: Login to Azure - Prod Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 if: failure() with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} @@ -416,13 +416,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 # v1.5.0 + uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 58e80442b70..4e9ce9e2bb6 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up cloc run: | @@ -56,7 +56,7 @@ jobs: package_version: ${{ steps.retrieve-version.outputs.package_version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Get Package Version id: retrieve-version @@ -79,7 +79,7 @@ jobs: _WIN_PKG_VERSION: 3.4 steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Setup Unix Vars run: | @@ -88,7 +88,7 @@ jobs: awk '{print tolower($0)}')" >> $GITHUB_ENV - name: Set up Node - uses: actions/setup-node@17f8bd926464a1afa4c6a11669539e9c1ba77048 # v3.2.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -129,14 +129,14 @@ jobs: | awk '{split($0, a); print a[1]}' > bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt - name: Upload unix zip asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload unix checksum asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -153,7 +153,7 @@ jobs: _WIN_PKG_VERSION: 3.4 steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Setup Windows builder run: | @@ -162,7 +162,7 @@ jobs: choco install nasm --no-progress - name: Set up Node - uses: actions/setup-node@17f8bd926464a1afa4c6a11669539e9c1ba77048 # v3.2.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -264,28 +264,28 @@ jobs: -t sha256 | Out-File -Encoding ASCII ./dist/bw-windows-sha256-${env:_PACKAGE_VERSION}.txt - name: Upload windows zip asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw-windows-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload windows checksum asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw-windows-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw-windows-sha256-${{ env._PACKAGE_VERSION }}.txt if-no-files-found: error - name: Upload Chocolatey asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload NPM Build Directory asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/build @@ -299,7 +299,7 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Print environment run: | @@ -309,7 +309,7 @@ jobs: echo "BW Package Version: $_PACKAGE_VERSION" - name: Get bw linux cli - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: bw-linux-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/snap @@ -322,7 +322,7 @@ jobs: ls -alth - name: Build snap - uses: snapcore/action-build@ea14cdeb353272f75977040488ca191880509a8c # v1.1.0 + uses: snapcore/action-build@3457752ec9b1c79a8290b5167fce2d14df0997c1 # v1.1.2 with: path: apps/cli/dist/snap @@ -351,14 +351,14 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload snap checksum asset - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -396,7 +396,7 @@ jobs: fi - name: Login to Azure - Prod Subscription - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 if: failure() with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} @@ -404,13 +404,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 + uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index f863d470093..b1989252dc8 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up cloc run: | @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Verify run: | @@ -83,7 +83,7 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Get Package Version id: retrieve-version @@ -143,10 +143,10 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -180,7 +180,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: | @@ -204,42 +204,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -259,10 +259,10 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -277,7 +277,7 @@ jobs: node-gyp install $(node -v) - name: Install AST - uses: bitwarden/gh-actions/install-ast@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/install-ast@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b - name: Set up environmentF run: choco install checksum --no-progress @@ -296,13 +296,13 @@ jobs: rustup show - name: Login to Azure - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "code-signing-vault-url, @@ -316,7 +316,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: apps/desktop/desktop_native/*.node @@ -369,91 +369,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -472,10 +472,10 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -503,14 +503,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -588,7 +588,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: apps/desktop/desktop_native/*.node @@ -624,10 +624,10 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -655,14 +655,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -740,7 +740,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: apps/desktop/desktop_native/*.node @@ -756,7 +756,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -779,28 +779,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml @@ -821,10 +821,10 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -852,14 +852,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -937,7 +937,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: apps/desktop/desktop_native/*.node @@ -953,7 +953,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -976,7 +976,7 @@ jobs: CSC_FOR_PULL_REQUEST: true - name: Upload .pkg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -1010,10 +1010,10 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -1036,14 +1036,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1121,7 +1121,7 @@ jobs: working-directory: ./ - name: Cache Native Module - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 id: cache with: path: apps/desktop/desktop_native/*.node @@ -1137,7 +1137,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1163,7 +1163,7 @@ jobs: run: zip -r Bitwarden-${{ env.PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app - name: Upload masdev artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip @@ -1181,22 +1181,22 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" - name: Upload Sources - uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 + uses: crowdin/github-action@102b5aa21783a64027193ef802a616140a1ca102 # v1.8.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -1261,7 +1261,7 @@ jobs: fi - name: Login to Azure - Prod Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 if: failure() with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} @@ -1269,13 +1269,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@471ae4aec27405f16c5b796e288f54262c406e5d + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 + uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 49e0d419a01..325d3f54df4 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up cloc run: | @@ -56,7 +56,7 @@ jobs: version: ${{ steps.version.outputs.value }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Get GitHub sha as version id: version @@ -87,10 +87,10 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # v3.4.1 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -126,7 +126,7 @@ jobs: run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build - name: Upload ${{ matrix.name }} artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: web-${{ env._VERSION }}-${{ matrix.name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip @@ -156,11 +156,11 @@ jobs: _VERSION: ${{ needs.setup.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Check Branch to Publish env: - PUBLISH_BRANCHES: "master,rc,hotfix-rc" + PUBLISH_BRANCHES: "master,rc,hotfix-rc-web" id: publish-branch-check run: | IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES @@ -173,7 +173,7 @@ jobs: ########## ACRs ########## - name: Login to Azure - QA - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }} @@ -181,7 +181,7 @@ jobs: run: az acr login -n bitwardenqa - name: Login to Azure - Prod - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} @@ -189,7 +189,7 @@ jobs: run: az acr login -n bitwardenprod - name: Download ${{ matrix.artifact_name }} artifact - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip path: apps/web @@ -229,13 +229,13 @@ jobs: run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + 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@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "github-pat-bitwarden-devops-bot-repo-scope" @@ -243,13 +243,13 @@ jobs: - name: Setup DCT if: ${{ env.is_publish_branch == 'true' }} id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff + uses: bitwarden/gh-actions/setup-docker-trust@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} azure-keyvault-name: "bitwarden-ci" - name: Build Docker image - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v4.0.0 with: context: apps/web file: apps/web/Dockerfile @@ -282,22 +282,22 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 # v2.3.4 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token" - name: Upload Sources - uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9 + uses: crowdin/github-action@102b5aa21783a64027193ef802a616140a1ca102 # v1.8.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -344,7 +344,7 @@ jobs: fi - name: Login to Azure - Prod Subscription - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 if: failure() with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} @@ -352,13 +352,13 @@ jobs: - name: Retrieve secrets id: retrieve-secrets if: failure() - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" - name: Notify Slack on failure - uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33 # v1.5.1 + uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 7da41236742..453a19423ab 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -13,18 +13,18 @@ jobs: steps: - name: Set up Node - uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea # v2.1.5 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: node-version: "16" - name: Checkout repo - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: fetch-depth: 0 - name: Cache npm id: npm-cache - uses: actions/cache@c64c572235d810460d0d6876e9c705ad5002b353 # v2.1.6 + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: "~/.npm" key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} @@ -33,7 +33,7 @@ jobs: run: npm ci - name: Publish to Chromatic - uses: chromaui/action@c72f0b48c8887c0ef0abe18ad865a6c1e01e73c6 + uses: chromaui/action@a89b674adf766dbde41ad9ea2b2b60b91188a0f0 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index cda1f39262a..35c31dc72ff 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -23,22 +23,22 @@ jobs: crowdin_project_id: "308189" steps: - name: Checkout repo - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - name: Download translations - uses: bitwarden/gh-actions/crowdin@05052c5c575ceb09ceea397fe241879e199ed44b + uses: bitwarden/gh-actions/crowdin@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/deploy-non-prod-web.yml b/.github/workflows/deploy-non-prod-web.yml index f5ca49964de..45f74ff52be 100644 --- a/.github/workflows/deploy-non-prod-web.yml +++ b/.github/workflows/deploy-non-prod-web.yml @@ -50,7 +50,7 @@ jobs: _ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} steps: - name: Create GitHub deployment - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -60,10 +60,10 @@ jobs: description: 'Deployment from branch ${{ github.ref_name }}' - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml path: apps/web @@ -76,7 +76,7 @@ jobs: run: unzip ${{ env._ENVIRONMENT_ARTIFACT }} - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ env._ENVIRONMENT_BRANCH }} path: deployment @@ -107,7 +107,7 @@ jobs: - name: Update deployment status to Success if: ${{ success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: ${{ env._ENVIRONMENT_URL }} @@ -116,7 +116,7 @@ jobs: - name: Update deployment status to Failure if: ${{ failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: ${{ env._ENVIRONMENT_URL }} diff --git a/.github/workflows/enforce-labels.yml b/.github/workflows/enforce-labels.yml index 6528c94f1f8..73092bb2e0d 100644 --- a/.github/workflows/enforce-labels.yml +++ b/.github/workflows/enforce-labels.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Enforce Label - uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb # v2.1.0 + uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2 with: BANNED_LABELS: "hold,needs-qa" BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged" diff --git a/.github/workflows/label-issue-pull-request.yml b/.github/workflows/label-issue-pull-request.yml index 9975bd6ef2e..a83e9e569b4 100644 --- a/.github/workflows/label-issue-pull-request.yml +++ b/.github/workflows/label-issue-pull-request.yml @@ -5,7 +5,8 @@ name: Label Issue Pull Request on: pull_request: - types: [opened, edited] # Check when PR is opened or target branch is edited + types: + - opened # Check when PR is opened paths-ignore: - .github/workflows/** # We don't need QA on workflow changes branches: @@ -16,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Add label to pull request - uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 + uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4 if: ${{ !github.event.pull_request.head.repo.fork }} with: add-labels: "needs-qa" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6d0314a0524..2cc004987a1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Lint filenames (no capital characters) run: | @@ -39,7 +39,7 @@ jobs: diff <(sort .github/whitelist-capital-letters.txt) <(sort tmp.txt) - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index ae22876b4c8..4a65b0d412c 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -27,7 +27,7 @@ jobs: release-version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -41,7 +41,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6 + uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -56,7 +56,7 @@ jobs: needs: setup steps: - name: Checkout repo - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Testing locales - extName length run: | @@ -92,7 +92,7 @@ jobs: - locales-test steps: - name: Create GitHub deployment - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -103,7 +103,7 @@ jobs: - name: Download latest Release build artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-browser.yml workflow_conclusion: success @@ -116,7 +116,7 @@ jobs: - name: Dry Run - Download latest master build artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-browser.yml workflow_conclusion: success @@ -139,7 +139,7 @@ jobs: - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@40bb172bd05f266cf9ba4ff965cb61e9ee5f6d01 + uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 with: artifacts: 'browser-source-${{ needs.setup.outputs.release-version }}.zip, dist-chrome-${{ needs.setup.outputs.release-version }}.zip, @@ -155,7 +155,7 @@ jobs: - name: Update deployment status to Success if: ${{ success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'success' @@ -163,7 +163,7 @@ jobs: - name: Update deployment status to Failure if: ${{ failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'failure' diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 603459876bd..5901d81740b 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -43,7 +43,7 @@ jobs: release-version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -57,7 +57,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6 + uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -67,7 +67,7 @@ jobs: - name: Create GitHub deployment if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -78,7 +78,7 @@ jobs: - name: Download all Release artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli @@ -87,7 +87,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli @@ -96,7 +96,7 @@ jobs: - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37 # v1.10.0 + uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 env: PKG_VERSION: ${{ steps.version.outputs.version }} with: @@ -118,7 +118,7 @@ jobs: - name: Update deployment status to Success if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'success' @@ -126,7 +126,7 @@ jobs: - name: Update deployment status to Failure if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'failure' @@ -141,16 +141,16 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "snapcraft-store-token" @@ -162,7 +162,7 @@ jobs: - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli @@ -172,7 +172,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli @@ -195,16 +195,16 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@24848bc889cfc0a8313c2b3e378ac0d625b9bc16 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "cli-choco-api-key" @@ -220,7 +220,7 @@ jobs: - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli/dist @@ -230,7 +230,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli/dist @@ -254,23 +254,23 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@24848bc889cfc0a8313c2b3e378ac0d625b9bc16 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "npm-api-key" - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli/build @@ -280,7 +280,7 @@ jobs: - name: Dry Run - Download artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-cli.yml path: apps/cli/build diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index 614b84cdc85..74d02aefc79 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -23,7 +23,7 @@ jobs: build_number: ${{ steps.increment-version.outputs.build_number }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Branch check run: | @@ -47,7 +47,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6 + uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: release-type: 'Initial Release' project-type: ts @@ -115,12 +115,12 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ needs.setup.outputs.branch-name }} - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -157,42 +157,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release-channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-linux.yml @@ -211,12 +211,12 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ needs.setup.outputs.branch-name }} - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -231,7 +231,7 @@ jobs: node-gyp install $(node -v) - name: Install AST - uses: bitwarden/gh-actions/install-ast@f135c42c8596cb535c5bcb7523c0b2eef89709ac + uses: bitwarden/gh-actions/install-ast@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b - name: Set up environment run: choco install checksum --no-progress @@ -243,13 +243,13 @@ jobs: choco --version - name: Login to Azure - uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "code-signing-vault-url, @@ -304,91 +304,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release-channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release-channel }}.yml @@ -406,12 +406,12 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ needs.setup.outputs.branch-name }} - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -434,14 +434,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -535,12 +535,12 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ needs.setup.outputs.branch-name }} - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -563,14 +563,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -653,7 +653,7 @@ jobs: - name: Download artifact from hotfix-rc if: github.ref == 'refs/heads/hotfix-rc' - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -662,7 +662,7 @@ jobs: - name: Download artifact from rc if: github.ref == 'refs/heads/rc' - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -671,7 +671,7 @@ jobs: - name: Download artifact from master if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }} - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -696,28 +696,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: ${{ needs.setup.outputs.release-channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-mac.yml @@ -737,12 +737,12 @@ jobs: working-directory: apps/desktop steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ needs.setup.outputs.branch-name }} - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -765,14 +765,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed + uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -855,7 +855,7 @@ jobs: - name: Download artifact from hotfix-rc if: github.ref == 'refs/heads/hotfix-rc' - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -864,7 +864,7 @@ jobs: - name: Download artifact from rc if: github.ref == 'refs/heads/rc' - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -873,7 +873,7 @@ jobs: - name: Download artifact from master if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }} - uses: dawidd6/action-download-artifact@b2abf1705491048a2d7074f7d90513044fd25d39 # v2.19.0 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build-browser.yml workflow_conclusion: success @@ -898,7 +898,7 @@ jobs: APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - name: Upload .pkg artifact - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -916,7 +916,7 @@ jobs: - macos-package-mas steps: - name: Create GitHub deployment - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -926,13 +926,13 @@ jobs: task: release - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, @@ -944,7 +944,7 @@ jobs: cf-prod-account" - name: Download all artifacts - uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 + uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: apps/desktop/artifacts @@ -983,7 +983,7 @@ jobs: - name: Update deployment status to Success if: ${{ success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'success' @@ -991,7 +991,7 @@ jobs: - name: Update deployment status to Failure if: ${{ failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'failure' @@ -1011,7 +1011,7 @@ jobs: - release steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Setup git config run: | diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index b4fa338897d..8eaf148be98 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -53,7 +53,7 @@ jobs: release-channel: ${{ steps.release-channel.outputs.channel }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -67,7 +67,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@ea9fab01d76940267b4147cc1c4542431246b9f6 + uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -94,7 +94,7 @@ jobs: - name: Create GitHub deployment if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -104,13 +104,13 @@ jobs: task: release - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, @@ -123,7 +123,7 @@ jobs: - name: Download all artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success @@ -132,7 +132,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success @@ -185,13 +185,13 @@ jobs: --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com - name: Get checksum files - uses: bitwarden/gh-actions/get-checksum@8b6a560c8ad3b9dab81659ae6cd1e319d8771a91 + uses: bitwarden/gh-actions/get-checksum@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: packages_dir: "apps/desktop/artifacts" file_path: "apps/desktop/artifacts/sha256-checksums.txt" - name: Create Release - uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 + uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && github.event.inputs.github_release }} env: PKG_VERSION: ${{ steps.version.outputs.version }} @@ -231,7 +231,7 @@ jobs: - name: Update deployment status to Success if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'success' @@ -239,7 +239,7 @@ jobs: - name: Update deployment status to Failure if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' state: 'failure' @@ -254,22 +254,22 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout Repo - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "snapcraft-store-token" - name: Install Snap - uses: samuelmeuli/action-snapcraft@10d7d0a84d9d86098b19f872257df314b0bd8e2d # v1.2.0 + uses: samuelmeuli/action-snapcraft@d33c176a9b784876d966f80fb1b461808edc0641 # v2.1.1 with: snapcraft_token: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} @@ -279,7 +279,7 @@ jobs: - name: Download Snap artifact if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success @@ -289,7 +289,7 @@ jobs: - name: Dry Run - Download Snap artifact if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success @@ -313,7 +313,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release-version }} steps: - name: Checkout Repo - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Print Environment run: | @@ -321,13 +321,13 @@ jobs: dotnet nuget --version - name: Login to Azure - uses: Azure/login@24848bc889cfc0a8313c2b3e378ac0d625b9bc16 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "cli-choco-api-key" @@ -345,7 +345,7 @@ jobs: - name: Download choco artifact if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success @@ -355,7 +355,7 @@ jobs: - name: Dry Run - Download choco artifact if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-desktop.yml workflow_conclusion: success diff --git a/.github/workflows/release-qa-web.yml b/.github/workflows/release-qa-web.yml index dea34a2e6f0..b50e48753c4 100644 --- a/.github/workflows/release-qa-web.yml +++ b/.github/workflows/release-qa-web.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Create GitHub deployment - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -20,10 +20,10 @@ jobs: description: 'Deployment from branch ${{ github.ref_name }}' - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Download latest cloud asset - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml path: apps/web @@ -36,7 +36,7 @@ jobs: run: unzip web-*-cloud-QA.zip - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: cf-pages-qa path: deployment @@ -67,7 +67,7 @@ jobs: - name: Update deployment status to Success if: ${{ success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: http://vault.qa.bitwarden.pw @@ -76,7 +76,7 @@ jobs: - name: Update deployment status to Failure if: ${{ failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: http://vault.qa.bitwarden.pw diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 223198c8053..e5aea6b3778 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -24,7 +24,7 @@ jobs: tag_version: ${{ steps.version.outputs.tag }} steps: - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Branch check if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -38,7 +38,7 @@ jobs: - name: Check Release Version id: version - uses: bitwarden/gh-actions/release-version-check@8f055ef543c7433c967a1b9b04a0f230923233bb + uses: bitwarden/gh-actions/release-version-check@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: release-type: ${{ github.event.inputs.release_type }} project-type: ts @@ -65,12 +65,12 @@ jobs: echo "Github Release Option: $_RELEASE_OPTION" - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 ########## DockerHub ########## - name: Setup DCT id: setup-dct - uses: bitwarden/gh-actions/setup-docker-trust@a8c384a05a974c05c48374c818b004be221d43ff + uses: bitwarden/gh-actions/setup-docker-trust@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} azure-keyvault-name: "bitwarden-ci" @@ -105,7 +105,7 @@ jobs: ########## ACR ########## - name: Login to Azure - PROD Subscription - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 # v1.1 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }} @@ -139,84 +139,97 @@ jobs: run: docker logout - cfpages-deploy: - name: Deploy Web Vault to CloudFlare Pages branch + ghpages-deploy: + name: Deploy to GitHub Pages runs-on: ubuntu-20.04 needs: - setup - - self-host env: _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} - _TAG_VERSION: ${{ needs.setup.outputs.release_version }} + _TAG_VERSION: ${{ needs.setup.outputs.tag_version }} + _BRANCH: "v${{ needs.setup.outputs.release_version }}-deploy" steps: - - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 + - name: Login to Azure - CI Subscription + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} + + - name: Retrieve bot secrets + id: retrieve-bot-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b + with: + keyvault: bitwarden-ci + secrets: "github-pat-bitwarden-devops-bot-repo-scope" + + - name: Checkout GH pages repo + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + with: + repository: bitwarden/web-vault-pages + path: ghpages-deployment + token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} - name: Download latest cloud asset if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml - path: apps/web + path: assets workflow_conclusion: success branch: ${{ github.ref_name }} artifacts: web-*-cloud-COMMERCIAL.zip - name: Dry Run - Download latest cloud asset if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml - path: apps/web + path: assets workflow_conclusion: success branch: master artifacts: web-*-cloud-COMMERCIAL.zip - name: Unzip build asset - working-directory: apps/web + working-directory: assets run: unzip web-*-cloud-COMMERCIAL.zip - - - name: Checkout Repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v3.0.2 - with: - ref: cf-pages - path: deployment - - - name: Setup git config + + - name: Create new branch run: | - git config --global user.name = "GitHub Action Bot" - git config --global user.email = "<>" + cd ${{ github.workspace }}/ghpages-deployment + git config user.name = "GitHub Action Bot" + git config user.email = "<>" git config --global url."https://github.com/".insteadOf ssh://git@github.com/ git config --global url."https://".insteadOf ssh:// - - - name: Deploy CloudFlare Pages + git checkout -b ${_BRANCH} + + - name: Copy build files run: | - rm -rf ./* - cp -R ../apps/web/build/* . - working-directory: deployment - - - name: Create cf-pages-deploy branch + rm -rf ${{ github.workspace }}/ghpages-deployment/* + cp -Rf ${{ github.workspace }}/assets/build/* ghpages-deployment/ + + - name: Commit and push changes + working-directory: ghpages-deployment run: | - git switch -c cf-pages-deploy-$_TAG_VERSION git add . - git commit -m "Staging deploy ${{ needs.setup.outputs.release_version }}" + git commit -m "Deploy Web v${_RELEASE_VERSION} to GitHub Pages" + git push --set-upstream origin ${_BRANCH} --force - if [[ "${{ github.event.inputs.release_type }}" != "Dry Run" ]]; then - git push -u origin cf-pages-deploy-$_TAG_VERSION - fi - working-directory: deployment - - - name: Create CloudFlare Pages Deploy PR - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + - name: Create GitHub Pages Deploy PR + working-directory: ghpages-deployment env: - PR_BRANCH: cf-pages-deploy-${{ env._TAG_VERSION }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }} run: | - gh pr create --title "Deploy $_RELEASE_VERSION to CloudFlare Pages" \ - --body "Deploying $_RELEASE_VERSION" \ - --base cf-pages \ - --head "$PR_BRANCH" - + if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then + gh pr create --title "Deploy v${_RELEASE_VERSION} to GitHub Pages" \ + --draft \ + --body "Deploying v${_RELEASE_VERSION}" \ + --base master \ + --head "${_BRANCH}" + else + gh pr create --title "Deploy v${_RELEASE_VERSION} to GitHub Pages" \ + --body "Deploying v${_RELEASE_VERSION}" \ + --base master \ + --head "${_BRANCH}" + fi release: name: Create GitHub Release @@ -224,11 +237,11 @@ jobs: needs: - setup - self-host - - cfpages-deploy + - ghpages-deploy steps: - name: Create GitHub deployment if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: chrnorm/deployment-action@1b599fe41a0ef1f95191e7f2eec4743f2d7dfc48 + uses: chrnorm/deployment-action@d42cde7132fcec920de534fffc3be83794335c00 # v2.0.5 id: deployment with: token: '${{ secrets.GITHUB_TOKEN }}' @@ -240,7 +253,7 @@ jobs: - name: Download latest build artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml path: apps/web/artifacts @@ -251,7 +264,7 @@ jobs: - name: Dry Run - Download latest build artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: bitwarden/gh-actions/download-artifacts@850faad0cf6c02a8c0dc46eddde2363fbd6c373a + uses: bitwarden/gh-actions/download-artifacts@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: workflow: build-web.yml path: apps/web/artifacts @@ -268,7 +281,7 @@ jobs: - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37 # v1.10.0 + uses: ncipollo/release-action@a2e71bdd4e7dab70ca26a852f29600c98b33153e # v1.12.0 with: name: "Web v${{ needs.setup.outputs.release_version }}" commit: ${{ github.sha }} @@ -281,7 +294,7 @@ jobs: - name: Update deployment status to Success if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: http://vault.bitwarden.com @@ -290,7 +303,7 @@ jobs: - name: Update deployment status to Failure if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@07b3930847f65e71c9c6802ff5a402f6dfb46b86 + uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 with: token: '${{ secrets.GITHUB_TOKEN }}' environment-url: http://vault.bitwarden.com diff --git a/.github/workflows/staged-rollout-desktop.yml b/.github/workflows/staged-rollout-desktop.yml index 04c0b8472fd..bd27e05cc69 100644 --- a/.github/workflows/staged-rollout-desktop.yml +++ b/.github/workflows/staged-rollout-desktop.yml @@ -20,13 +20,13 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Login to Azure - uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "aws-electron-access-id, diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index 5cd154cf283..98f3b9d1722 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: 'Run stale action' - uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 # v5.0.0 + uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 with: stale-issue-label: 'needs-reply' stale-pr-label: 'needs-changes' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15496b01e06..870b5a6d980 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,10 +25,10 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Set up Node - uses: actions/setup-node@9ced9a43a244f3ac94f13bfd896db8c8f30da67a # v3.0.0 + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' @@ -52,7 +52,7 @@ jobs: run: npm run test - name: Report test results - uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 + uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 if: always() with: name: Test Results @@ -79,10 +79,10 @@ jobs: sudo apt-get install -y gnome-keyring dbus-x11 - name: Checkout repo - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Install rust - uses: actions-rs/toolchain@88dc2356392166efad76775c878094f4e83ff746 + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af # v1.0.7 with: toolchain: stable profile: minimal diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 4274022f8f2..991b25eef94 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -18,7 +18,7 @@ jobs: version_number: ${{ steps.version.outputs.new-version }} steps: - name: Checkout Branch - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Calculate bumped version id: version diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 0727e927a98..752e4baba2c 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -43,19 +43,19 @@ jobs: uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Login to Azure - Prod Subscription - uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 + uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 with: creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} - name: Retrieve secrets id: retrieve-secrets - uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af + uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: keyvault: "bitwarden-ci" secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@c8bb57c57e8df1be8c73ff3d59deab1dbc00e0d1 # v5.2.0 + uses: crazy-max/ghaction-import-gpg@72b6676b71ab476b77e676928516f6982eef7a41 # v5.3.0 with: gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }} passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }} @@ -86,14 +86,14 @@ jobs: - name: Bump Browser Version - Manifest if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} - uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945 + uses: bitwarden/gh-actions/version-bump@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: version: ${{ github.event.inputs.version_number }} file_path: "apps/browser/src/manifest.json" - name: Bump Browser Version - Manifest v3 if: ${{ github.event.inputs.client == 'Browser' || github.event.inputs.client == 'All' }} - uses: bitwarden/gh-actions/version-bump@03ad9a873c39cdc95dd8d77dbbda67f84db43945 + uses: bitwarden/gh-actions/version-bump@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b with: version: ${{ github.event.inputs.version_number }} file_path: "apps/browser/src/manifest.v3.json" @@ -187,4 +187,4 @@ jobs: ## Objective Automated ${{ github.event.inputs.client }} version bump to ${{ github.event.inputs.version_number }}" - + diff --git a/.github/workflows/workflow-linter.yml b/.github/workflows/workflow-linter.yml index 9fda2eee0a1..39f2436b722 100644 --- a/.github/workflows/workflow-linter.yml +++ b/.github/workflows/workflow-linter.yml @@ -8,4 +8,4 @@ on: jobs: call-workflow: - uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@master + uses: bitwarden/gh-actions/.github/workflows/workflow-linter.yml@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b diff --git a/.prettierrc.json b/.prettierrc.json index de753c537d2..29ca392ce1b 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,11 @@ { - "printWidth": 100 + "printWidth": 100, + "overrides": [ + { + "files": "*.mdx", + "options": { + "proseWrap": "always" + } + } + ] } diff --git a/apps/browser/jest.config.js b/apps/browser/jest.config.js index 4f954afa9e2..cde02cd9959 100644 --- a/apps/browser/jest.config.js +++ b/apps/browser/jest.config.js @@ -2,8 +2,9 @@ const { pathsToModuleNameMapper } = require("ts-jest"); const { compilerOptions } = require("./tsconfig"); -const sharedConfig = require("../../libs/shared/jest.config.base"); +const sharedConfig = require("../../libs/shared/jest.config.angular"); +/** @type {import('jest').Config} */ module.exports = { ...sharedConfig, preset: "jest-preset-angular", diff --git a/apps/browser/package.json b/apps/browser/package.json index 0057704287d..b29ab9c27b3 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -6,6 +6,7 @@ "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", "build:watch": "webpack --watch", "build:watch:mv3": "cross-env MANIFEST_VERSION=3 webpack --watch", + "build:watch:autofill": "cross-env AUTOFILL_VERSION=2 webpack --watch", "build:prod": "cross-env NODE_ENV=production webpack", "build:prod:watch": "cross-env NODE_ENV=production webpack --watch", "dist": "npm run build:prod && gulp dist", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 5e95165a32f..71cd3d1a66b 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index fae3bb8e6ec..0c12f845d51 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Element avto-dolduruldu" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ana parolu tənzimlə" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Bölgə" + }, + "opensInANewWindow": { + "message": "Yeni bir pəncərədə açılır" + }, + "eu": { + "message": "AB", + "description": "European Union" + }, + "us": { + "message": "ABŞ", + "description": "United States" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 587584c2986..80800f4aa38 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -1461,17 +1461,35 @@ "autoFillSuccess": { "message": "Аўтазапоўнены элемент" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Прызначыць асноўны пароль" }, "currentMasterPass": { - "message": "Current master password" + "message": "Бягучы асноўны пароль" }, "newMasterPass": { - "message": "New master password" + "message": "Новы асноўны пароль" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Пацвердзіць новы асноўны пароль" }, "masterPasswordPolicyInEffect": { "message": "Адна або больш палітык арганізацыі патрабуе, каб ваш асноўны пароль адпавядаў наступным патрабаванням:" @@ -1854,7 +1872,7 @@ "message": "Ваш асноўны пароль нядаўна быў зменены адміністратарам арганізацыі. Для таго, каб атрымаць доступ да вашага сховішча, вы павінны абнавіць яго зараз. Гэта прывядзе да завяршэння бягучага сеанса і вам неабходна будзе ўвайсці паўторна. Сеансы на іншых прыладах могуць заставацца актыўнымі на працягу адной гадзіны." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Ваш асноўны пароль не адпавядае адной або некалькім палітыкам арганізацыі. Для атрымання доступу да сховішча, вы павінны абнавіць яго. Працягваючы, вы выйдзіце з бягучага сеанса і вам неабходна будзе ўвайсці паўторна. Актыўныя сеансы на іншых прыладах могуць заставацца актыўнымі на працягу адной гадзіны." }, "resetPasswordPolicyAutoEnroll": { "message": "Аўтаматычная рэгістрацыя" @@ -1888,7 +1906,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "Палітыка вашай арганізацыі ўплывае на час чакання сховішча. Максімальны дазволены час чакання сховішча складае гадзін: $HOURS$; хвілін: $MINUTES$. Для часу чакання вашага сховішча прызначана дзеянне $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -1905,7 +1923,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "Палітыкай вашай арганізацыі прызначана дзеянне $ACTION$ для часу чакання вашага сховішча.", "placeholders": { "action": { "content": "$1", @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Адкрываць у новым акне" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index f6c815de5af..b17692bc83b 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Автоматично дописан запис" }, + "insecurePageWarning": { + "message": "Внимание: това е незащитена уеб страница (HTTP) и всяка изпратена информация би могла да бъде видяна и променена от някой недоброжелател. Този елемент за вход първоначално е бил записан към страница със защитена връзка (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Искате ли да попълните данните за вход въпреки това?" + }, + "autofillIframeWarning": { + "message": "Формулярът се предоставя от различен домейн от онзи, който е във Вашия запис. Натиснете „Добре“, за да бъдат попълнени данните автоматично, или „Отказ“, за да прекратите операцията." + }, + "autofillIframeWarningTip": { + "message": "Ако искате повече да не виждате това предупреждение, запазете този адрес – $HOSTNAME$ – към записа за този уеб сайт.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Задаване на главна парола" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Регион" + }, + "opensInANewWindow": { + "message": "Отваря се в нов прозорец" + }, + "eu": { + "message": "ЕС", + "description": "European Union" + }, + "us": { + "message": "САЩ", + "description": "United States" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 418a0821c65..76143e7619a 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "স্বতঃপূরণকৃত বস্তু" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "মূল পাসওয়ার্ড ধার্য করুন" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index c9ae2e1660c..b3047a98789 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 11952dde012..e117d21790c 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Element emplenat automàticament " }, + "insecurePageWarning": { + "message": "Avís: aquesta és una pàgina HTTP no segura i altres persones poden veure i canviar qualsevol informació que envieu. Aquest inici de sessió es va guardar originalment en una pàgina segura (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Encara voleu omplir aquest inici de sessió?" + }, + "autofillIframeWarning": { + "message": "El formulari està allotjat en un domini diferent de l'URI de l'inici de sessió guardat. Trieu D'acord per omplir -lo automàticament de totes maneres o Cancel·la per aturar-ho." + }, + "autofillIframeWarningTip": { + "message": "Per evitar aquest avís en el futur, guardeu aquest URI, $HOSTNAME$, al vostre element d'inici de sessió de Bitwarden d'aquest lloc.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Estableix la contrasenya mestra" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "S'obri en una finestra nova" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index f99484f1029..d83282e08a4 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Položka byla automaticky vyplněna " }, + "insecurePageWarning": { + "message": "Varování: Toto je nezabezpečená stránka HTTP a všechny informace, které odešlete, mohou být případně viditelné a změněny ostatními. Toto přihlášení bylo původně uloženo na zabezpečené (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Chcete přesto vyplnit toto přihlášení?" + }, + "autofillIframeWarning": { + "message": "Formulář je hostován jinou doménou než URI uloženého přihlášení. Zvolte OK pro automatické vyplnění nebo Zrušit pro zrušení." + }, + "autofillIframeWarningTip": { + "message": "Aby se zabránilo tomuto varování v budoucnu, uložte tuto URI: $HOSTNAME$ do vaší přihlašovací položky Bitwarden pro tuto stránku.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Nastavit hlavní heslo" }, @@ -1959,10 +1977,10 @@ "message": "Vypršel časový limit relace. Vraťte se zpět a zkuste se znovu přihlásit." }, "exportingPersonalVaultTitle": { - "message": "Exportování individuálního trezoru" + "message": "Exportování osobního trezoru" }, "exportingPersonalVaultDescription": { - "message": "Budou exportovány pouze položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", + "message": "Budou exportovány jen osobní položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", "placeholders": { "email": { "content": "$1", @@ -2135,7 +2153,7 @@ "message": "Slabé a odhalené hlavní heslo" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Slabeé heslo bylo nalezeno mezi odhalenými hesly. K zabezpečení Vašeho účtu používejte silné a jedinečné heslo. Opravdu chcete používat toto heslo?" + "message": "Slabé heslo bylo nalezeno mezi odhalenými hesly. K zabezpečení Vašeho účtu používejte silné a jedinečné heslo. Opravdu chcete používat toto heslo?" }, "checkForBreaches": { "message": "Zkontrolovat heslo, zda nebylo odhaleno" @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Otevře se v novém okně" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index e3892253e73..5aebf5dd80d 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Autoudfyldte element" }, + "insecurePageWarning": { + "message": "Advarsel: Dette er en ikke-sikret HTTP side, og alle indsendte oplysninger kan potentielt ses og ændres af andre. Dette login blev oprindeligt gemt på en sikker (HTTPS) side." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "Formularen hostes af et andet domæne end URI'en for det gemte login. Vælg OK for at autoudfylde alligevel, eller Afbryd for at stoppe." + }, + "autofillIframeWarningTip": { + "message": "For at forhindre denne advarsel i fremtiden, så gem denne URI, $HOSTNAME$, til Bitwarden login-emnet for dette websted.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Indstil hovedadgangskode" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Åbnes i et nyt vindue" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "USA", + "description": "United States" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 2857d76d401..c7827d4ddc4 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -634,7 +634,7 @@ "message": "Kontextmenüoptionen anzeigen" }, "contextMenuItemDesc": { - "message": "Verwende einen zweiten Klick, um auf die Erstellung von Passwörtern und passende Zugangsdaten für die Webseite zuzugreifen. " + "message": "Greife über einen Rechtsklick auf die Erstellung von Passwörtern und passende Zugangsdaten für die Website zu." }, "defaultUriMatchDetection": { "message": "Standard URI-Übereinstimmungserkennung", @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Eintrag automatisch ausgefüllt " }, + "insecurePageWarning": { + "message": "Warnung: Dies ist eine ungesicherte HTTP-Seite und alle Informationen, die du absendest, können möglicherweise von anderen gesehen und geändert werden. Diese Zugangsdaten wurden ursprünglich auf einer sicheren (HTTPS-)Seite gespeichert." + }, + "insecurePageWarningFillPrompt": { + "message": "Möchtest du diese Zugangsdaten trotzdem ausfüllen?" + }, + "autofillIframeWarning": { + "message": "Das Formular wird von einer anderen Domain als der URI deines gespeicherten Logins gehostet. Wähle OK, um trotzdem automatisch auszufüllen, oder Abbrechen, um aufzuhören." + }, + "autofillIframeWarningTip": { + "message": "Um diese Warnung in Zukunft zu verhindern, speichere diese URI, $HOSTNAME$, in deinem Bitwarden Zugangsdaten-Eintrag für diese Seite.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Master-Passwort festlegen" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Wird in einem neuen Fenster geöffnet" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 41a380da6ab..e6da9f04c8d 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Αυτόματη συμπλήρωση αντικειμένου" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ορισμός Κύριου Κωδικού" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Ανοίγει σε νέο παράθυρο" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 1b8c6315bee..69d26333a84 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2203,7 +2221,18 @@ } } }, + "region": { + "message": "Region" + }, "opensInANewWindow": { "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 35f4bef23f1..653ff32074c 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index ec9105d30b4..3fe6222cfcf 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Auto-filled item" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 5ab38e853b3..afd3ea4ce87 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Objeto autorellenado" }, + "insecurePageWarning": { + "message": "Atención: Esta es una página HTTP no segura, y cualquier información que envíes puede ser vista y cambiada por otros. Este inicio de sesión fue guardado originalmente en una página segura (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "¿Sigue deseando rellenar este inicio de sesión?" + }, + "autofillIframeWarning": { + "message": "El formulario está alojado por un dominio diferente al URI de su registro guardado. Elija OK para autorrellenar de todos modos, o Cancelar para parar." + }, + "autofillIframeWarningTip": { + "message": "Para prevenir esta advertencia en el futuro, guarde esta URI, $HOSTNAME$, en su elemento de inicio de sesión de Bitwarden para este sitio.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Establecer contraseña maestra" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Región" + }, + "opensInANewWindow": { + "message": "Abre en una nueva ventana" + }, + "eu": { + "message": "Unión Europea", + "description": "European Union" + }, + "us": { + "message": "EE.UU.", + "description": "United States" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 4b235b26b7d..02ee8d04384 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -1461,17 +1461,35 @@ "autoFillSuccess": { "message": "Kirje täideti" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Määra ülemparool" }, "currentMasterPass": { - "message": "Current master password" + "message": "Praegune ülemparool" }, "newMasterPass": { - "message": "New master password" + "message": "Uus ülemparool" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Kinnita uus ülemparool" }, "masterPasswordPolicyInEffect": { "message": "Üks või enam organisatsiooni eeskirja nõuavad, et ülemparool vastaks nendele nõudmistele:" @@ -1854,7 +1872,7 @@ "message": "Organisatsiooni administraator muutis hiljuti sinu ülemparooli. Hoidlale ligi pääsemiseks pead seda nüüd uuendama. Jätkates logitakse sind käimasolevast sessioonist välja, misjärel nõutakse uuesti sisselogimist. Teistes seadmetes olevad aktiivsed sessioonid jäävad aktiivseks kuni üheks tunniks." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "Sinu ülemparool ei vasta ühele või rohkemale organisatsiooni poolt seatud poliitikale. Hoidlale ligipääsemiseks pead oma ülemaprooli uuendama. Jätkamisel logitakse sind praegusest sessioonist välja, mistõttu pead uuesti sisse logima. Teistes seadmetes olevad aktiivsed sessioonid aeguvad umbes ühe tunni jooksul." }, "resetPasswordPolicyAutoEnroll": { "message": "Automaatne liitumine" @@ -1888,7 +1906,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "Organisatsiooni poliitikad mõjutavad sinu hoidla ajalõppu. Maksimaalne lubatud hoidla ajalõpp on $HOURS$ tund(i) ja $MINUTES$ minut(it). Sinu hoidla ajalõpu tegevus on $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -1905,7 +1923,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "Organisatsiooni poliitika on sinu hoidla ajalõpu tegevuse seadistanud $ACTION$ peale.", "placeholders": { "action": { "content": "$1", @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Piirkond" + }, + "opensInANewWindow": { + "message": "Avaneb uues aknas" + }, + "eu": { + "message": "EL", + "description": "European Union" + }, + "us": { + "message": "USA", + "description": "United States" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 7cb87c9939c..e2189ce6a68 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Elementua auto-beteta" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ezarri pasahitz nagusia" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 4e594b0e02a..0e4a59e164b 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -1461,17 +1461,35 @@ "autoFillSuccess": { "message": "مورد خودکار پر شد" }, + "insecurePageWarning": { + "message": "هشدار: این یک صفحه HTTP ناامن است و هر اطلاعاتی که ارسال می‌کنید می‌تواند توسط دیگران دیده شود و تغییر کند. این ورود در ابتدا در یک صفحه امن (HTTPS) ذخیره شد." + }, + "insecurePageWarningFillPrompt": { + "message": "آیا هنوز می‌خواهید این ورود را پر کنید؟" + }, + "autofillIframeWarning": { + "message": "فرم توسط دامنه ای متفاوت از نشانی اینترنتی ورود به سیستم ذخیره شده شما میزبانی می‌شود. به هر حال برای پر کردن خودکار، تأیید را انتخاب کنید یا برای توقف، لغو را انتخاب کنید." + }, + "autofillIframeWarningTip": { + "message": "برای جلوگیری از این هشدار در آینده، این نشانی اینترنتی، $HOSTNAME$، را در مورد ورود Bitwarden خود برای این سایت ذخیره کنید.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "تنظیم کلمه عبور اصلی" }, "currentMasterPass": { - "message": "Current master password" + "message": "کلمه عبور اصلی فعلی" }, "newMasterPass": { - "message": "New master password" + "message": "کلمه عبور اصلی جدید" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "تأیید کلمه عبور اصلی جدید" }, "masterPasswordPolicyInEffect": { "message": "یک یا چند سیاست سازمانی برای تأمین شرایط زیر به کلمه عبور اصلی شما احتیاج دارد:" @@ -1854,7 +1872,7 @@ "message": "کلمه عبور اصلی شما اخیراً توسط سرپرست سازمان‌تان تغییر کرده است. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به‌روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می‌شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "کلمه عبور اصلی شما با یک یا چند سیاست سازمان‌تان مطابقت ندارد. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به‌روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می‌شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." }, "resetPasswordPolicyAutoEnroll": { "message": "ثبت نام خودکار" @@ -1888,7 +1906,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "سیاست‌های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می‌گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است. عملگر مهلت زمانی گاوصندوق شما روی $ACTION$ تنظیم شده است.", "placeholders": { "hours": { "content": "$1", @@ -1905,7 +1923,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "سباست‌های سازمان شما، عملگر زمان‌بندی گاوصندوق شما را روی $ACTION$ تنظیم کرده است.", "placeholders": { "action": { "content": "$1", @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "در پنجره جدید باز می‌شود" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 8e67983d06c..69b77b26123 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Kohde täytettiin automaattisesti" }, + "insecurePageWarning": { + "message": "Varoitus: Tämä on suojaamaton HTTP-sivu, eli ulkopuolisten tahojen voi olla mahdollista tarkastella ja muuttaa lähettämiäsi tietoja. Tämä kirjautumistieto on alun perin tallennettu suojatulle HTTPS-sivulle." + }, + "insecurePageWarningFillPrompt": { + "message": "Haluatko silti täyttää kirjautumistiedot?" + }, + "autofillIframeWarning": { + "message": "Lomakkeen URI-osoite poikkeaa kirjautumistietoon tallennetusta osoitteesta. Täytä se siitä huolimatta valitsemalla OK tai peru täyttö valitsemalla Peruuta." + }, + "autofillIframeWarningTip": { + "message": "Välttyäksesi varoitukselta jatkossa, tallenna URI $HOSTNAME$ sivustolle tallennettuun Bitwarden-kirjautumistietoosi.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Aseta pääsalasana" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Alue" + }, + "opensInANewWindow": { + "message": "Avautuu uudessa ikkunassa" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index dcc1a716201..e98884c2007 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item na auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Itakda ang master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 7387c5c055d..8f4e769c695 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Élément saisi automatiquement" }, + "insecurePageWarning": { + "message": "Avertissement : il s'agit d'une page HTTP non sécurisée, et toute information que vous soumettez peut potentiellement être vue et modifiée par d'autres. À l'origine, cet identifiant a été enregistré sur une page sécurisée (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Voulez-vous toujours saisir automatiquement cet identifiant ?" + }, + "autofillIframeWarning": { + "message": "Le formulaire est hébergé par un domaine différent de l'URI d'enregistrement de votre identifiant. Choisissez OK pour poursuivre la saisie automatique, ou Annuler pour arrêter." + }, + "autofillIframeWarningTip": { + "message": "À l'avenir, pour éviter cet avertissement, enregistrez cet URI, $HOSTNAME$, dans votre élément de connexion Bitwarden pour ce site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Définir le mot de passe principal" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Région" + }, + "opensInANewWindow": { + "message": "S'ouvre dans une nouvelle fenêtre" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 6ee364e0f2f..fc90bade6c0 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "בוצעה השלמה אוטומטית" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "הגדר סיסמה ראשית" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 3d63f1ce8e8..df561ad0e47 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "ऑटो फिल आइटम" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "मास्टर पासवर्ड सेट करें" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index a85bc97aba9..37ecb3de8c8 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Auto-ispunjena stavka" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Postavi glavnu lozinku" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 2a211171317..c5e8214b635 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Automatikusan kitöltött elem" }, + "insecurePageWarning": { + "message": "Figyelmeztetés: Ez egy nem biztonságos HTTP oldal és az elküldött információkat mások láthatják és módosíthatják. Ezt a bejelentkezést eredetileg egy biztonságos (HTTPS) oldalra mentették." + }, + "insecurePageWarningFillPrompt": { + "message": "Még mindig ki szeretnénk tölteni ezt a bejelentkezést?" + }, + "autofillIframeWarning": { + "message": "Az űrlapot egy másik domain tárolja, mint a mentett bejelentkezés URI-ja. Az automatikus kitöltéshez válasszuk az OK gombot, a leállításhoz pedig a Mégsem lehetőséget." + }, + "autofillIframeWarningTip": { + "message": "Ennek a figyelmeztetésnek a jövőbeni elkerülése érdekében mentsük el ezt az URI-t - $HOSTNAME$ - a Bitwarden bejelentkezési elemébe ennél a webhelynél.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Mesterjelszó beállítása" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Régió" + }, + "opensInANewWindow": { + "message": "Megnyitás új ablakban" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 147a80e801f..6addc264fb2 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item Terisi Otomatis" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Atur Kata Sandi Utama" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 87d434d7b4d..1971331fd98 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Elemento riempito automaticamente " }, + "insecurePageWarning": { + "message": "Attenzione: questa è una pagina HTTP non protetta, e tutte le informazioni che invii potrebbero essere viste e modificate da altri. Questo login è stato originariamente salvato su una pagina sicura (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Vuoi comunque riempire automaticamente questo login?" + }, + "autofillIframeWarning": { + "message": "Il modulo è ospitato da un dominio diverso dall'URI del tuo login salvato. Clicca Ok per riempire automaticamente comunque, o Annulla per interrompere." + }, + "autofillIframeWarningTip": { + "message": "Per evitare questo avviso in futuro, salva questo URI, $HOSTNAME$, nel tuo elemento di login Bitwarden per questo sito.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Imposta password principale" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Regione" + }, + "opensInANewWindow": { + "message": "Si apre in una nuova finestra" + }, + "eu": { + "message": "UE", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index a70a3a151c1..630dfb3e217 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "アイテムを自動入力しました" }, + "insecurePageWarning": { + "message": "警告: これはセキュリティ保護されていない HTTP ページであり、送信する情報は他の人によって見られ、変更される可能性があります。 このログイン情報はもともとセキュア (HTTPS) ページに保存されていました。" + }, + "insecurePageWarningFillPrompt": { + "message": "このログイン情報を入力しますか?" + }, + "autofillIframeWarning": { + "message": "フォームは保存したログイン情報の URI とは異なるドメインによってホストされています。無視して自動入力するなら OK を選択し、中止したければキャンセルを選択してください。" + }, + "autofillIframeWarningTip": { + "message": "この警告を将来的に防ぐためには、この URI と $HOSTNAME$ をこのサイトの Bitwarden ログインアイテムに保存してください。", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "マスターパスワードを設定" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "リージョン" + }, + "opensInANewWindow": { + "message": "新しいウィンドウで開く" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "米国", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index cb647f18dc3..2f484324f39 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 91d52d757dd..baa61fad177 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "ಸ್ವಯಂ ತುಂಬಿದ ಐಟಂ" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಹೊಂದಿಸಿ" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index e206ba19641..962fe7347cf 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "항목을 자동 완성함" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "마스터 비밀번호 설정" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index fe5ab8db0c9..e6a9dbdc5c6 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Pagrindinio slaptažodžio nustatymas" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index c697a15f97b..38c892aaede 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Automātiski aizpildīts vienums" }, + "insecurePageWarning": { + "message": "Brīdinājums: šī ir nedroša HTTP lapa, un ir iespējams, ka citi var redzēt vai mainīt visu tajā iesniegto informāciju. Šis pieteikšanās vienums sākotnēji tika saglabāts drošā (HTTPS) lapā." + }, + "insecurePageWarningFillPrompt": { + "message": "Vai tiešām joprojām aizpildīt šo pieteikšanās vienumu?" + }, + "autofillIframeWarning": { + "message": "Veidne ir izvietota citā domēnā, nekā saglabātā pieteikšanās vienuma URI. Jāizvēlas \"Labi\", lai vienalga automātiski aizpildītu, vai \"Atcelt\", lai apturētu." + }, + "autofillIframeWarningTip": { + "message": "Lai novērstu šī brīdinājuma turpmāku rādīšanu, jāsaglabā šis URI, $HOSTNAME$, šīs vietnes Bitwarde pieteikšanās vienumā.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Uzstādīt galveno paroli" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Apgabals" + }, + "opensInANewWindow": { + "message": "Atver jaunā logā" + }, + "eu": { + "message": "ES", + "description": "European Union" + }, + "us": { + "message": "ASV", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index b07fb3d8254..4c4e9936e0f 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "യാന്ത്രികമായി പൂരിപ്പിച്ച ഇനം" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "പ്രാഥമിക പാസ്‌വേഡ് സജ്ജമാക്കുക" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 2e6e149d13f..8ede2fb9389 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Autoutfylt element" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Angi hovedpassord" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index fb19d76339b..cf3cae05d40 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Automatisch gevuld item" }, + "insecurePageWarning": { + "message": "Waarschuwing: Dit is een onbeveiligde HTTP-pagina waardoor anderen alle informatie die je verstuurt kunnen zien en wijzigen. Deze login is oorspronkelijk opgeslagen op een beveiligde (HTTPS) pagina." + }, + "insecurePageWarningFillPrompt": { + "message": "Wil je je inloggegevens nog steeds invullen?" + }, + "autofillIframeWarning": { + "message": "Dit formulier wordt door een ander domein gehost dan de URI van jouw opgeslagen login. Kies OK voor toch automatisch invullen, of Annuleren om te stoppen." + }, + "autofillIframeWarningTip": { + "message": "Om deze waarschuwing in de toekomst te voorkomen, bewaar je deze URI, $HOSTNAME$, bij je Bitwarden-login voor deze site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Hoofdwachtwoord instellen" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Regio" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 5602194d754..ca32eb73aa6 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Element został automatycznie uzupełniony" }, + "insecurePageWarning": { + "message": "Ostrzeżenie: Jest to niezabezpieczona strona HTTP i wszelkie przekazane informacje mogą być potencjalnie widoczne i zmienione przez innych. Ten login został pierwotnie zapisany na stronie bezpiecznej (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Nadal chcesz uzupełnić ten login?" + }, + "autofillIframeWarning": { + "message": "Formularz jest hostowany przez inną domenę niż zapisany adres URI dla tego loginu. Wybierz OK, aby i tak automatycznie wypełnić lub anuluj aby zatrzymać." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ustaw hasło główne" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Otwiera w nowym oknie" + }, + "eu": { + "message": "UE", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index df3281a5959..87585d561d0 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item Auto-Preenchido" }, + "insecurePageWarning": { + "message": "Aviso: Esta é uma página HTTP não segura, e qualquer informação que você enviar poderá ser interceptada e modificada por outras pessoas. Este login foi originalmente salvo em uma página segura (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Você ainda deseja preencher esse login?" + }, + "autofillIframeWarning": { + "message": "O formulário está hospedado em um domínio diferente do URI do seu login salvo. Escolha OK para preencher automaticamente mesmo assim ou Cancelar para parar." + }, + "autofillIframeWarningTip": { + "message": "Para evitar este aviso no futuro, salve este URI, $HOSTNAME$, no seu item de login no Bitwarden para este site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Definir Senha Mestra" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index ae5649477cb..99503282049 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -56,7 +56,7 @@ "message": "Cofre" }, "myVault": { - "message": "O meu Cofre" + "message": "O meu cofre" }, "allVaults": { "message": "Todos os Cofres" @@ -128,7 +128,7 @@ "message": "Continuar" }, "sendVerificationCode": { - "message": "Envie um código de verificação para o seu e-mail" + "message": "Enviar um código de verificação para o seu e-mail" }, "sendCode": { "message": "Enviar o código" @@ -1345,7 +1345,7 @@ "message": "Tem a certeza de que pretende utilizar a opção \"Nunca\"? Definir as suas opções de bloqueio para \"Nunca\" armazena a chave de encriptação do seu cofre no seu dispositivo. Se utilizar esta opção deve assegurar-se de que mantém o seu dispositivo devidamente protegido." }, "noOrganizationsList": { - "message": "Você não pertence a nenhuma organização. Organizações permitem-lhe partilhar itens em segurança com outros utilizadores." + "message": "Não pertence a nenhuma organização. As organizações permitem-lhe partilhar itens em segurança com outros utilizadores." }, "noCollectionsInList": { "message": "Não existem coleções para listar." @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-preenchido" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Definir palavra-passe mestra" }, @@ -1726,7 +1744,7 @@ } }, "custom": { - "message": "Custom" + "message": "Personalizado" }, "maximumAccessCount": { "message": "Maximum Access Count" @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Região" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "UE", + "description": "European Union" + }, + "us": { + "message": "EUA", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 199ea672d86..47a684aa1ae 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Articolul s-a completat automat " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Setare parolă principală" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index b076d2017fd..9da7f861dc8 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Элемент заполнен " }, + "insecurePageWarning": { + "message": "Предупреждение: это незащищенная HTTP-страница, и любая информация, которую вы отправляете, потенциально может быть просмотрена и изменена кем угодно. Этот логин изначально был сохранен на защищенной (HTTPS) странице." + }, + "insecurePageWarningFillPrompt": { + "message": "Вы по-прежнему хотите заполнить этот логин?" + }, + "autofillIframeWarning": { + "message": "Форма размещена в домене, отличном от URI вашего сохраненного логина. Выберите OK для автозаполнения в любом случае или Отмена для остановки действия." + }, + "autofillIframeWarningTip": { + "message": "Чтобы больше не получать это предупреждение, сохраните этот URI, $HOSTNAME$, в соответствующем логине.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Задать мастер-пароль" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Регион" + }, + "opensInANewWindow": { + "message": "Откроется в новом окне" + }, + "eu": { + "message": "Европа", + "description": "European Union" + }, + "us": { + "message": "США", + "description": "United States" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 074813b0212..94baba9b6da 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "ස්වයංක්රීය-පිරවූ අයිතමය" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "මාස්ටර් මුරපදය සකසන්න" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index d1c9fd7ae7d..2fa04daf3df 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Automaticky vyplnené" }, + "insecurePageWarning": { + "message": "Upozornenie: Toto je nezabezpečená HTTP stránka a akékoľvek informácie, ktoré odošlete, môžu potenciálne vidieť a zmeniť ostatní. Toto prihlásenie bolo pôvodne uložené na zabezpečenej stránke (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Prajete si napriek tomu vyplniť prihlasovacie údaje?" + }, + "autofillIframeWarning": { + "message": "Formulár je hosťovaný inou doménou ako má URI uložených prihlasovacích údajov. Zvoľte OK ak chcete aj tak automaticky vyplniť údaje, alebo Zrušiť pre zastavenie." + }, + "autofillIframeWarningTip": { + "message": "Ak chcete tomuto upozorneniu v budúcnosti zabrániť, uložte URI, $HOSTNAME$, do položky prihlásenia Bitwardenu pre túto stránku.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Nastaviť hlavné heslo" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Región" + }, + "opensInANewWindow": { + "message": "Otvárať v novom okne" + }, + "eu": { + "message": "EÚ", + "description": "European Union" + }, + "us": { + "message": "USA", + "description": "United States" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index e5fa0c23e79..91fdc621fdf 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Brezplačen upravitelj gesel", + "message": "Bitwarden - Brezplačni upravitelj gesel", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -77,7 +77,7 @@ "message": "Kopiraj opombo" }, "copyUri": { - "message": "Kopiraj URL" + "message": "Kopiraj URI" }, "copyUsername": { "message": "Kopiraj uporabniško ime" @@ -89,37 +89,37 @@ "message": "Kopiraj varnostno kodo" }, "autoFill": { - "message": "samodejno zapolni" + "message": "Samodejno izpolnjevanje" }, "generatePasswordCopied": { "message": "Generiraj geslo (kopirano)" }, "copyElementIdentifier": { - "message": "Copy custom field name" + "message": "Kopiraj naziv polja po meri" }, "noMatchingLogins": { - "message": "Nobenih ujemajočih prijav." + "message": "Ni ustreznih prijav." }, "unlockVaultMenu": { "message": "Odkleni svoj trezor" }, "loginToVaultMenu": { - "message": "Log in to your vault" + "message": "Prijavi se v svoj trezor" }, "autoFillInfo": { - "message": "Nobene prijave ni na voljo za auto-izpolnitev za trenutni zavihek." + "message": "Za samodejno izpolnjevanje v trenutnem zavihku ni na voljo nobena prijava." }, "addLogin": { "message": "Dodaj prijavo" }, "addItem": { - "message": "Dodal element" + "message": "Dodaj element" }, "passwordHint": { "message": "Namig za geslo" }, "enterEmailToGetHint": { - "message": "Vnesite epoštni naslov vašega računa, da prejmete namig za vaše glavno geslo." + "message": "Vnesite e-poštni naslov svojega računa in poslali vam bomo namig za vaše glavno geslo." }, "getMasterPasswordHint": { "message": "Pridobi namig za glavno geslo" @@ -128,7 +128,7 @@ "message": "Nadaljuj" }, "sendVerificationCode": { - "message": "Send a verification code to your email" + "message": "Pošlji kodo za preverjanje po e-pošti" }, "sendCode": { "message": "Pošlji kodo" @@ -137,10 +137,10 @@ "message": "Koda poslana" }, "verificationCode": { - "message": "Verifikacijska koda" + "message": "Koda za preverjanje" }, "confirmIdentity": { - "message": "Confirm your identity to continue." + "message": "Za nadaljevanje potrdite svojo istovetnost." }, "account": { "message": "Račun" @@ -190,19 +190,19 @@ "message": "Mape" }, "noFolders": { - "message": "Ni map za prikazat." + "message": "Nobene takšne mape ni." }, "helpFeedback": { "message": "Pomoč in povratne informacije" }, "helpCenter": { - "message": "Bitwarden Help center" + "message": "Bitwardnov center za pomoč" }, "communityForums": { - "message": "Explore Bitwarden community forums" + "message": "Prebrskajte Bitwardnove skupnostne forume" }, "contactSupport": { - "message": "Contact Bitwarden support" + "message": "Kontaktirajte podporo uporabnikom" }, "sync": { "message": "Sinhronizacija" @@ -227,7 +227,7 @@ "message": "Bitwarden spletni trezor" }, "importItems": { - "message": "Uvozi predmete" + "message": "Uvozi elemente" }, "select": { "message": "Izberi" @@ -236,7 +236,7 @@ "message": "Generiraj geslo" }, "regeneratePassword": { - "message": "Regeneriraj geslo" + "message": "Ponovno ustvari geslo" }, "options": { "message": "Možnosti" @@ -245,13 +245,13 @@ "message": "Dolžina" }, "uppercase": { - "message": "Uppercase (A-Z)" + "message": "Velike črke (A-Z)" }, "lowercase": { - "message": "Lowercase (a-z)" + "message": "Male črke (a-z)" }, "numbers": { - "message": "Številke (0-9)" + "message": "Števke (0-9)" }, "specialCharacters": { "message": "Posebni znaki (!@#$%^&*)" @@ -267,10 +267,10 @@ "description": "Make the first letter of a work uppercase." }, "includeNumber": { - "message": "Vključi številko" + "message": "Vključi števko" }, "minNumbers": { - "message": "Minimalno števil" + "message": "Minimalno števk" }, "minSpecial": { "message": "Minimalno posebnih znakov" @@ -288,10 +288,10 @@ "message": "Pogled" }, "noItemsInList": { - "message": "Ni predmetov za prikazat." + "message": "Tukaj ni ničesar." }, "itemInformation": { - "message": "Informacije o izdelku" + "message": "Informacije o elementu" }, "username": { "message": "Uporabniško ime" @@ -312,16 +312,16 @@ "message": "Opomba" }, "editItem": { - "message": "Uredi vnos" + "message": "Uredi element" }, "folder": { "message": "Mapa" }, "deleteItem": { - "message": "Izbiši vnos" + "message": "Izbiši element" }, "viewItem": { - "message": "Ogled vnosa" + "message": "Ogled elementa" }, "launch": { "message": "Zaženi" @@ -330,10 +330,10 @@ "message": "Spletna stran" }, "toggleVisibility": { - "message": "Preklopi vidljivost" + "message": "Preklopi vidnost" }, "manage": { - "message": "Upravljaj" + "message": "Upravljanje" }, "other": { "message": "Drugo" @@ -342,22 +342,22 @@ "message": "Ocenite to razširitev" }, "rateExtensionDesc": { - "message": "Premislite če nam želite pomagati z dobro oceno!" + "message": "Premislite, ali bi nam želeli pomagati z dobro oceno!" }, "browserNotSupportClipboard": { - "message": "Vaš spletni brskalnik ne podpira enostavno kopiranje odložišča. Kopirajte ročno." + "message": "Vaš brskalnik ne podpira enostavnega kopiranja na odložišče. Prosimo, kopirajte ročno." }, "verifyIdentity": { - "message": "Verify identity" + "message": "Preverjanje istovetnosti" }, "yourVaultIsLocked": { - "message": "Vaš trezor je zaklenjen. Potrdite vaše glavno geslo za nadaljevanje." + "message": "Vaš trezor je zaklenjen. Za nadaljevanje potrdite svojo identiteto." }, "unlock": { "message": "Odkleni" }, "loggedInAsOn": { - "message": "Prijavljeni kot $EMAIL$ na $HOSTNAME$.", + "message": "Prijavljeni ste kot $EMAIL$ na $HOSTNAME$.", "placeholders": { "email": { "content": "$1", @@ -373,7 +373,7 @@ "message": "Napačno glavno geslo" }, "vaultTimeout": { - "message": "Časovna omejitev trezorja" + "message": "Zakleni trezor, ko preteče toliko časa:" }, "lockNow": { "message": "Zakleni zdaj" @@ -412,10 +412,10 @@ "message": "4 ure" }, "onLocked": { - "message": "Ob sistemskem zaklepu" + "message": "Ob zaklepu sistema" }, "onRestart": { - "message": "Ob ponovnem zagonu spletnega brskalnika" + "message": "Ob ponovnem zagonu brskalnika" }, "never": { "message": "Nikoli" @@ -433,13 +433,13 @@ "message": "Neveljaven epoštni naslov." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Glavno geslo je obvezno." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Ponoven vnos glavnega gesla je obvezen." }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "Glavno geslo mora vsebovati vsaj $VALUE$ znakov.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -452,16 +452,16 @@ "message": "Potrditev glavnega gesla se ne ujema." }, "newAccountCreated": { - "message": "Vaš nov račun je bil ustvarjen! Sedaj se lahko prijavite." + "message": "Vaš račun je ustvarjen. Lahko se prijavite." }, "masterPassSent": { - "message": "Poslali smo vam epoštno spročilo z namigom za vaše glavno geslo." + "message": "Poslali smo vam e-poštno spročilo z namigom za vaše glavno geslo." }, "verificationCodeRequired": { - "message": "Verifikacijska koda je obvezna." + "message": "Koda za preverjanje je obvezna." }, "invalidVerificationCode": { - "message": "Invalid verification code" + "message": "Neveljavna koda za preverjanje" }, "valueCopied": { "message": "$VALUE$ kopirano", @@ -474,7 +474,7 @@ } }, "autofillError": { - "message": "Ni mogoče avto-izpolniti izbrazno prijavo na tej spletni strani. Namesto tega kopirajte in prilepite podatke." + "message": "Izbrane prijave na tej strani ni mogoče samodejno izpolniti. Namesto tega podatke kopirajte in prilepite." }, "loggedOut": { "message": "Odjavljen" @@ -498,31 +498,31 @@ "message": "Ime je obvezno." }, "addedFolder": { - "message": "Dodana mapa" + "message": "Mapa dodana" }, "changeMasterPass": { "message": "Spremeni glavno geslo" }, "changeMasterPasswordConfirmation": { - "message": "Svoje glavno geslo lahko spremenite v bitwarden.com spletnem trezorju. Želite obiskati spletno stran zdaj?" + "message": "Svoje glavno geslo lahko spremenite v Bitwardnovem spletnem trezorju. Želite zdaj obiskati Bitwardnovo spletno stran?" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Avtentikacija v dveh korakih dodatno varuje vaš račun, saj zahteva, da vsakokratno prijavo potrdite z drugo napravo, kot je varnostni ključ, aplikacija za preverjanje pristnosti, SMS, telefonski klic ali e-pošta. Avtentikacijo v dveh korakih lahko omogočite v spletnem trezorju bitwarden.com. Ali želite spletno stran obiskati sedaj?" }, "editedFolder": { - "message": "Urejena mapa" + "message": "Mapa shranjena" }, "deleteFolderConfirmation": { "message": "Ste prepričani, da želite izbrisati to mapo?" }, "deletedFolder": { - "message": "Izbrisana mapa" + "message": "Mapa izbrisana" }, "gettingStartedTutorial": { - "message": "Getting started tutorial" + "message": "Vodič za začetnike" }, "gettingStartedTutorialVideo": { - "message": "Watch our getting started tutorial to learn how to get the most out of the browser extension." + "message": "Naš vodič za začtenike vam pokaže, kako najbolje izkoristiti Bitwardnovo razširitev za brskalnik." }, "syncingComplete": { "message": "Sinhronizacija končana" @@ -534,7 +534,7 @@ "message": "Geslo je bilo kopirano" }, "uri": { - "message": "URL" + "message": "URI" }, "uriPosition": { "message": "URI $POSITION$", @@ -547,31 +547,31 @@ } }, "newUri": { - "message": "Nov URL" + "message": "Nov URI" }, "addedItem": { - "message": "Dodan vnos" + "message": "Element dodan" }, "editedItem": { - "message": "Urejen vnos" + "message": "Element shranjen" }, "deleteItemConfirmation": { - "message": "Ali ste prepričani, da želite izbrisati?" + "message": "Ali ste prepričani, da želite to izbrisati?" }, "deletedItem": { - "message": "Pošlji vnos v smeti" + "message": "Element poslan v smeti" }, "overwritePassword": { "message": "Prepiši geslo" }, "overwritePasswordConfirmation": { - "message": "Ali ste prepričani, da želite prepisati vaše trenutno geslo?" + "message": "Ali ste prepričani, da želite prepisati trenutno geslo?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "Prepiši uporabniško ime" }, "overwriteUsernameConfirmation": { - "message": "Are you sure you want to overwrite the current username?" + "message": "Ste prepričani, da želite prepisati trenutno uporabniško ime?" }, "searchFolder": { "message": "Preišči mapo" @@ -580,68 +580,68 @@ "message": "Preišči zbirko" }, "searchType": { - "message": "Search type" + "message": "Išči med tipi" }, "noneFolder": { "message": "Brez mape", "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "Predlagaj dodajanje prijave" }, "addLoginNotificationDesc": { - "message": "Ask to add an item if one isn't found in your vault." + "message": "Predlagaj dodajanje novega elementa, če v trezorju ni ustreznega." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Prikaži kartice na strani Zavihek" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Na strani Zavihek prikaži kartice za lažje samodejno izpoljnjevanje." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Prikaži identitete na strani Zavihek" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Na strani Zavihek prikaži elemente identitete za lažje samodejno izpolnjevanje." }, "clearClipboard": { "message": "Počisti odložišče", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { - "message": "Automatically clear copied values from your clipboard.", + "message": "Samodejno izbriši kopirane vrednosti z odložišča.", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "notificationAddDesc": { - "message": "Should Bitwarden remember this password for you?" + "message": "Naj si Bitwarden zapomni to geslo?" }, "notificationAddSave": { "message": "Da, shrani zdaj" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Predlagaj posodobitev obstoječe prijave" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Vprašaj, ali naj Bitwarden posodobi geslo prijave, kadar zazna spremembo gesla na spletni strani" }, "notificationChangeDesc": { - "message": "Do you want to update this password in Bitwarden?" + "message": "Želite, da Bitwarden shrani spremembo tega gesla?" }, "notificationChangeSave": { "message": "Da, posodobi zdaj" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "Prikaži možnosti kontekstnega menuja" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Z desnim klikom se vam prikažejo možnosti generiranja gesel in shranjenih prijav za spletno stran, na kateri ste." }, "defaultUriMatchDetection": { - "message": "Default URI match detection", + "message": "Privzet način preverjanja ujemanja URI-ja", "description": "Default URI match detection for auto-fill." }, "defaultUriMatchDetectionDesc": { - "message": "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill." + "message": "Izberite privzeti način preverjanja ujemanja URI-ja pri samodejnem izpolnjevanju in drugih dejanjih." }, "theme": { "message": "Tema" @@ -675,7 +675,7 @@ "message": "Potrdite izvoz trezorja" }, "exportWarningDesc": { - "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it." + "message": "Ta datoteka z izvoženimi podatki vsebuje podatke iz vašega trezorja v nešifrirani obliki. Ne shranjujte in ne pošiljajte je po nezavarovanih kanalih, kot je elektronska pošta. Po uporabi jo takoj izbrišite." }, "encExportKeyWarningDesc": { "message": "Ta izvoz šifrira vaše podatke z uporabo ključa za šifriranje. Če boste kdaj zamenjali ključ za šifriranje, je potrebno, da ponovno naredite izvoz, ker ne boste mogli dešifrirati to izvoženo datoteko." @@ -690,19 +690,19 @@ "message": "V skupni rabi" }, "learnOrg": { - "message": "Learn about organizations" + "message": "Preberite več o organizacijah" }, "learnOrgConfirmation": { - "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" + "message": "Bitwarden omogoča, da elemente v svojem trezorju delite z drugimi z uporabo organizacije. Želite obskati spletišče bitwarden.com za več informacij?" }, "moveToOrganization": { - "message": "Move to organization" + "message": "Premakni v organizacijo" }, "share": { "message": "Deli" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "Element $ITEMNAME$ premaknjen v $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -715,7 +715,7 @@ } }, "moveToOrgDesc": { - "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." + "message": "Izberite organizacijo, v katero želite premakniti ta element. S tem boste prenesli lastništvo elementa na organizacijo in ne boste več njegov neposredni lastnik." }, "learnMore": { "message": "Več o tem" @@ -772,28 +772,28 @@ "message": "Upravljanje članstva" }, "premiumManageAlert": { - "message": "S svojim članstvom lahko upravljate na bitwarden.com spletnem trezorju. Želite obiskati spletno stran zdaj?" + "message": "S svojim članstvom lahko upravljate na spletnem trezorju bitwarden.com. Želite obiskati to spletno stran zdaj?" }, "premiumRefresh": { - "message": "Osvežite članstvo" + "message": "Osveži status članstva" }, "premiumNotCurrentMember": { "message": "Trenutno niste premium član." }, "premiumSignUpAndGet": { - "message": "Prijavite se za premium članstvo in dobite:" + "message": "Če postanete premium član, dobite:" }, "ppremiumSignUpStorage": { - "message": "1 GB šifriranog prostora za shrambo podatkov." + "message": "1 GB šifriranega prostora za shrambo podatkov." }, "ppremiumSignUpTwoStep": { - "message": "Dodatne možnosti za prijavo v dveh korakih kot so YubiKey, FIDO U2F in Duo." + "message": "Dodatne možnosti za prijavo v dveh korakih, n.pr. YubiKey, FIDO U2F in Duo." }, "ppremiumSignUpReports": { - "message": "Higiena gesel, zdravje računa in poročila o kraji podatkov, da lahko ohranite vaš trezor varen." + "message": "Higiena gesel, zdravje računa in poročila o kraji podatkov, ki vam pomagajo ohraniti varnost vašega trezorja." }, "ppremiumSignUpTotp": { - "message": "TOTP verifikacijska koda (2FA) generator za prijave v vašem trezorju." + "message": "Generator TOTP verifikacijskih kod (2FA) za prijave v vašem trezorju." }, "ppremiumSignUpSupport": { "message": "Prioritetna podpora strankam." @@ -805,13 +805,13 @@ "message": "Kupite premium članstvo" }, "premiumPurchaseAlert": { - "message": "Premium članstvo lahko kupite na bitwarden.com spletnem trezoju. Želite obiskati spletno stran zdaj?" + "message": "Premium članstvo lahko kupite na spletnem trezoju bitwarden.com. Želite obiskati spletno stran zdaj?" }, "premiumCurrentMember": { "message": "Ste premium član!" }, "premiumCurrentMemberThanks": { - "message": "Hvala da podpirate Bitwarden." + "message": "Hvala, ker podpirate Bitwarden." }, "premiumPrice": { "message": "Vse za samo $PRICE$ /leto!", @@ -826,16 +826,16 @@ "message": "Osveževanje zaključeno" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Samodejno kopiraj TOTP" }, "disableAutoTotpCopyDesc": { - "message": "Če za prijavo uporabljate avtentikacijski ključ je TOTP verifikacijska koda avtomatično kopirana v vaše odložišče kadarkoli avto-izpolnite prijavo." + "message": "Če za prijavo uporabljate avtentikacijski ključ, se verifikacijska koda TOTP samodejno kopira v odložišče, kadar uporabite samodejno izpolnjevanje." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Ob zagonu zahtevaj biometrično preverjanje" }, "premiumRequired": { - "message": "Premium članstvo je potrebno" + "message": "Potrebno je premium članstvo" }, "premiumRequiredDesc": { "message": "Premium članstvo je potrebno za uporabo te funkcije." @@ -853,7 +853,7 @@ } }, "verificationCodeEmailSent": { - "message": "Verification email sent to $EMAIL$.", + "message": "Potrditveno sporočilo poslano na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -892,7 +892,7 @@ "message": "Ta račun ima omogočemo prijavo v dveh korakih, ampak, nobena izmed konfiguriranih prijav v dveh korakih ni podprta v teb spletnem brskalniku." }, "noTwoStepProviders2": { - "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." + "message": "Uporabite enega izmed podprtih spletnih brskalnikov (npr. Chrome) in/ali dodajte ponudnika, ki je bolje podprt na različnih brskalnikih (npr. aplikacija za avtentikacijo)." }, "twoStepOptions": { "message": "Možnosti dvostopenjske prijave" @@ -907,14 +907,14 @@ "message": "Aplikacija za avtentikacijo" }, "authenticatorAppDesc": { - "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "message": "Uporabite aplikacijo za avtentikacijo (npr. Authy ali Google Authenticator), ki za vas ustvarja časovno spremenljive kode.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "YubiKey OTP Security Key" + "message": "Varnostni ključ YubiKey za enkratna gesla" }, "yubiKeyDesc": { - "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + "message": "Za dostop do svojega računa uporabite YubiKey. Podprti so YubiKey 4, 4 Nano, 4C in naprave NEO." }, "duoDesc": { "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", @@ -970,34 +970,34 @@ "message": "Environment URLs saved" }, "enableAutoFillOnPageLoad": { - "message": "Auto-fill on page load" + "message": "Samodejno izpolni, ko se stran naloži" }, "enableAutoFillOnPageLoadDesc": { - "message": "If a login form is detected, auto-fill when the web page loads." + "message": "Če Bitwarden na strani zazna prijavni obrazec, ga samodejno izpolni takoj, ko se stran naloži." }, "experimentalFeature": { - "message": "Compromised or untrusted websites can exploit auto-fill on page load." + "message": "Spletne strani, ki jim ne zaupate ali v katere so vdrli, lahko zlorabijo samodejno izpolnjevanje ob naložitvi strani." }, "learnMoreAboutAutofill": { - "message": "Learn more about auto-fill" + "message": "Preberite več o samodejnem izpolnjevanju" }, "defaultAutoFillOnPageLoad": { - "message": "Default autofill setting for login items" + "message": "Privzeta nastavitev samodejnega izpolnjevanja za prijavne elemente" }, "defaultAutoFillOnPageLoadDesc": { - "message": "You can turn off auto-fill on page load for individual login items from the item's Edit view." + "message": "Samodejno izpolnjevanje ob naložitvi strani lahko izklopite za posamčne prijave, ko jih urejate." }, "itemAutoFillOnPageLoad": { - "message": "Auto-fill on page load (if set up in Options)" + "message": "Samodejno izpolni ob naložitvi strani (če je omogočeno v Možnostih)" }, "autoFillOnPageLoadUseDefault": { "message": "Uporabi privzete nastavitve" }, "autoFillOnPageLoadYes": { - "message": "Auto-fill on page load" + "message": "Samodejno izpolni ob naložitvi strani" }, "autoFillOnPageLoadNo": { - "message": "Do not auto-fill on page load" + "message": "Ne izpolnjuj samodejno ob naložitvi strani" }, "commandOpenPopup": { "message": "Open vault popup" @@ -1006,10 +1006,10 @@ "message": "Open vault in sidebar" }, "commandAutofillDesc": { - "message": "Auto-fill the last used login for the current website" + "message": "Samodejno izpolni s prijavo, ki je bila na tej strani uporabljena zadnja" }, "commandGeneratePasswordDesc": { - "message": "Generate and copy a new random password to the clipboard" + "message": "Ustvari novo naključno geslo in ga kopiraj v odložišče" }, "commandLockVaultDesc": { "message": "Zakleni trezor" @@ -1018,7 +1018,7 @@ "message": "Private mode support is experimental and some features are limited." }, "customFields": { - "message": "Custom fields" + "message": "Polja po meri" }, "copyValue": { "message": "Kopiraj vrednost" @@ -1027,10 +1027,10 @@ "message": "Vrednost" }, "newCustomField": { - "message": "New custom field" + "message": "Novo polje po meri" }, "dragToSort": { - "message": "Drag to sort" + "message": "Sortirajte z vlečenjem" }, "cfTypeText": { "message": "Besedilo" @@ -1083,7 +1083,7 @@ "message": "Leto poteka" }, "expiration": { - "message": "Potek" + "message": "Veljavna do" }, "january": { "message": "Januar" @@ -1143,7 +1143,7 @@ "message": "Dr." }, "mx": { - "message": "Mx" + "message": "Gx" }, "firstName": { "message": "Ime" @@ -1212,7 +1212,7 @@ "message": "Prijave" }, "typeSecureNote": { - "message": "Varni zapisek" + "message": "Zavarovan zapisek" }, "typeCard": { "message": "Kartica" @@ -1233,7 +1233,7 @@ "message": "Priljubljeno" }, "popOutNewWindow": { - "message": "Pop out to a new window" + "message": "Odpri v svojem oknu" }, "refresh": { "message": "Osveži" @@ -1248,17 +1248,17 @@ "message": "Prijave" }, "secureNotes": { - "message": "Varni zapiski" + "message": "Zavarovani zapiski" }, "clear": { "message": "Počisti", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "Preveri, ali je bilo geslo izpostavljeno" }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "To geslo je bilo že $VALUE$-krat razkrito med raznimi ukradenimi podatki. Morali bi ga zamenjati.", "placeholders": { "value": { "content": "$1", @@ -1267,43 +1267,43 @@ } }, "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." + "message": "Tega gesla ni najti med znanimi ukradenimi podatki. Najbrž je varno, da ga uporabljate." }, "baseDomain": { - "message": "Base domain", + "message": "Bazna domena", "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Ime domene", "description": "Domain name. Ex. website.com" }, "host": { - "message": "Host", + "message": "Gostitelj", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { - "message": "Exact" + "message": "Dobesedno ujemanje" }, "startsWith": { - "message": "Starts with" + "message": "Ujemanje začetka" }, "regEx": { - "message": "Regular expression", + "message": "Regularni izraz", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Match detection", + "message": "Preverjanje ujemanja", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "Default match detection", + "message": "Privzeto preverjanje ujemanja", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { - "message": "Toggle options" + "message": "Prikaži/skrij možnosti" }, "toggleCurrentUris": { - "message": "Toggle current URIs", + "message": "Prikaži/skrij URI-je odprtih zavihkov", "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { @@ -1315,13 +1315,13 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Tipi" + "message": "Vrste" }, "allItems": { - "message": "All items" + "message": "Vsi elementi" }, "noPasswordsInList": { - "message": "There are no passwords to list." + "message": "Ni takšnih gesel." }, "remove": { "message": "Odstrani" @@ -1334,27 +1334,27 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Ustvarjeno", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "Password updated", + "message": "Geslo posodobljeno.", "description": "ex. Date this password was updated" }, "neverLockWarning": { - "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + "message": "Ste prepričani, da vam možnost Nikoli ustreza? Pri nastavitvi Nikoli se šifrirni ključ vašega trezorja shrani v vaši napravi. Ob uporabi te možnosti morate skrbeti za ustrezno varnost svoje naprave." }, "noOrganizationsList": { - "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + "message": "Niste član nobene organizacije. Organizacije vam omogočajo varno skupno rabo elementov z drugimi uporabniki." }, "noCollectionsInList": { - "message": "There are no collections to list." + "message": "Ni zbirk za prikaz." }, "ownership": { "message": "Lastništvo" }, "whoOwnsThisItem": { - "message": "Who owns this item?" + "message": "Kdo je lastnik tega elementa?" }, "strong": { "message": "Močno", @@ -1372,53 +1372,53 @@ "message": "Šibko glavno geslo" }, "weakMasterPasswordDesc": { - "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" + "message": "Glavno geslo, ki ste ga izbrali, je šibko. Za primerno zaščito svojega Bitwarden računa morate uporabiti močno glavno geslo. Ste prepričani, da želite uporabiti izbrano glavno geslo?" }, "pin": { "message": "PIN", "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Odkleni s PIN kodo" + "message": "Odkleni s PIN-kodo" }, "setYourPinCode": { - "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." + "message": "Za odklep Bitwardna si nastavite PIN-kodo. PIN-koda bo ponastavljena, če se boste popolnoma odjavili iz aplikacije." }, "pinRequired": { - "message": "Potrebna je PIN koda." + "message": "Potrebna je PIN-koda." }, "invalidPin": { - "message": "Nepravilna PIN koda." + "message": "Nepravilna PIN-koda." }, "unlockWithBiometrics": { "message": "Prijava z biometriko" }, "awaitDesktop": { - "message": "Awaiting confirmation from desktop" + "message": "Čakam na potrditev z namizja" }, "awaitDesktopDesc": { "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." }, "lockWithMasterPassOnRestart": { - "message": "Lock with master password on browser restart" + "message": "Zakleni z glavnim geslom ob ponovnem zagonu brskalnika" }, "selectOneCollection": { - "message": "You must select at least one collection." + "message": "Izbrati morate vsaj eno zbirko." }, "cloneItem": { - "message": "Clone item" + "message": "Podvoji element" }, "clone": { - "message": "Kloniraj" + "message": "Podvoji" }, "passwordGeneratorPolicyInEffect": { "message": "One or more organization policies are affecting your generator settings." }, "vaultTimeoutAction": { - "message": "Vault timeout action" + "message": "Dejanje ob poteku roka" }, "lock": { - "message": "Zakleni", + "message": "Zaklepanje", "description": "Verb form: to make secure or inaccesible by" }, "trash": { @@ -1429,49 +1429,67 @@ "message": "Preišči koš" }, "permanentlyDeleteItem": { - "message": "Permanently delete item" + "message": "Trajno izbriši element" }, "permanentlyDeleteItemConfirmation": { - "message": "Are you sure you want to permanently delete this item?" + "message": "Ste prepričani, da želite ta element trajno izbrisati?" }, "permanentlyDeletedItem": { - "message": "Item permanently deleted" + "message": "Element trajno izbrisan" }, "restoreItem": { - "message": "Restore item" + "message": "Obnovi element" }, "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" + "message": "Ste prepričani, da želite obnoviti ta element?" }, "restoredItem": { - "message": "Item restored" + "message": "Element obnovljen" }, "vaultTimeoutLogOutConfirmation": { "message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Timeout action confirmation" + "message": "Potrditev dejanja ob poteku roka" }, "autoFillAndSave": { - "message": "Auto-fill and save" + "message": "Samodejno izpolni in shrani" }, "autoFillSuccessAndSavedUri": { - "message": "Item auto-filled and URI saved" + "message": "Element je bil samodejno izpolnjen in shranjen" }, "autoFillSuccess": { - "message": "Item auto-filled " + "message": "Element je bil samodejno izpolnjen" + }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } }, "setMasterPassword": { "message": "Nastavi glavno geslo" }, "currentMasterPass": { - "message": "Current master password" + "message": "Trenutno glavno geslo" }, "newMasterPass": { - "message": "New master password" + "message": "Novo glavno geslo" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Ponovitev glavnega gesla" }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" @@ -1513,22 +1531,22 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Your new master password does not meet the policy requirements." + "message": "Vaše novo glavno geslo ne ustreza zahtevam." }, "acceptPolicies": { - "message": "By checking this box you agree to the following:" + "message": "Strinjam se z naslednjim:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Niste sprejeli Pogojev uporabe in Pravilnika o zasebnosti." }, "termsOfService": { - "message": "Terms of Service" + "message": "Pogoji uporabe" }, "privacyPolicy": { "message": "Pravilnik o zasebnosti" }, "hintEqualsPassword": { - "message": "Your password hint cannot be the same as your password." + "message": "Namig za geslo ne sme biti enak geslu." }, "ok": { "message": "V redu" @@ -1537,7 +1555,7 @@ "message": "Desktop sync verification" }, "desktopIntegrationVerificationText": { - "message": "Please verify that the desktop application shows this fingerprint: " + "message": "Prosimo, preverite, da namizna aplikacija prikazuje naslednje identifikacijsko geslo: " }, "desktopIntegrationDisabledTitle": { "message": "Browser integration is not set up" @@ -1582,31 +1600,31 @@ "message": "Browser biometrics is not supported on this device." }, "nativeMessaginPermissionErrorTitle": { - "message": "Permission not provided" + "message": "Dovoljenje manjka" }, "nativeMessaginPermissionErrorDesc": { - "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." + "message": "Brez dovoljenja za komunikacijo z Bitwardnovo namizno aplikacijo ni mogoče uporabljati bimetričnega preverjanja v razširitvi brskalnika. Prosimo, poskusite ponovno." }, "nativeMessaginPermissionSidebarTitle": { - "message": "Permission request error" + "message": "Napaka pri zahtevku za dovoljenje" }, "nativeMessaginPermissionSidebarDesc": { "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." }, "personalOwnershipSubmitError": { - "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available collections." + "message": "Politika podjetja določa, da ne morete shranjevati elementov v svoj osebni trezor. Spremenite lastništvo na organizacijo in izberite izmed zbirk na voljo." }, "personalOwnershipPolicyInEffect": { "message": "An organization policy is affecting your ownership options." }, "excludedDomains": { - "message": "Excluded domains" + "message": "Izključene domene" }, "excludedDomainsDesc": { - "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + "message": "Za te domene Bitwarden ne bo predlagal shranjevanja prijavnih podatkov. Sprememba nastavitev stopi v veljavo šele, ko osvežite stran." }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ ni veljavna domena", "placeholders": { "domain": { "content": "$1", @@ -1615,15 +1633,15 @@ } }, "send": { - "message": "Pošlji", + "message": "Pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { - "message": "Search Sends", + "message": "Išči pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "addSend": { - "message": "Add Send", + "message": "Dodaj pošiljko", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { @@ -1633,7 +1651,7 @@ "message": "Datoteka" }, "allSends": { - "message": "All Sends", + "message": "Vse pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { @@ -1650,67 +1668,67 @@ "message": "Password protected" }, "copySendLink": { - "message": "Copy Send link", + "message": "Kopiraj povezavo pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { - "message": "Remove Password" + "message": "Odstrani geslo" }, "delete": { "message": "Izbriši" }, "removedPassword": { - "message": "Password removed" + "message": "Geslo odstranjeno" }, "deletedSend": { - "message": "Send deleted", + "message": "Pošiljka izbrisana", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Pošlji povezavo", + "message": "Povezava pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { "message": "Onemogočeno" }, "removePasswordConfirmation": { - "message": "Are you sure you want to remove the password?" + "message": "Ste prepričani, da želite odstraniti geslo?" }, "deleteSend": { - "message": "Delete Send", + "message": "Izbriši pošiljko", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "Are you sure you want to delete this Send?", + "message": "Ste prepričani, da želite izbrisati to pošiljko?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { - "message": "Edit Send", + "message": "Uredi pošiljko", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeHeader": { - "message": "What type of Send is this?", + "message": "Kakšna vrsta pošiljke je to?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNameDesc": { - "message": "A friendly name to describe this Send.", + "message": "Prijazno ime, ki opisuje to pošiljko", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFileDesc": { - "message": "The file you want to send." + "message": "Datoteka, ki jo želite poslati" }, "deletionDate": { - "message": "Deletion date" + "message": "Datum izbrisa" }, "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", + "message": "Pošiljka bo trajno izbrisana ob izbranem času.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { - "message": "Expiration date" + "message": "Datum poteka" }, "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", + "message": "Če to nastavite, bo pošiljka potekla ob izbranem času.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { @@ -1726,62 +1744,62 @@ } }, "custom": { - "message": "Custom" + "message": "Po meri" }, "maximumAccessCount": { - "message": "Maximum Access Count" + "message": "Največje dovoljeno število dostopov" }, "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", + "message": "Če to nastavite, uporabniki po določenem številu dostopov ne bodo mogli več dostopati do pošiljke.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", + "message": "Za dostop do te pošiljke lahko nastavite geslo.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { - "message": "Private notes about this Send.", + "message": "Zasebni zapiski o tej pošiljki.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", + "message": "Onemogoči to pošiljko, da nihče ne more dostopati do nje", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", + "message": "Kopiraj povezavo te pošiljke v odložišče po shranjevanju", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { - "message": "The text you want to send." + "message": "Besedilo, ki ga želite poslati" }, "sendHideText": { - "message": "Hide this Send's text by default.", + "message": "Privzeto skrij besedilo te pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { - "message": "Current access count" + "message": "Trenutno število dstopov" }, "createSend": { - "message": "New Send", + "message": "Nova pošiljka", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { - "message": "New password" + "message": "Novo geslo" }, "sendDisabled": { - "message": "Send removed", + "message": "Pošiljka odstranjena", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Due to an enterprise policy, you are only able to delete an existing Send.", + "message": "Pravila podjetja določajo, da lahko izbrišete le obstoječo pošiljko.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Send created", + "message": "Pošiljka ustvarjena", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Send saved", + "message": "Pošiljka shranjena", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -1794,61 +1812,61 @@ "message": "In order to choose a file using Safari, pop out to a new window by clicking this banner." }, "sendFileCalloutHeader": { - "message": "Before you start" + "message": "Preden pričnete" }, "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", + "message": "Za vnos datuma s pomočjo koledarčka", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" }, "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", + "message": "kliknite tukaj", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" }, "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", + "message": "za prikaz v lastnem oknu.", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" }, "expirationDateIsInvalid": { - "message": "The expiration date provided is not valid." + "message": "Datum poteka ni veljaven." }, "deletionDateIsInvalid": { - "message": "The deletion date provided is not valid." + "message": "Datum izbrisa ni veljaven." }, "expirationDateAndTimeRequired": { - "message": "An expiration date and time are required." + "message": "Datum in čas poteka sta obvezna." }, "deletionDateAndTimeRequired": { - "message": "A deletion date and time are required." + "message": "Datum in čas izbrisa sta obvezna." }, "dateParsingError": { - "message": "There was an error saving your deletion and expiration dates." + "message": "Pri shranjevanju datumov poteka in izbrisa je prišlo do napake." }, "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Skrij moj e-naslov pred prejemniki." }, "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." + "message": "Nekatere nastavitve organizacije vplivajo na možnosti, ki jih imate v zvezi s pošiljkami." }, "passwordPrompt": { - "message": "Master password re-prompt" + "message": "Ponovno zahtevaj glavno geslo" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "Potrditev glavnega gesla" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "To dejanje je zaščiteno. Za nadaljevanje vpišite svoje glavno geslo, da potrdite svojo istovetnost." }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "Potrebna je potrditev e-naslova" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature. You can verify your email in the web vault." + "message": "Za uporabo te funkcionalnosti morate potrditi svoj e-naslov. To lahko storite v spletnem trezorju." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Posodobi glavno geslo" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Spremeni glavno geslo" }, "updateMasterPasswordWarning": { "message": "Your master password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." @@ -1863,7 +1881,7 @@ "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." }, "selectFolder": { - "message": "Select folder..." + "message": "Izberi mapo..." }, "ssoCompleteRegistration": { "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." @@ -1953,7 +1971,7 @@ "message": "You have left the organization." }, "toggleCharacterCount": { - "message": "Toggle character count" + "message": "Prikaži/skrij št. znakov" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1962,7 +1980,7 @@ "message": "Exporting individual vault" }, "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "message": "Izvoženi bodo samo posamezni elementi trezorja, ki so povezani z $EMAIL$. Elementi trezorjev organizacij ne bodo izvoženi.", "placeholders": { "email": { "content": "$1", @@ -1974,13 +1992,13 @@ "message": "Napaka" }, "regenerateUsername": { - "message": "Regenerate username" + "message": "Ponovno ustvari uporabniško ime" }, "generateUsername": { - "message": "Generate username" + "message": "Ustvari uporabniško ime" }, "usernameType": { - "message": "Username type" + "message": "Vrsta uporabniškega imena" }, "plusAddressedEmail": { "message": "Plus addressed email", @@ -2005,10 +2023,10 @@ "message": "Ime spletne strani" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Kaj želite generirati?" }, "passwordType": { - "message": "Password type" + "message": "Vrsta gesla" }, "service": { "message": "Service" @@ -2033,13 +2051,13 @@ "message": "Key connector error: make sure key connector is available and working correctly." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Potrebno je premium članstvo" }, "organizationIsDisabled": { "message": "Organization suspended." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "Do elementov v suspendiranih organizacijah ne morete dostopati. Za pomoč se obrnite na lastnika svoje organizacije." }, "loggingInTo": { "message": "Logging in to $DOMAIN$", @@ -2111,7 +2129,7 @@ "message": "Identifikacijsko geslo" }, "fingerprintMatchInfo": { - "message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device." + "message": "Prosimo, preverite, da je vaš trezor odklenjem in da se identifikacijski gesli na tej in drugi napravi ujemata." }, "resendNotification": { "message": "Resend notification" @@ -2138,13 +2156,13 @@ "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "Preverite, ali je bilo geslo izpostavljeno v krajah podatkov" }, "important": { - "message": "Important:" + "message": "Pomembno:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "Če pozabite glavno geslo, ga ne bo mogoče povrniti!" }, "characterMinimum": { "message": "$LENGTH$ character minimum", @@ -2156,13 +2174,13 @@ } }, "autofillPageLoadPolicyActivated": { - "message": "Your organization policies have turned on auto-fill on page load." + "message": "V pravilih vaše organizacije je vklopljeno samodejno izpolnjevanje." }, "howToAutofill": { - "message": "How to auto-fill" + "message": "Kako uporabljati samodejno izpolnjevanje" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this page or use the shortcut: $COMMAND$", + "message": "Izberite element na tej strani ali pa uporabite bližnjico $COMMAND$.", "placeholders": { "command": { "content": "$1", @@ -2171,22 +2189,22 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this page or set a shortcut in settings." + "message": "Izberite element na tej strani ali pa nastavite bližnjico v nastavitvah." }, "gotIt": { - "message": "Got it" + "message": "Razumem" }, "autofillSettings": { - "message": "Auto-fill settings" + "message": "Nastavitve" }, "autofillShortcut": { - "message": "Auto-fill keyboard shortcut" + "message": "Bližnjica za samodejno izpolnjevanje" }, "autofillShortcutNotSet": { - "message": "The auto-fill shortcut is not set. Change this in the browser's settings." + "message": "Bližnjična tipka za samodejno izpolnjevanje ni nastavljena. Nastavite jo lahko v nastavitvah brskalnika." }, "autofillShortcutText": { - "message": "The auto-fill shortcut is: $COMMAND$. Change this in the browser's settings.", + "message": "Bližnjična tipka za samodejno izpolnjevanje je $COMMAND$. Spremenite jo lahko v nastavitvah brskalnika.", "placeholders": { "command": { "content": "$1", @@ -2195,12 +2213,26 @@ } }, "autofillShortcutTextSafari": { - "message": "Default auto-fill shortcut: $COMMAND$.", + "message": "Privzeta bližnjica za samodejno izpolnjevanje: $COMMAND$", "placeholders": { "command": { "content": "$1", "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Odpre se v novem oknu" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 08ed28cf8c0..952ae901451 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Ставка ауто-попуњена" }, + "insecurePageWarning": { + "message": "Упозорење: ово је необезбеђена ХТТП страница и све информације које пошаљете потенцијално могу други да виде и промене. Ова пријава је првобитно била сачувана на безбедној (ХТТПС) страници." + }, + "insecurePageWarningFillPrompt": { + "message": "Да ли и даље желите да попуните ову пријаву?" + }, + "autofillIframeWarning": { + "message": "Образац је хостован на другом домену од УРЛ-а ваше сачуване пријаве. Изаберите ОК да бисте ипак аутоматски попунили или Откажи да бисте зауставили." + }, + "autofillIframeWarningTip": { + "message": "Да бисте спречили ово упозорење у будућности, сачувајте ову УРЛ, $HOSTNAME$, у Bitwarden пријавју за овај сајт.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Постави Главну Лозинку" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Регион" + }, + "opensInANewWindow": { + "message": "Отвара се у новом прозору" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 67e14d7eb99..79afc8729d5 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Fyllde i objektet automatiskt" }, + "insecurePageWarning": { + "message": "Varning: Detta är en icke säkrad HTTP-sida, och all information du skickar kan potentiellt ses och ändras av andra. Denna inloggning sparades ursprungligen på en säker (HTTPS) sida." + }, + "insecurePageWarningFillPrompt": { + "message": "Vill du fortfarande fylla i denna inloggning?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "För att förhindra denna varning i framtiden, spara denna URI, $HOSTNAME$, till ditt Bitwarden inloggningsobjekt för denna webbplats.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ange huvudlösenord" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Öppnas i ett nytt fönster" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 9a97e79cbe3..69d26333a84 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Item auto-filled " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Set master password" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 1abd08cf04f..1e43987ab06 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "รายการเติมอัตโนมัติ " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "ตั้งรหัสผ่านหลัก" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 25066f7b5c2..cdc8e109fe4 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Kayıt otomatik dolduruldu " }, + "insecurePageWarning": { + "message": "Uyarı: Güvenli olmayan bir HTTP sayfasındasınız. Gönderdiğiniz bilgiler potansiyel olarak başkaları tarafından görülebilir ve değiştirilebilir. Bu hesabı güvenli (HTTPS) bir sayfa üzerinden kaydetmiştiniz." + }, + "insecurePageWarningFillPrompt": { + "message": "Yine de bu hesabı doldurmak istiyor musunuz?" + }, + "autofillIframeWarning": { + "message": "Bu form, kayıtlı hesabınızın URI'sinden farklı bir alan adında yer alıyor. Yine de otomatik doldurmak isterseniz \"Tamam\"ı, durdurmak için \"İptal\"i seçin." + }, + "autofillIframeWarningTip": { + "message": "İleride bu uyarıyı görmek istemiyorsanız bu siteye ait Bitwarden hesap kaydınıza $HOSTNAME$ URI'sini ekleyin.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Ana parolayı belirle" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Bölge" + }, + "opensInANewWindow": { + "message": "Yeni pencerede açılır" + }, + "eu": { + "message": "AB", + "description": "European Union" + }, + "us": { + "message": "ABD", + "description": "United States" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index afda5c290a6..1969bc33624 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Запис заповнено" }, + "insecurePageWarning": { + "message": "Попередження: це незахищена сторінка HTTP, тому будь-яка інформація, яку ви передаєте, потенційно може бути переглянута чи змінена сторонніми. Ці облікові дані було збережено на безпечній сторінці (HTTPS)." + }, + "insecurePageWarningFillPrompt": { + "message": "Ви все ще бажаєте заповнити поля для входу?" + }, + "autofillIframeWarning": { + "message": "Домен форми входу відрізняється від URL-адреси, за якою його було збережено. Оберіть OK, якщо ви все одно хочете її заповнити, або Скасувати для зупинки." + }, + "autofillIframeWarningTip": { + "message": "Щоб надалі не бачити таке попередження, збережіть цей URI, $HOSTNAME$ у записі входу Bitwarden для цього сайту.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Встановити головний пароль" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Регіон" + }, + "opensInANewWindow": { + "message": "Відкривається у новому вікні" + }, + "eu": { + "message": "ЄС", + "description": "European Union" + }, + "us": { + "message": "США", + "description": "United States" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index d1cdfc0113f..fee4c66f3a9 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "Đã tự động điền mục " }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "Đặt mật khẩu chính" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index fb49cfcf92b..45c3c62db0e 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "项目已自动填充 " }, + "insecurePageWarning": { + "message": "警告:这是一个不安全的 HTTP 页面,您提交的任何信息都可能被其他人看到和更改。此登录最初保存在安全 (HTTPS) 页面上。" + }, + "insecurePageWarningFillPrompt": { + "message": "您仍然想要填充此登录信息吗?" + }, + "autofillIframeWarning": { + "message": "该表单由不同于您保存的登录的 URI 域名托管。选择「确定」以自动填充,或选择「取消」停止填充。" + }, + "autofillIframeWarningTip": { + "message": "要防止以后再次出现此警告,请将此站点的 URI $HOSTNAME$ 保存到您的 Bitwarden 登录项目中。", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "设置主密码" }, @@ -1854,7 +1872,7 @@ "message": "您的主密码最近被您组织的管理员更改过。要访问密码库,您必须立即更新它。继续操作将使您退出当前会话并要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。" }, "updateWeakMasterPasswordWarning": { - "message": "您的主密码不符合您的组织策略要求。要访问密码库,必须立即更新您的主密码。继续操作将使您退出当前会话,要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。" + "message": "您的主密码不符合某一项或多项组织策略要求。要访问密码库,必须立即更新您的主密码。继续操作将使您退出当前会话,并要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。" }, "resetPasswordPolicyAutoEnroll": { "message": "自动注册" @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "区域" + }, + "opensInANewWindow": { + "message": "在新窗口中打开" + }, + "eu": { + "message": "欧盟", + "description": "European Union" + }, + "us": { + "message": "美国", + "description": "United States" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 8da41079ca3..42e082f136b 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -1461,6 +1461,24 @@ "autoFillSuccess": { "message": "項目已自動填入" }, + "insecurePageWarning": { + "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + }, + "insecurePageWarningFillPrompt": { + "message": "Do you still wish to fill this login?" + }, + "autofillIframeWarning": { + "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to auto-fill anyway, or Cancel to stop." + }, + "autofillIframeWarningTip": { + "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "placeholders": { + "hostname": { + "content": "$1", + "example": "www.example.com" + } + } + }, "setMasterPassword": { "message": "設定主密碼" }, @@ -2202,5 +2220,19 @@ "example": "CTRL+Shift+L" } } + }, + "region": { + "message": "Region" + }, + "opensInANewWindow": { + "message": "Opens in a new window" + }, + "eu": { + "message": "EU", + "description": "European Union" + }, + "us": { + "message": "US", + "description": "United States" } } diff --git a/apps/browser/src/auth/popup/environment.component.ts b/apps/browser/src/auth/popup/environment.component.ts index 3b58ccfa1d5..5ad42a600c7 100644 --- a/apps/browser/src/auth/popup/environment.component.ts +++ b/apps/browser/src/auth/popup/environment.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core"; import { Router } from "@angular/router"; import { EnvironmentComponent as BaseEnvironmentComponent } from "@bitwarden/angular/components/environment.component"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -18,9 +19,10 @@ export class EnvironmentComponent extends BaseEnvironmentComponent implements On platformUtilsService: PlatformUtilsService, public environmentService: BrowserEnvironmentService, i18nService: I18nService, - private router: Router + private router: Router, + modalService: ModalService ) { - super(platformUtilsService, environmentService, i18nService); + super(platformUtilsService, environmentService, i18nService, modalService); this.showCustom = true; } diff --git a/apps/browser/src/auth/popup/home.component.html b/apps/browser/src/auth/popup/home.component.html index fb51458c136..da208a8d50c 100644 --- a/apps/browser/src/auth/popup/home.component.html +++ b/apps/browser/src/auth/popup/home.component.html @@ -9,9 +9,7 @@ - +
- diff --git a/apps/browser/src/auth/popup/home.component.ts b/apps/browser/src/auth/popup/home.component.ts index 9f864fa2ad5..361218c6e4b 100644 --- a/apps/browser/src/auth/popup/home.component.ts +++ b/apps/browser/src/auth/popup/home.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; +import { Router } from "@angular/router"; +import { Subject, takeUntil } from "rxjs"; +import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -12,9 +14,12 @@ import { LoginService } from "@bitwarden/common/auth/abstractions/login.service" selector: "app-home", templateUrl: "home.component.html", }) -export class HomeComponent implements OnInit { - loginInitiated = false; +export class HomeComponent implements OnInit, OnDestroy { + @ViewChild(EnvironmentSelectorComponent, { static: true }) + environmentSelector!: EnvironmentSelectorComponent; + private destroyed$: Subject = new Subject(); + loginInitiated = false; formGroup = this.formBuilder.group({ email: ["", [Validators.required, Validators.email]], rememberEmail: [false], @@ -27,9 +32,9 @@ export class HomeComponent implements OnInit { private router: Router, private i18nService: I18nService, private environmentService: EnvironmentService, - private route: ActivatedRoute, private loginService: LoginService ) {} + async ngOnInit(): Promise { let savedEmail = this.loginService.getEmail(); const rememberEmail = this.loginService.getRememberEmail(); @@ -48,6 +53,18 @@ export class HomeComponent implements OnInit { }); } } + + this.environmentSelector.onOpenSelfHostedSettings + .pipe(takeUntil(this.destroyed$)) + .subscribe(() => { + this.setFormValues(); + this.router.navigate(["environment"]); + }); + } + + ngOnDestroy(): void { + this.destroyed$.next(); + this.destroyed$.complete(); } submit() { diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login.component.ts index cc48a75e4e9..0652070a4da 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login.component.ts @@ -3,9 +3,9 @@ import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @@ -27,7 +27,7 @@ import { flagEnabled } from "../../flags"; export class LoginComponent extends BaseLoginComponent { showPasswordless = false; constructor( - apiService: ApiService, + devicesApiService: DevicesApiServiceAbstraction, appIdService: AppIdService, authService: AuthService, router: Router, @@ -46,7 +46,7 @@ export class LoginComponent extends BaseLoginComponent { loginService: LoginService ) { super( - apiService, + devicesApiService, appIdService, authService, router, diff --git a/apps/browser/src/autofill/content/autofill.js b/apps/browser/src/autofill/content/autofill.js index 09c616bfb51..052fd1120fe 100644 --- a/apps/browser/src/autofill/content/autofill.js +++ b/apps/browser/src/autofill/content/autofill.js @@ -58,8 +58,8 @@ /** * For a given element `el`, returns the value of the attribute `attrName`. - * @param {HTMLElement} el - * @param {string} attrName + * @param {HTMLElement} el + * @param {string} attrName * @returns {string} The value of the attribute */ function getElementAttrValue(el, attrName) { @@ -96,7 +96,7 @@ /** * Returns the value of the given element. - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {any} Value of the element */ function getElementValue(el) { @@ -124,7 +124,7 @@ /** * If `el` is a `` element */ function getSelectElementOptions(el) { @@ -147,7 +147,7 @@ /** * If `el` is in a data table, get the label in the row directly above it - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {string} A string containing the label, or null if not found */ function getLabelTop(el) { @@ -187,7 +187,7 @@ /** * Get the contents of the elements that are labels for `el` - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {string} A string containing all of the `innerText` or `textContent` values for all elements that are labels for `el` */ function getLabelTag(el) { @@ -239,10 +239,10 @@ /** * Add property `prop` with value `val` to the object `obj` - * @param {object} obj - * @param {string} prop - * @param {any} val - * @param {*} d + * @param {object} obj + * @param {string} prop + * @param {any} val + * @param {*} d */ function addProp(obj, prop, val, d) { if (0 !== d && d === val || null === val || void 0 === val) { @@ -254,7 +254,7 @@ /** * Converts the string `s` to lowercase - * @param {string} s + * @param {string} s * @returns Lowercase string */ function toLowerString(s) { @@ -263,8 +263,8 @@ /** * Query the document `doc` for elements matching the selector `selector` - * @param {Document} doc - * @param {string} query + * @param {Document} doc + * @param {string} query * @returns {HTMLElement[]} An array of elements matching the selector */ function queryDoc(doc, query) { @@ -452,8 +452,8 @@ /** * Do the event on the element. * @param {HTMLElement} kedol The element to do the event on - * @param {string} fonor The event name - * @returns + * @param {string} fonor The event name + * @returns */ function doEventOnElement(kedol, fonor) { var quebo; @@ -465,7 +465,7 @@ /** * Clean up the string `s` to remove non-printable characters and whitespace. - * @param {string} s + * @param {string} s * @returns {string} Clean text */ function cleanText(s) { @@ -477,7 +477,7 @@ /** * If `el` is a text node, add the node's text to `arr`. * If `el` is an element node, add the element's `textContent or `innerText` to `arr`. - * @param {string[]} arr An array of `textContent` or `innerText` values + * @param {string[]} arr An array of `textContent` or `innerText` values * @param {HTMLElement} el The element to push to the array */ function checkNodeType(arr, el) { @@ -511,9 +511,9 @@ /** * Recursively gather all of the text values from the elements preceding `el` in the DOM - * @param {HTMLElement} el + * @param {HTMLElement} el * @param {string[]} arr An array of `textContent` or `innerText` values - * @param {number} steps The number of steps to take up the DOM tree + * @param {number} steps The number of steps to take up the DOM tree */ function shiftForLeftLabel(el, arr, steps) { var sib; @@ -544,7 +544,7 @@ /** * Determine if the element is visible. * Visible is define as not having `display: none` or `visibility: hidden`. - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {boolean} Returns `true` if the element is visible and `false` otherwise */ function isElementVisible(el) { @@ -576,7 +576,7 @@ /** * Determine if the element is "viewable" on the screen. * "Viewable" is defined as being visible in the DOM and being within the confines of the viewport. - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {boolean} Returns `true` if the element is viewable and `false` otherwise */ function isElementViewable(el) { @@ -615,7 +615,7 @@ // If the right side of the bounding rectangle is outside the viewport, the x coordinate of the center point is the window width (minus offset) divided by 2. // If the right side of the bounding rectangle is inside the viewport, the x coordinate of the center point is the width of the bounding rectangle divided by 2. // If the bottom of the bounding rectangle is outside the viewport, the y coordinate of the center point is the window height (minus offset) divided by 2. - // If the bottom side of the bounding rectangle is inside the viewport, the y coordinate of the center point is the height of the bounding rectangle divided by + // If the bottom side of the bounding rectangle is inside the viewport, the y coordinate of the center point is the height of the bounding rectangle divided by // We then use elementFromPoint to find the element at that point. for (var pointEl = el.ownerDocument.elementFromPoint(leftOffset + (rect.right > window.innerWidth ? (window.innerWidth - leftOffset) / 2 : rect.width / 2), topOffset + (rect.bottom > window.innerHeight ? (window.innerHeight - topOffset) / 2 : rect.height / 2)); pointEl && pointEl !== el && pointEl !== document;) { // If the element we found is a label, and the element we're checking has labels @@ -637,7 +637,7 @@ /** * Retrieve the element from the document with the specified `opid` property - * @param {number} opId + * @param {number} opId * @returns {HTMLElement} The element with the specified `opiId`, or `null` if no such element exists */ function getElementForOPID(opId) { @@ -676,7 +676,7 @@ var els = []; try { var elsList = theDoc.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="reset"])' + - ':not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), select, ' + + ':not([type="button"]):not([type="image"]):not([type="file"]):not([data-bwignore]), select, textarea, ' + 'span[data-bwautofill]'); els = Array.prototype.slice.call(elsList); } catch (e) { } @@ -714,7 +714,7 @@ /** * Focus the element `el` and optionally restore its original value - * @param {HTMLElement} el + * @param {HTMLElement} el * @param {boolean} setVal Set the value of the element to its original value */ function focusElement(el, setVal) { @@ -741,14 +741,29 @@ // Check if URL is not secure when the original saved one was function urlNotSecure(savedURLs) { - var passwordInputs = null; - if (!savedURLs) { + if (!savedURLs || !savedURLs.length) { return false; } - return savedURLs.some(url => url?.indexOf('https://') === 0) && 'http:' === document.location.protocol && (passwordInputs = document.querySelectorAll('input[type=password]'), - 0 < passwordInputs.length && (confirmResult = confirm('Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page.\n\nDo you still wish to fill this login?'), - 0 == confirmResult)) ? true : false; + const confirmationWarning = [ + chrome.i18n.getMessage("insecurePageWarning"), + chrome.i18n.getMessage("insecurePageWarningFillPrompt", [window.location.hostname]) + ].join('\n\n'); + + if ( + // At least one of the `savedURLs` uses SSL + savedURLs.some(url => url.startsWith('https://')) && + // The current page is not using SSL + document.location.protocol === 'http:' && + // There are password inputs on the page + document.querySelectorAll('input[type=password]')?.length + ) { + // The user agrees the page is unsafe or not + return !confirm(confirmationWarning); + } + + // The page is secure + return false; } // Detect if within an iframe, and the iframe is sandboxed @@ -777,10 +792,13 @@ // confirm() is blocked by sandboxed iframes, but we don't want to fill sandboxed iframes anyway. // If this occurs, confirm() returns false without displaying the dialog box, and autofill will be aborted. // The browser may print a message to the console, but this is not a standard error that we can handle. - var acceptedIframeWarning = confirm("The form is hosted by a different domain than the URI " + - "of your saved login. Choose OK to auto-fill anyway, or Cancel to stop. " + - "To prevent this warning in the future, save this URI, " + - window.location.hostname + ", to your login."); + const confirmationWarning = [ + chrome.i18n.getMessage("autofillIframeWarning"), + chrome.i18n.getMessage("autofillIframeWarningTip", [window.location.hostname]) + ].join('\n\n'); + + const acceptedIframeWarning = confirm(confirmationWarning); + if (!acceptedIframeWarning) { return; } @@ -883,8 +901,8 @@ /** * Find all elements matching `query` and fill them using the value `op` from the fill script - * @param {string} query - * @param {string} op + * @param {string} query + * @param {string} op * @returns {HTMLElement} */ function doFillByQuery(query, op) { @@ -897,8 +915,8 @@ /** * Assign `valueToSet` to all elements in the DOM that match `query`. - * @param {string} query - * @param {string} valueToSet + * @param {string} query + * @param {string} valueToSet * @returns {Array} Array of elements that were set. */ function doSimpleSetByQuery(query, valueToSet) { @@ -912,8 +930,8 @@ /** * Do a a click and focus on the element with the given `opId`. - * @param {number} opId - * @returns + * @param {number} opId + * @returns */ function doFocusByOpId(opId) { var el = getElementByOpId(opId) @@ -927,8 +945,8 @@ /** * Do a click on the element with the given `opId`. - * @param {number} opId - * @returns + * @param {number} opId + * @returns */ function doClickByOpId(opId) { var el = getElementByOpId(opId); @@ -936,9 +954,9 @@ } /** - * Do a `click` and `focus` on all elements that match the query. - * @param {string} query - * @returns + * Do a `click` and `focus` on all elements that match the query. + * @param {string} query + * @returns */ function doClickByQuery(query) { query = selectAllFromDoc(query); @@ -961,8 +979,8 @@ /** * Fll an element `el` using the value `op` from the fill script - * @param {HTMLElement} el - * @param {string} op + * @param {HTMLElement} el + * @param {string} op */ function fillTheElement(el, op) { var shouldCheck; @@ -994,7 +1012,7 @@ /** * Do all the fill operations needed on the element `el`. - * @param {HTMLElement} el + * @param {HTMLElement} el * @param {*} afterValSetFunc The function to perform after the operations are complete. */ function doAllFillOperations(el, afterValSetFunc) { @@ -1018,8 +1036,8 @@ /** * Normalize the event based on API support - * @param {HTMLElement} el - * @param {string} eventName + * @param {HTMLElement} el + * @param {string} eventName * @returns {Event} A normalized event */ function normalizeEvent(el, eventName) { @@ -1046,7 +1064,7 @@ /** * Simulate the entry of a value into an element. * Clicks the element, focuses it, and then fires a keydown, keypress, and keyup event. - * @param {HTMLElement} el + * @param {HTMLElement} el */ function setValueForElement(el) { var valueToSet = el.value; @@ -1061,7 +1079,7 @@ /** * Simulate the entry of a value into an element by using events. * Dispatches a keydown, keypress, and keyup event, then fires the `input` and `change` events before removing focus. - * @param {HTMLElement} el + * @param {HTMLElement} el */ function setValueForElementByEvent(el) { var valueToSet = el.value, @@ -1081,7 +1099,7 @@ /** * Click on an element `el` - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {boolean} Returns true if the element was clicked and false if it was not able to be clicked */ function clickElement(el) { @@ -1116,7 +1134,7 @@ /** * Determine if we can apply styling to `el` to indicate that it was filled. - * @param {HTMLElement} el + * @param {HTMLElement} el * @returns {boolean} Returns true if we can see the element to apply styling. */ function canSeeElementToStyle(el) { @@ -1149,7 +1167,7 @@ /** * Find the element for the given `opid`. - * @param {number} theOpId + * @param {number} theOpId * @returns {HTMLElement} The element for the given `opid`, or `null` if not found. */ function getElementByOpId(theOpId) { @@ -1159,7 +1177,7 @@ } try { // START MODIFICATION - var elements = Array.prototype.slice.call(selectAllFromDoc('input, select, button, ' + + var elements = Array.prototype.slice.call(selectAllFromDoc('input, select, button, textarea, ' + 'span[data-bwautofill]')); // END MODIFICATION var filteredElements = elements.filter(function (o) { @@ -1181,8 +1199,8 @@ /** * Helper for doc.querySelectorAll - * @param {string} theSelector - * @returns + * @param {string} theSelector + * @returns */ function selectAllFromDoc(theSelector) { var d = document, elements = []; @@ -1194,7 +1212,7 @@ /** * Focus an element and optionally re-set its value after focusing - * @param {HTMLElement} el + * @param {HTMLElement} el * @param {boolean} setValue Re-set the value after focusing */ function doFocusElement(el, setValue) { diff --git a/apps/browser/src/autofill/content/autofillv2.ts b/apps/browser/src/autofill/content/autofillv2.ts new file mode 100644 index 00000000000..8bf16ff879c --- /dev/null +++ b/apps/browser/src/autofill/content/autofillv2.ts @@ -0,0 +1,1391 @@ +/* eslint-disable no-var, no-console, no-prototype-builtins */ +// These eslint rules are disabled because the original JS was not written with them in mind and we don't want to fix +// them all now + +/* + 1Password Extension + + Lovingly handcrafted by Dave Teare, Michael Fey, Rad Azzouz, and Roustem Karimov. + Copyright (c) 2014 AgileBits. All rights reserved. + + ================================================================================ + + Copyright (c) 2014 AgileBits Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +/* + MODIFICATIONS FROM ORIGINAL + + 1. Populate isFirefox + 2. Remove isChrome and isSafari since they are not used. + 3. Unminify and format to meet Mozilla review requirements. + 4. Remove unnecessary input types from getFormElements query selector and limit number of elements returned. + 5. Remove fakeTested prop. + 6. Rename com.agilebits.* stuff to com.bitwarden.* + 7. Remove "some useful globals" on window + 8. Add ability to autofill span[data-bwautofill] elements + 9. Add new handler, for new command that responds with page details in response callback + 10. Handle sandbox iframe and sandbox rule in CSP + 11. Work on array of saved urls instead of just one to determine if we should autofill non-https sites + 12. Remove setting of attribute com.browser.browser.userEdited on user-inputs + 13. Handle null value URLs in urlNotSecure + 14. Convert to Typescript, add typings and remove dead code (not marked with START/END MODIFICATION) + */ +import AutofillForm from "../models/autofill-form"; +import AutofillPageDetails from "../models/autofill-page-details"; +import AutofillScript, { + AutofillScriptOptions, + FillScript, + FillScriptOp, +} from "../models/autofill-script"; + +/** + * The Document with additional custom properties added by this script + */ +type AutofillDocument = Document & { + elementsByOPID: Record; + elementForOPID: (opId: string) => Element; +}; + +/** + * A HTMLElement (usually a form element) with additional custom properties added by this script + */ +type ElementWithOpId = T & { + opid: string; +}; + +/** + * This script's definition of a Form Element (only a subset of HTML form elements) + * This is defined by getFormElements + */ +type FormElement = HTMLInputElement | HTMLSelectElement | HTMLSpanElement; + +/** + * A Form Element that we can set a value on (fill) + */ +type FillableControl = HTMLInputElement | HTMLSelectElement; + +function collect(document: Document) { + // START MODIFICATION + var isFirefox = + navigator.userAgent.indexOf("Firefox") !== -1 || navigator.userAgent.indexOf("Gecko/") !== -1; + // END MODIFICATION + + (document as AutofillDocument).elementsByOPID = {}; + + function getPageDetails(theDoc: Document, oneShotId: string) { + // start helpers + + /** + * For a given element `el`, returns the value of the attribute `attrName`. + * @param {HTMLElement} el + * @param {string} attrName + * @returns {string} The value of the attribute + */ + function getElementAttrValue(el: any, attrName: string) { + var attrVal = el[attrName]; + if ("string" == typeof attrVal) { + return attrVal; + } + attrVal = el.getAttribute(attrName); + return "string" == typeof attrVal ? attrVal : null; + } + + /** + * Returns the value of the given element. + * @param {HTMLElement} el + * @returns {any} Value of the element + */ + function getElementValue(el: any) { + switch (toLowerString(el.type)) { + case "checkbox": + return el.checked ? "✓" : ""; + + case "hidden": + el = el.value; + if (!el || "number" != typeof el.length) { + return ""; + } + 254 < el.length && (el = el.substr(0, 254) + "...SNIPPED"); + return el; + + default: + // START MODIFICATION + if (!el.type && el.tagName.toLowerCase() === "span") { + return el.innerText; + } + // END MODIFICATION + return el.value; + } + } + + /** + * If `el` is a ` - - - {{ - helperText - }} - diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/components/input-checkbox.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/components/input-checkbox.component.ts deleted file mode 100644 index b494c6c817a..00000000000 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/components/input-checkbox.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from "@angular/core"; - -import { BaseCvaComponent } from "./base-cva.component"; - -/** For use in the SSO Config Form only - will be deprecated by the Component Library */ -@Component({ - selector: "app-input-checkbox", - templateUrl: "input-checkbox.component.html", -}) -export class InputCheckboxComponent extends BaseCvaComponent {} diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations.module.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations.module.ts index 6cbd6ad663b..08f7dea6406 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations.module.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/organizations.module.ts @@ -4,7 +4,6 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module"; import { SsoComponent } from "../../auth/sso/sso.component"; -import { InputCheckboxComponent } from "./components/input-checkbox.component"; import { DomainAddEditDialogComponent } from "./manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component"; import { DomainVerificationComponent } from "./manage/domain-verification/domain-verification.component"; import { ScimComponent } from "./manage/scim.component"; @@ -13,7 +12,6 @@ import { OrganizationsRoutingModule } from "./organizations-routing.module"; @NgModule({ imports: [SharedModule, OrganizationsRoutingModule], declarations: [ - InputCheckboxComponent, SsoComponent, ScimComponent, DomainVerificationComponent, diff --git a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html index 7c24e0686da..0f68788acd5 100644 --- a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html +++ b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.html @@ -8,32 +8,24 @@ title="{{ 'loading' | i18n }}" aria-hidden="true" > - {{ "loading" | i18n }} + {{ "loading" | i18n }} -
+

{{ "ssoPolicyHelpStart" | i18n }} {{ "ssoPolicyHelpLink" | i18n }} {{ "ssoPolicyHelpEnd" | i18n }}
- {{ "ssoPolicyHelpKeyConnector" | i18n }}

- + + {{ "allowSso" | i18n }} + + {{ "allowSsoDesc" | i18n }} + {{ "ssoIdentifier" | i18n }} @@ -43,31 +35,25 @@
-
- -
- - -
-
- - -
-
+ + + {{ "memberDecryptionKeyConnectorDescStart" | i18n }} + {{ "memberDecryptionKeyConnectorDescLink" | i18n }} + {{ "memberDecryptionKeyConnectorDescEnd" | i18n }} + + + + + + {{ "trustedDeviceEncryption" | i18n }} + + + {{ "memberDecryptionTdeDescStart" | i18n }} + {{ "memberDecryptionTdeDescLink" | i18n }} + {{ "memberDecryptionTdeDescEnd" | i18n }} + + + - + {{ "keyConnectorWarning" | i18n }} @@ -205,11 +216,15 @@
- + + {{ "getClaimsFromUserInfoEndpoint" | i18n }} + +
- + + {{ "spWantAssertionsSigned" | i18n }} + + - + + {{ "spValidateCertificates" | i18n }} + +
@@ -462,21 +485,29 @@ [label]="'idpAllowUnsolicitedAuthnResponse' | i18n" > --> - + + {{ "idpAllowOutboundLogoutRequests" | i18n }} + + - + + {{ "idpSignAuthenticationRequests" | i18n }} + + - diff --git a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts index 56619a08fbb..f6646bd2f97 100644 --- a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts +++ b/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts @@ -12,12 +12,14 @@ import { concatMap, Subject, takeUntil } from "rxjs"; import { SelectOptions } from "@bitwarden/angular/interfaces/selectOptions"; import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { + MemberDecryptionType, OpenIdConnectRedirectBehavior, Saml2BindingType, Saml2NameIdFormat, @@ -28,6 +30,7 @@ import { SsoConfigApi } from "@bitwarden/common/auth/models/api/sso-config.api"; import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request"; import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response"; import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { Utils } from "@bitwarden/common/misc/utils"; import { ssoTypeValidator } from "./sso-type.validator"; @@ -40,6 +43,7 @@ const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha2 }) export class SsoComponent implements OnInit, OnDestroy { readonly ssoType = SsoType; + readonly memberDecryptionType = MemberDecryptionType; readonly ssoTypeOptions: SelectOptions[] = [ { name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true }, @@ -83,6 +87,8 @@ export class SsoComponent implements OnInit, OnDestroy { ]; private destroy$ = new Subject(); + showTdeOptions = false; + showKeyConnectorOptions = false; showOpenIdCustomizations = false; @@ -90,7 +96,6 @@ export class SsoComponent implements OnInit, OnDestroy { haveTestedKeyConnector = false; organizationId: string; organization: Organization; - formPromise: Promise; callbackPath: string; signedOutCallbackPath: string; @@ -147,7 +152,7 @@ export class SsoComponent implements OnInit, OnDestroy { protected ssoConfigForm = this.formBuilder.group>({ configType: new FormControl(SsoType.None), - keyConnectorEnabled: new FormControl(false), + memberDecryptionType: new FormControl(MemberDecryptionType.MasterPassword), keyConnectorUrl: new FormControl(""), openId: this.openIdForm, saml: this.samlForm, @@ -174,7 +179,8 @@ export class SsoComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private organizationService: OrganizationService, - private organizationApiService: OrganizationApiServiceAbstraction + private organizationApiService: OrganizationApiServiceAbstraction, + private configService: ConfigServiceAbstraction ) {} async ngOnInit() { @@ -223,6 +229,15 @@ export class SsoComponent implements OnInit, OnDestroy { takeUntil(this.destroy$) ) .subscribe(); + + const tdeFeatureFlag = await this.configService.getFeatureFlagBool( + FeatureFlag.TrustedDeviceEncryption + ); + + this.showTdeOptions = tdeFeatureFlag && !this.platformUtilsService.isSelfHost(); + // If the tde flag is not enabled, continue showing the key connector options to keep the UI the same + // Once the flag is removed, we can rely on the platformUtilsService.isSelfHost() check alone + this.showKeyConnectorOptions = !tdeFeatureFlag || this.platformUtilsService.isSelfHost(); } ngOnDestroy(): void { @@ -244,10 +259,10 @@ export class SsoComponent implements OnInit, OnDestroy { this.loading = false; } - async submit() { + submit = async () => { this.updateFormValidationState(this.ssoConfigForm); - if (this.ssoConfigForm.value.keyConnectorEnabled) { + if (this.ssoConfigForm.value.memberDecryptionType === MemberDecryptionType.KeyConnector) { this.haveTestedKeyConnector = false; await this.validateKeyConnectorUrl(); } @@ -262,18 +277,11 @@ export class SsoComponent implements OnInit, OnDestroy { request.identifier = this.ssoIdentifierCtrl.value === "" ? null : this.ssoIdentifierCtrl.value; request.data = SsoConfigApi.fromView(this.ssoConfigForm.getRawValue()); - this.formPromise = this.organizationApiService.updateSso(this.organizationId, request); + const response = await this.organizationApiService.updateSso(this.organizationId, request); + this.populateForm(response); - try { - const response = await this.formPromise; - this.populateForm(response); - this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved")); - } catch { - // Logged by appApiAction, do nothing - } - - this.formPromise = null; - } + this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved")); + }; async validateKeyConnectorUrl() { if (this.haveTestedKeyConnector) { @@ -313,7 +321,7 @@ export class SsoComponent implements OnInit, OnDestroy { get enableTestKeyConnector() { return ( - this.ssoConfigForm.get("keyConnectorEnabled").value && + this.ssoConfigForm.value?.memberDecryptionType === MemberDecryptionType.KeyConnector && !Utils.isNullOrWhitespace(this.keyConnectorUrl?.value) ); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets-manager.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets-manager.module.ts index 754eca24537..1468af322ae 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets-manager.module.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets-manager.module.ts @@ -5,10 +5,8 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { LayoutModule } from "./layout/layout.module"; import { SecretsManagerSharedModule } from "./shared/sm-shared.module"; import { SecretsManagerRoutingModule } from "./sm-routing.module"; -import { SMGuard } from "./sm.guard"; @NgModule({ imports: [SharedModule, SecretsManagerSharedModule, SecretsManagerRoutingModule, LayoutModule], - providers: [SMGuard], }) export class SecretsManagerModule {} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index 0e374719708..64b75511ff4 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -68,6 +68,9 @@ export class SecretDialogComponent implements OnInit { throw new Error(`The secret dialog was not called with the appropriate operation values.`); } else if (this.data.operation == OperationType.Add) { await this.loadProjects(true); + if (this.data.projectId == null || this.data.projectId == "") { + this.addNewProjectOptionToProjectsDropDown(); + } } if (this.data.projectId) { @@ -78,10 +81,6 @@ export class SecretDialogComponent implements OnInit { this.formGroup.get("project").removeValidators(Validators.required); this.formGroup.get("project").updateValueAndValidity(); } - - if (this.data.projectId == null || this.data.projectId == "") { - this.addNewProjectOptionToProjectsDropDown(); - } } async loadData() { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html index efcb70a0ef9..632e10c8c7f 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.html @@ -87,7 +87,9 @@ diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts index 1ea2f6ad35c..d0cb891a493 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts @@ -3,6 +3,8 @@ import { ActivatedRoute } from "@angular/router"; import { combineLatestWith, firstValueFrom, Observable, startWith, switchMap } from "rxjs"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { openUserVerificationPrompt } from "@bitwarden/web-vault/app/auth/shared/components/user-verification"; @@ -25,7 +27,9 @@ export class AccessTokenComponent implements OnInit { private route: ActivatedRoute, private accessService: AccessService, private dialogService: DialogServiceAbstraction, - private platformUtilsService: PlatformUtilsService + private modalService: ModalService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService ) {} ngOnInit() { @@ -41,6 +45,15 @@ export class AccessTokenComponent implements OnInit { } protected async revoke(tokens: AccessTokenView[]) { + if (!tokens?.length) { + this.platformUtilsService.showToast( + "error", + null, + this.i18nService.t("noAccessTokenSelected") + ); + return; + } + if (!(await this.verifyUser())) { return; } @@ -50,7 +63,7 @@ export class AccessTokenComponent implements OnInit { tokens.map((t) => t.id) ); - this.platformUtilsService.showToast("success", null, "Access tokens revoked."); + this.platformUtilsService.showToast("success", null, this.i18nService.t("accessTokenRevoked")); } protected openNewAccessTokenDialog() { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts index 236881c20b9..f279314b43e 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts @@ -13,59 +13,70 @@ import { ProjectsModule } from "./projects/projects.module"; import { SecretsModule } from "./secrets/secrets.module"; import { ServiceAccountsModule } from "./service-accounts/service-accounts.module"; import { SettingsModule } from "./settings/settings.module"; -import { SMGuard } from "./sm.guard"; +import { canActivateSM } from "./sm.guard"; import { TrashModule } from "./trash/trash.module"; const routes: Routes = [ buildFlaggedRoute("secretsManager", { - path: ":organizationId", - component: LayoutComponent, - canActivate: [AuthGuard, OrganizationPermissionsGuard, SMGuard], - data: { - organizationPermissions: (org: Organization) => org.canAccessSecretsManager, - }, + path: "", children: [ { path: "", - component: NavigationComponent, - outlet: "sidebar", - }, - { - path: "secrets", - loadChildren: () => SecretsModule, - data: { - titleId: "secrets", - }, - }, - { - path: "projects", - loadChildren: () => ProjectsModule, - data: { - titleId: "projects", - }, - }, - { - path: "service-accounts", - loadChildren: () => ServiceAccountsModule, - data: { - titleId: "serviceAccounts", - }, - }, - { - path: "trash", - loadChildren: () => TrashModule, - data: { - titleId: "trash", - }, - }, - { - path: "settings", - loadChildren: () => SettingsModule, - }, - { - path: "", - loadChildren: () => OverviewModule, + canActivate: [canActivateSM], pathMatch: "full", + children: [], + }, + { + path: ":organizationId", + component: LayoutComponent, + canActivate: [AuthGuard, OrganizationPermissionsGuard], + data: { + organizationPermissions: (org: Organization) => org.canAccessSecretsManager, + }, + children: [ + { + path: "", + component: NavigationComponent, + outlet: "sidebar", + }, + { + path: "secrets", + loadChildren: () => SecretsModule, + data: { + titleId: "secrets", + }, + }, + { + path: "projects", + loadChildren: () => ProjectsModule, + data: { + titleId: "projects", + }, + }, + { + path: "service-accounts", + loadChildren: () => ServiceAccountsModule, + data: { + titleId: "serviceAccounts", + }, + }, + { + path: "trash", + loadChildren: () => TrashModule, + data: { + titleId: "trash", + }, + }, + { + path: "settings", + loadChildren: () => SettingsModule, + }, + { + path: "", + loadChildren: () => OverviewModule, + pathMatch: "full", + }, + ], }, ], }), diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts b/bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts index b937c3ec07f..340f0484f7b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts @@ -1,10 +1,42 @@ -import { Injectable } from "@angular/core"; -import { ActivatedRouteSnapshot, CanActivate } from "@angular/router"; +import { inject } from "@angular/core"; +import { + ActivatedRouteSnapshot, + CanActivateFn, + createUrlTreeFromSnapshot, + RouterStateSnapshot, +} from "@angular/router"; -@Injectable() -export class SMGuard implements CanActivate { - async canActivate(route: ActivatedRouteSnapshot) { - // TODO: Verify org - return true; +import { AuthGuard } from "@bitwarden/angular/auth/guards/auth.guard"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; + +/** + * Redirects from root `/sm` to first organization with access to SM + */ +export const canActivateSM: CanActivateFn = async ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot +) => { + const syncService = inject(SyncService); + const authService = inject(AuthService); + const orgService = inject(OrganizationService); + const authGuard = inject(AuthGuard); + + /** Workaround to avoid service initialization race condition. */ + if ((await syncService.getLastSync()) == null) { + await syncService.fullSync(false); } -} + + if ((await authService.getAuthStatus()) !== AuthenticationStatus.Unlocked) { + return authGuard.canActivate(route, state); + } + + const orgs = await orgService.getAll(); + const smOrg = orgs.find((o) => o.canAccessSecretsManager); + if (smOrg) { + return createUrlTreeFromSnapshot(route, ["/sm", smOrg.id]); + } + return createUrlTreeFromSnapshot(route, ["/vault"]); +}; diff --git a/jest.config.js b/jest.config.js index ec58fe0c019..8b54a826583 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,12 +2,14 @@ const { pathsToModuleNameMapper } = require("ts-jest"); const { compilerOptions } = require("./tsconfig"); +/** @type {import('jest').Config} */ module.exports = { reporters: ["default", "jest-junit"], collectCoverage: true, coverageReporters: ["html", "lcov"], coverageDirectory: "coverage", + moduleNameMapper: pathsToModuleNameMapper(compilerOptions?.paths || {}, { prefix: "/", }), @@ -30,13 +32,4 @@ module.exports = { // https://github.com/facebook/jest/issues/9430#issuecomment-1149882002 // Also anecdotally improves performance when run locally maxWorkers: 3, - globals: { - "ts-jest": { - // Further workaround for memory leak, recommended here: - // https://github.com/kulshekhar/ts-jest/issues/1967#issuecomment-697494014 - // Makes tests run faster and reduces size/rate of leak, but loses typechecking on test code - // See https://bitwarden.atlassian.net/browse/EC-497 for more info - isolatedModules: true, - }, - }, }; diff --git a/libs/angular/jest.config.js b/libs/angular/jest.config.js index 3be0f66db55..e294e4ff472 100644 --- a/libs/angular/jest.config.js +++ b/libs/angular/jest.config.js @@ -2,8 +2,9 @@ const { pathsToModuleNameMapper } = require("ts-jest"); const { compilerOptions } = require("../shared/tsconfig.libs"); -const sharedConfig = require("../../libs/shared/jest.config.base"); +const sharedConfig = require("../../libs/shared/jest.config.angular"); +/** @type {import('jest').Config} */ module.exports = { ...sharedConfig, displayName: "libs/angular tests", diff --git a/libs/angular/package.json b/libs/angular/package.json index 603826cb69b..322c8c826a2 100644 --- a/libs/angular/package.json +++ b/libs/angular/package.json @@ -13,7 +13,7 @@ }, "license": "GPL-3.0", "scripts": { - "clean": "rimraf dist/**/*", + "clean": "rimraf dist", "build": "npm run clean && tsc", "build:watch": "npm run clean && tsc -watch" } diff --git a/libs/angular/src/auth/components/environment-selector.component.html b/libs/angular/src/auth/components/environment-selector.component.html new file mode 100644 index 00000000000..e64d09da066 --- /dev/null +++ b/libs/angular/src/auth/components/environment-selector.component.html @@ -0,0 +1,87 @@ + + + +
+ +
+
diff --git a/libs/angular/src/auth/components/environment-selector.component.ts b/libs/angular/src/auth/components/environment-selector.component.ts new file mode 100644 index 00000000000..d35347c1479 --- /dev/null +++ b/libs/angular/src/auth/components/environment-selector.component.ts @@ -0,0 +1,109 @@ +import { animate, state, style, transition, trigger } from "@angular/animations"; +import { ConnectedPosition } from "@angular/cdk/overlay"; +import { Component, EventEmitter, OnDestroy, OnInit, Output } from "@angular/core"; +import { Router } from "@angular/router"; +import { Subject, takeUntil } from "rxjs"; + +import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction"; +import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; + +@Component({ + selector: "environment-selector", + templateUrl: "environment-selector.component.html", + animations: [ + trigger("transformPanel", [ + state( + "void", + style({ + opacity: 0, + }) + ), + transition( + "void => open", + animate( + "100ms linear", + style({ + opacity: 1, + }) + ) + ), + transition("* => void", animate("100ms linear", style({ opacity: 0 }))), + ]), + ], +}) +export class EnvironmentSelectorComponent implements OnInit, OnDestroy { + @Output() onOpenSelfHostedSettings = new EventEmitter(); + euServerFlagEnabled: boolean; + isOpen = false; + showingModal = false; + selectedEnvironment: ServerEnvironment; + ServerEnvironmentType = ServerEnvironment; + overlayPostition: ConnectedPosition[] = [ + { + originX: "start", + originY: "bottom", + overlayX: "start", + overlayY: "top", + }, + ]; + protected componentDestroyed$: Subject = new Subject(); + + constructor( + protected environmentService: EnvironmentService, + protected configService: ConfigServiceAbstraction, + protected router: Router + ) {} + + async ngOnInit() { + this.configService.serverConfig$.pipe(takeUntil(this.componentDestroyed$)).subscribe(() => { + this.updateEnvironmentInfo(); + }); + this.updateEnvironmentInfo(); + } + + ngOnDestroy(): void { + this.componentDestroyed$.next(); + this.componentDestroyed$.complete(); + } + + async toggle(option: ServerEnvironment) { + this.isOpen = !this.isOpen; + if (option === null) { + return; + } + if (option === ServerEnvironment.EU) { + await this.environmentService.setUrls({ base: "https://vault.bitwarden.eu" }); + } else if (option === ServerEnvironment.US) { + await this.environmentService.setUrls({ base: "https://vault.bitwarden.com" }); + } else if (option === ServerEnvironment.SelfHosted) { + this.onOpenSelfHostedSettings.emit(); + } + this.updateEnvironmentInfo(); + } + + async updateEnvironmentInfo() { + this.euServerFlagEnabled = await this.configService.getFeatureFlagBool( + FeatureFlag.DisplayEuEnvironmentFlag + ); + const webvaultUrl = this.environmentService.getWebVaultUrl(); + if (this.environmentService.isSelfHosted()) { + this.selectedEnvironment = ServerEnvironment.SelfHosted; + } else if (webvaultUrl != null && webvaultUrl.includes("bitwarden.eu")) { + this.selectedEnvironment = ServerEnvironment.EU; + } else { + this.selectedEnvironment = ServerEnvironment.US; + } + } + + close() { + this.isOpen = false; + this.updateEnvironmentInfo(); + } +} + +enum ServerEnvironment { + US = "US", + EU = "EU", + SelfHosted = "Self-hosted", +} diff --git a/libs/angular/src/auth/components/login.component.ts b/libs/angular/src/auth/components/login.component.ts index 0d7c594e695..e882d938946 100644 --- a/libs/angular/src/auth/components/login.component.ts +++ b/libs/angular/src/auth/components/login.component.ts @@ -3,9 +3,9 @@ import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { take } from "rxjs/operators"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AppIdService } from "@bitwarden/common/abstractions/appId.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { AllValidationErrors, @@ -55,7 +55,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit } constructor( - protected apiService: ApiService, + protected devicesApiService: DevicesApiServiceAbstraction, protected appIdService: AppIdService, protected authService: AuthService, protected router: Router, @@ -295,7 +295,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit async getLoginWithDevice(email: string) { try { const deviceIdentifier = await this.appIdService.getAppId(); - const res = await this.apiService.getKnownDevice(email, deviceIdentifier); + const res = await this.devicesApiService.getKnownDevice(email, deviceIdentifier); //ensure the application is not self-hosted this.showLoginWithDevice = res && !this.selfHosted; } catch (e) { diff --git a/libs/angular/src/auth/components/two-factor.component.ts b/libs/angular/src/auth/components/two-factor.component.ts index c7bf5075cd0..11b7c9bbac5 100644 --- a/libs/angular/src/auth/components/two-factor.component.ts +++ b/libs/angular/src/auth/components/two-factor.component.ts @@ -202,7 +202,9 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI } if (this.onSuccessfulLogin != null) { this.loginService.clearValues(); - await this.onSuccessfulLogin(); + // Note: awaiting this will currently cause a hang on desktop & browser as they will wait for a full sync to complete + // before nagivating to the success route. + this.onSuccessfulLogin(); } if (response.resetMasterPassword) { this.successRoute = "set-password"; diff --git a/libs/angular/src/components/environment.component.ts b/libs/angular/src/components/environment.component.ts index 347b5686e7b..f47fcf91247 100644 --- a/libs/angular/src/components/environment.component.ts +++ b/libs/angular/src/components/environment.component.ts @@ -4,6 +4,8 @@ import { EnvironmentService } from "@bitwarden/common/abstractions/environment.s import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { ModalService } from "../services/modal.service"; + @Directive() export class EnvironmentComponent { @Output() onSaved = new EventEmitter(); @@ -19,7 +21,8 @@ export class EnvironmentComponent { constructor( protected platformUtilsService: PlatformUtilsService, protected environmentService: EnvironmentService, - protected i18nService: I18nService + protected i18nService: I18nService, + private modalService: ModalService ) { const urls = this.environmentService.getUrls(); @@ -59,5 +62,6 @@ export class EnvironmentComponent { protected saved() { this.onSaved.emit(); + this.modalService.closeAll(); } } diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 79448cda470..4920485946a 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -10,6 +10,8 @@ import { ConfigApiServiceAbstraction } from "@bitwarden/common/abstractions/conf import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; +import { DeviceCryptoServiceAbstraction } from "@bitwarden/common/abstractions/device-crypto.service.abstraction"; +import { DevicesApiServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices-api.service.abstraction"; import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; @@ -90,6 +92,8 @@ import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service import { CryptoService } from "@bitwarden/common/services/crypto.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation"; +import { DeviceCryptoService } from "@bitwarden/common/services/device-crypto.service.implementation"; +import { DevicesApiServiceImplementation } from "@bitwarden/common/services/devices/devices-api.service.implementation"; import { EnvironmentService } from "@bitwarden/common/services/environment.service"; import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; @@ -351,6 +355,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; PlatformUtilsServiceAbstraction, LogService, StateServiceAbstraction, + AppIdServiceAbstraction, + DevicesApiServiceAbstraction, ], }, { @@ -615,7 +621,12 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; { provide: ConfigServiceAbstraction, useClass: ConfigService, - deps: [StateServiceAbstraction, ConfigApiServiceAbstraction, AuthServiceAbstraction], + deps: [ + StateServiceAbstraction, + ConfigApiServiceAbstraction, + AuthServiceAbstraction, + EnvironmentServiceAbstraction, + ], }, { provide: ConfigApiServiceAbstraction, @@ -651,6 +662,23 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction"; useClass: OrgDomainApiService, deps: [OrgDomainServiceAbstraction, ApiServiceAbstraction], }, + { + provide: DevicesApiServiceAbstraction, + useClass: DevicesApiServiceImplementation, + deps: [ApiServiceAbstraction], + }, + { + provide: DeviceCryptoServiceAbstraction, + useClass: DeviceCryptoService, + deps: [ + CryptoFunctionServiceAbstraction, + CryptoServiceAbstraction, + EncryptService, + StateServiceAbstraction, + AppIdServiceAbstraction, + DevicesApiServiceAbstraction, + ], + }, ], }) export class JslibServicesModule {} diff --git a/libs/angular/src/services/theming/theming.service.ts b/libs/angular/src/services/theming/theming.service.ts index 270cd92c4fe..fbae277cabb 100644 --- a/libs/angular/src/services/theming/theming.service.ts +++ b/libs/angular/src/services/theming/theming.service.ts @@ -63,7 +63,7 @@ export class ThemingService implements AbstractThemingService { protected monitorSystemThemeChanges(): void { fromEvent( - this.window.matchMedia("(prefers-color-scheme: dark)"), + window.matchMedia("(prefers-color-scheme: dark)"), "change" ).subscribe((event) => { this.updateSystemTheme(event.matches ? ThemeType.Dark : ThemeType.Light); diff --git a/libs/angular/src/tools/generator/components/password-generator-history.component.ts b/libs/angular/src/tools/generator/components/password-generator-history.component.ts index 3b86ef05e43..1c2e93928ab 100644 --- a/libs/angular/src/tools/generator/components/password-generator-history.component.ts +++ b/libs/angular/src/tools/generator/components/password-generator-history.component.ts @@ -22,10 +22,10 @@ export class PasswordGeneratorHistoryComponent implements OnInit { this.history = await this.passwordGenerationService.getHistory(); } - clear() { + clear = async () => { this.history = []; - this.passwordGenerationService.clear(); - } + await this.passwordGenerationService.clear(); + }; copy(password: string) { const copyOptions = this.win != null ? { window: this.win } : null; diff --git a/libs/angular/src/utils/form-selection-list.ts b/libs/angular/src/utils/form-selection-list.ts index 026ef367a4c..aa454e81e44 100644 --- a/libs/angular/src/utils/form-selection-list.ts +++ b/libs/angular/src/utils/form-selection-list.ts @@ -198,4 +198,18 @@ export class FormSelectionList< this.selectItem(selectedItem.id, selectedItem); } } + + /** + * Helper method to iterate over each "selected" form control and its corresponding item + * @param fn - The function to call for each form control and its corresponding item + */ + forEachControlItem( + fn: (control: AbstractControl, TControlValue>, value: TItem) => void + ) { + for (let i = 0; i < this.formArray.length; i++) { + // The selectedItems array and formArray are explicitly kept in sync, + // so we can safely assume the index of the form control and item are the same + fn(this.formArray.at(i), this.selectedItems[i]); + } + } } diff --git a/libs/common/jest.config.js b/libs/common/jest.config.js index 29309a78306..d7f78abbf38 100644 --- a/libs/common/jest.config.js +++ b/libs/common/jest.config.js @@ -2,8 +2,9 @@ const { pathsToModuleNameMapper } = require("ts-jest"); const { compilerOptions } = require("../shared/tsconfig.libs"); -const sharedConfig = require("../shared/jest.config.base"); +const sharedConfig = require("../shared/jest.config.ts"); +/** @type {import('jest').Config} */ module.exports = { ...sharedConfig, displayName: "libs/common tests", diff --git a/libs/common/package.json b/libs/common/package.json index 37e3821df1a..5e0f5ae20c6 100644 --- a/libs/common/package.json +++ b/libs/common/package.json @@ -13,7 +13,7 @@ }, "license": "GPL-3.0", "scripts": { - "clean": "rimraf dist/**/*", + "clean": "rimraf dist", "build": "npm run clean && tsc", "build:watch": "npm run clean && tsc -watch" } diff --git a/libs/common/spec/index.ts b/libs/common/spec/index.ts new file mode 100644 index 00000000000..494b31b521d --- /dev/null +++ b/libs/common/spec/index.ts @@ -0,0 +1,3 @@ +export * from "./utils"; +export * from "./intercept-console"; +export * from "./matchers"; diff --git a/libs/common/spec/shared/interceptConsole.ts b/libs/common/spec/intercept-console.ts similarity index 100% rename from libs/common/spec/shared/interceptConsole.ts rename to libs/common/spec/intercept-console.ts diff --git a/libs/common/spec/matchers/index.ts b/libs/common/spec/matchers/index.ts new file mode 100644 index 00000000000..59f6409fefa --- /dev/null +++ b/libs/common/spec/matchers/index.ts @@ -0,0 +1 @@ +export * from "./to-equal-buffer"; diff --git a/libs/common/spec/matchers/toEqualBuffer.spec.ts b/libs/common/spec/matchers/to-equal-buffer.spec.ts similarity index 100% rename from libs/common/spec/matchers/toEqualBuffer.spec.ts rename to libs/common/spec/matchers/to-equal-buffer.spec.ts diff --git a/libs/common/spec/matchers/toEqualBuffer.ts b/libs/common/spec/matchers/to-equal-buffer.ts similarity index 100% rename from libs/common/spec/matchers/toEqualBuffer.ts rename to libs/common/spec/matchers/to-equal-buffer.ts diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 5670a5dc369..2273b290191 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -361,7 +361,6 @@ export abstract class ApiService { putDeviceVerificationSettings: ( request: DeviceVerificationRequest ) => Promise; - getKnownDevice: (email: string, deviceIdentifier: string) => Promise; getEmergencyAccessTrusted: () => Promise>; getEmergencyAccessGranted: () => Promise>; diff --git a/libs/common/src/abstractions/broadcaster.service.ts b/libs/common/src/abstractions/broadcaster.service.ts index 9d080d03632..5df3c033433 100644 --- a/libs/common/src/abstractions/broadcaster.service.ts +++ b/libs/common/src/abstractions/broadcaster.service.ts @@ -2,8 +2,20 @@ export interface MessageBase { command: string; } +/** + * @deprecated Use the observable from the appropriate service instead. + */ export abstract class BroadcasterService { + /** + * @deprecated Use the observable from the appropriate service instead. + */ send: (message: MessageBase, id?: string) => void; + /** + * @deprecated Use the observable from the appropriate service instead. + */ subscribe: (id: string, messageCallback: (message: MessageBase) => void) => void; + /** + * @deprecated Use the observable from the appropriate service instead. + */ unsubscribe: (id: string) => void; } diff --git a/libs/common/src/abstractions/device-crypto.service.abstraction.ts b/libs/common/src/abstractions/device-crypto.service.abstraction.ts new file mode 100644 index 00000000000..23b3be967f5 --- /dev/null +++ b/libs/common/src/abstractions/device-crypto.service.abstraction.ts @@ -0,0 +1,8 @@ +import { DeviceKey } from "../models/domain/symmetric-crypto-key"; + +import { DeviceResponse } from "./devices/responses/device.response"; + +export abstract class DeviceCryptoServiceAbstraction { + trustDevice: () => Promise; + getDeviceKey: () => Promise; +} diff --git a/libs/common/src/abstractions/devices/devices-api.service.abstraction.ts b/libs/common/src/abstractions/devices/devices-api.service.abstraction.ts new file mode 100644 index 00000000000..345b728977e --- /dev/null +++ b/libs/common/src/abstractions/devices/devices-api.service.abstraction.ts @@ -0,0 +1,14 @@ +import { DeviceResponse } from "./responses/device.response"; + +export abstract class DevicesApiServiceAbstraction { + getKnownDevice: (email: string, deviceIdentifier: string) => Promise; + + getDeviceByIdentifier: (deviceIdentifier: string) => Promise; + + updateTrustedDeviceKeys: ( + deviceIdentifier: string, + devicePublicKeyEncryptedUserSymKey: string, + userSymKeyEncryptedDevicePublicKey: string, + deviceKeyEncryptedDevicePrivateKey: string + ) => Promise; +} diff --git a/libs/common/src/auth/models/response/device.response.ts b/libs/common/src/abstractions/devices/responses/device.response.ts similarity index 65% rename from libs/common/src/auth/models/response/device.response.ts rename to libs/common/src/abstractions/devices/responses/device.response.ts index 2770499e81e..331df2e16cd 100644 --- a/libs/common/src/auth/models/response/device.response.ts +++ b/libs/common/src/abstractions/devices/responses/device.response.ts @@ -7,6 +7,9 @@ export class DeviceResponse extends BaseResponse { identifier: string; type: DeviceType; creationDate: string; + encryptedUserKey: string; + encryptedPublicKey: string; + encryptedPrivateKey: string; constructor(response: any) { super(response); @@ -15,5 +18,8 @@ export class DeviceResponse extends BaseResponse { this.identifier = this.getResponseProperty("Identifier"); this.type = this.getResponseProperty("Type"); this.creationDate = this.getResponseProperty("CreationDate"); + this.encryptedUserKey = this.getResponseProperty("EncryptedUserKey"); + this.encryptedPublicKey = this.getResponseProperty("EncryptedPublicKey"); + this.encryptedPrivateKey = this.getResponseProperty("EncryptedPrivateKey"); } } diff --git a/libs/common/src/abstractions/state.service.ts b/libs/common/src/abstractions/state.service.ts index ac5bb705969..04cfb609fed 100644 --- a/libs/common/src/abstractions/state.service.ts +++ b/libs/common/src/abstractions/state.service.ts @@ -17,7 +17,7 @@ import { ServerConfigData } from "../models/data/server-config.data"; import { Account, AccountSettingsSettings } from "../models/domain/account"; import { EncString } from "../models/domain/enc-string"; import { StorageOptions } from "../models/domain/storage-options"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { DeviceKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { WindowState } from "../models/domain/window-state"; import { GeneratedPasswordHistory } from "../tools/generator/password"; import { SendData } from "../tools/send/models/data/send.data"; @@ -163,6 +163,8 @@ export abstract class StateService { setDontShowIdentitiesCurrentTab: (value: boolean, options?: StorageOptions) => Promise; getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise; setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise; + getDeviceKey: (options?: StorageOptions) => Promise; + setDeviceKey: (value: DeviceKey, options?: StorageOptions) => Promise; getEmail: (options?: StorageOptions) => Promise; setEmail: (value: string, options?: StorageOptions) => Promise; getEmailVerified: (options?: StorageOptions) => Promise; diff --git a/libs/common/spec/models/domain/collection.spec.ts b/libs/common/src/admin-console/models/domain/collection.spec.ts similarity index 86% rename from libs/common/spec/models/domain/collection.spec.ts rename to libs/common/src/admin-console/models/domain/collection.spec.ts index 6f3a5bcfec6..977a5e53cfa 100644 --- a/libs/common/spec/models/domain/collection.spec.ts +++ b/libs/common/src/admin-console/models/domain/collection.spec.ts @@ -1,7 +1,7 @@ -import { CollectionData } from "@bitwarden/common/admin-console/models/data/collection.data"; -import { Collection } from "@bitwarden/common/admin-console/models/domain/collection"; +import { mockEnc } from "../../../../spec"; +import { CollectionData } from "../data/collection.data"; -import { mockEnc } from "../../utils"; +import { Collection } from "./collection"; describe("Collection", () => { let data: CollectionData; diff --git a/libs/common/spec/services/organization/organization.service.spec.ts b/libs/common/src/admin-console/services/organization/organization.service.spec.ts similarity index 94% rename from libs/common/spec/services/organization/organization.service.spec.ts rename to libs/common/src/admin-console/services/organization/organization.service.spec.ts index df875d77487..3947df97071 100644 --- a/libs/common/spec/services/organization/organization.service.spec.ts +++ b/libs/common/src/admin-console/services/organization/organization.service.spec.ts @@ -1,9 +1,10 @@ import { MockProxy, mock, any, mockClear } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom } from "rxjs"; -import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; -import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service"; +import { StateService } from "../../../abstractions/state.service"; +import { OrganizationData } from "../../models/data/organization.data"; + +import { OrganizationService } from "./organization.service"; describe("Organization Service", () => { let organizationService: OrganizationService; diff --git a/libs/common/src/auth/enums/sso.ts b/libs/common/src/auth/enums/sso.ts index d9be7274bb9..0c86a27151f 100644 --- a/libs/common/src/auth/enums/sso.ts +++ b/libs/common/src/auth/enums/sso.ts @@ -4,6 +4,12 @@ export enum SsoType { Saml2 = 2, } +export enum MemberDecryptionType { + MasterPassword = 0, + KeyConnector = 1, + TrustedDeviceEncryption = 2, +} + export enum OpenIdConnectRedirectBehavior { RedirectGet = 0, FormPost = 1, diff --git a/libs/common/src/auth/models/api/sso-config.api.ts b/libs/common/src/auth/models/api/sso-config.api.ts index e681d2c27b9..98408712dcb 100644 --- a/libs/common/src/auth/models/api/sso-config.api.ts +++ b/libs/common/src/auth/models/api/sso-config.api.ts @@ -1,5 +1,6 @@ import { BaseResponse } from "../../../models/response/base.response"; import { + MemberDecryptionType, OpenIdConnectRedirectBehavior, Saml2BindingType, Saml2NameIdFormat, @@ -11,8 +12,8 @@ import { SsoConfigView } from "../view/sso-config.view"; export class SsoConfigApi extends BaseResponse { static fromView(view: SsoConfigView, api = new SsoConfigApi()) { api.configType = view.configType; + api.memberDecryptionType = view.memberDecryptionType; - api.keyConnectorEnabled = view.keyConnectorEnabled; api.keyConnectorUrl = view.keyConnectorUrl; if (api.configType === SsoType.OpenIdConnect) { @@ -52,8 +53,8 @@ export class SsoConfigApi extends BaseResponse { return api; } configType: SsoType; + memberDecryptionType: MemberDecryptionType; - keyConnectorEnabled: boolean; keyConnectorUrl: string; // OpenId @@ -95,8 +96,8 @@ export class SsoConfigApi extends BaseResponse { } this.configType = this.getResponseProperty("ConfigType"); + this.memberDecryptionType = this.getResponseProperty("MemberDecryptionType"); - this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled"); this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); this.authority = this.getResponseProperty("Authority"); diff --git a/libs/common/src/auth/models/view/sso-config.view.ts b/libs/common/src/auth/models/view/sso-config.view.ts index 073e48654d3..4830bad1a24 100644 --- a/libs/common/src/auth/models/view/sso-config.view.ts +++ b/libs/common/src/auth/models/view/sso-config.view.ts @@ -1,5 +1,6 @@ import { View } from "../../../models/view/view"; import { + MemberDecryptionType, OpenIdConnectRedirectBehavior, Saml2BindingType, Saml2NameIdFormat, @@ -14,7 +15,7 @@ export class SsoConfigView extends View { configType: SsoType; - keyConnectorEnabled: boolean; + memberDecryptionType: MemberDecryptionType; keyConnectorUrl: string; openId: { @@ -66,8 +67,8 @@ export class SsoConfigView extends View { } this.configType = orgSsoResponse.data.configType; + this.memberDecryptionType = orgSsoResponse.data.memberDecryptionType; - this.keyConnectorEnabled = orgSsoResponse.data.keyConnectorEnabled; this.keyConnectorUrl = orgSsoResponse.data.keyConnectorUrl; if (this.configType === SsoType.OpenIdConnect) { diff --git a/libs/common/src/billing/models/response/organization-subscription.response.ts b/libs/common/src/billing/models/response/organization-subscription.response.ts index 09c9f3a7586..35d66bba048 100644 --- a/libs/common/src/billing/models/response/organization-subscription.response.ts +++ b/libs/common/src/billing/models/response/organization-subscription.response.ts @@ -11,6 +11,7 @@ export class OrganizationSubscriptionResponse extends OrganizationResponse { subscription: BillingSubscriptionResponse; upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse; expiration: string; + expirationWithoutGracePeriod: string; constructor(response: any) { super(response); @@ -24,5 +25,6 @@ export class OrganizationSubscriptionResponse extends OrganizationResponse { ? null : new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice); this.expiration = this.getResponseProperty("Expiration"); + this.expirationWithoutGracePeriod = this.getResponseProperty("ExpirationWithoutGracePeriod"); } } diff --git a/libs/common/src/billing/models/view/self-hosted-organization-subscription.view.ts b/libs/common/src/billing/models/view/self-hosted-organization-subscription.view.ts new file mode 100644 index 00000000000..c1f5640207e --- /dev/null +++ b/libs/common/src/billing/models/view/self-hosted-organization-subscription.view.ts @@ -0,0 +1,61 @@ +import { View } from "../../../models/view/view"; +import { OrganizationSubscriptionResponse } from "../response/organization-subscription.response"; + +export class SelfHostedOrganizationSubscriptionView implements View { + planName: string; + + /** + * Date the subscription expires, including the grace period. + */ + expirationWithGracePeriod?: Date; + + /** + * Date the subscription expires, excluding the grace period. + * This will be `null` for older (< v12) license files because they do not include this date. + * In this case, you have to rely on the `expirationWithGracePeriod` instead. + */ + expirationWithoutGracePeriod?: Date; + + constructor(response: OrganizationSubscriptionResponse) { + if (response == null) { + return; + } + + this.planName = response.plan.name; + this.expirationWithGracePeriod = + response.expiration != null ? new Date(response.expiration) : null; + this.expirationWithoutGracePeriod = + response.expirationWithoutGracePeriod != null + ? new Date(response.expirationWithoutGracePeriod) + : null; + } + + /** + * The subscription has separate expiration dates for the subscription and the end of grace period. + */ + get hasSeparateGracePeriod() { + return this.expirationWithGracePeriod != null && this.expirationWithoutGracePeriod != null; + } + + /** + * True if the subscription has an expiration date. + */ + get hasExpiration() { + return this.expirationWithGracePeriod != null; + } + + /** + * True if the subscription has an expiration date that has past, but may still be within the grace period. + * For older licenses (< v12), this will always be false because they do not include the `expirationWithoutGracePeriod`. + */ + get isExpiredWithoutGracePeriod() { + return this.hasSeparateGracePeriod && this.expirationWithoutGracePeriod < new Date(); + } + + /** + * True if the subscription has an expiration date that has past, including the grace period. + */ + get isExpiredAndOutsideGracePeriod() { + return this.hasExpiration && this.expirationWithGracePeriod < new Date(); + } +} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 47d7dfcd6a2..e8a05911b9f 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -1,4 +1,5 @@ export enum FeatureFlag { DisplayEuEnvironmentFlag = "display-eu-environment", DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", + TrustedDeviceEncryption = "trusted-device-encryption", } diff --git a/libs/common/spec/misc/sequentialize.spec.ts b/libs/common/src/misc/sequentialize.spec.ts similarity index 98% rename from libs/common/spec/misc/sequentialize.spec.ts rename to libs/common/src/misc/sequentialize.spec.ts index 77f10afb9a8..1dc5f2b2c27 100644 --- a/libs/common/spec/misc/sequentialize.spec.ts +++ b/libs/common/src/misc/sequentialize.spec.ts @@ -1,4 +1,4 @@ -import { sequentialize } from "@bitwarden/common/misc/sequentialize"; +import { sequentialize } from "./sequentialize"; describe("sequentialize decorator", () => { it("should call the function once", async () => { diff --git a/libs/common/spec/misc/throttle.spec.ts b/libs/common/src/misc/throttle.spec.ts similarity index 95% rename from libs/common/spec/misc/throttle.spec.ts rename to libs/common/src/misc/throttle.spec.ts index 27572c6e04e..0947d4af664 100644 --- a/libs/common/spec/misc/throttle.spec.ts +++ b/libs/common/src/misc/throttle.spec.ts @@ -1,5 +1,5 @@ -import { sequentialize } from "@bitwarden/common/misc/sequentialize"; -import { throttle } from "@bitwarden/common/misc/throttle"; +import { sequentialize } from "./sequentialize"; +import { throttle } from "./throttle"; describe("throttle decorator", () => { it("should call the function once at a time", async () => { diff --git a/libs/common/spec/misc/utils.spec.ts b/libs/common/src/misc/utils.spec.ts similarity index 99% rename from libs/common/spec/misc/utils.spec.ts rename to libs/common/src/misc/utils.spec.ts index 52dbf6ad941..5f7d63fee22 100644 --- a/libs/common/spec/misc/utils.spec.ts +++ b/libs/common/src/misc/utils.spec.ts @@ -1,6 +1,6 @@ import * as path from "path"; -import { Utils } from "@bitwarden/common/misc/utils"; +import { Utils } from "./utils"; describe("Utils Service", () => { describe("getDomain", () => { diff --git a/libs/common/src/models/data/server-config.data.spec.ts b/libs/common/src/models/data/server-config.data.spec.ts index 1c4e890ab80..30dc46cf1bc 100644 --- a/libs/common/src/models/data/server-config.data.spec.ts +++ b/libs/common/src/models/data/server-config.data.spec.ts @@ -7,7 +7,7 @@ import { describe("ServerConfigData", () => { describe("fromJSON", () => { it("should create a ServerConfigData from a JSON object", () => { - const serverConfigData = ServerConfigData.fromJSON({ + const json = { version: "1.0.0", gitHash: "1234567890", server: { @@ -22,18 +22,11 @@ describe("ServerConfigData", () => { sso: "https://sso.com", }, utcDate: "2020-01-01T00:00:00.000Z", - }); + featureStates: { feature: "state" }, + }; + const serverConfigData = ServerConfigData.fromJSON(json); - expect(serverConfigData.version).toEqual("1.0.0"); - expect(serverConfigData.gitHash).toEqual("1234567890"); - expect(serverConfigData.server.name).toEqual("test"); - expect(serverConfigData.server.url).toEqual("https://test.com"); - expect(serverConfigData.environment.vault).toEqual("https://vault.com"); - expect(serverConfigData.environment.api).toEqual("https://api.com"); - expect(serverConfigData.environment.identity).toEqual("https://identity.com"); - expect(serverConfigData.environment.notifications).toEqual("https://notifications.com"); - expect(serverConfigData.environment.sso).toEqual("https://sso.com"); - expect(serverConfigData.utcDate).toEqual("2020-01-01T00:00:00.000Z"); + expect(serverConfigData).toEqual(json); }); it("should be an instance of ServerConfigData", () => { diff --git a/libs/common/src/models/domain/account-keys.spec.ts b/libs/common/src/models/domain/account-keys.spec.ts index 8dac20d4ebb..bacfac25a65 100644 --- a/libs/common/src/models/domain/account-keys.spec.ts +++ b/libs/common/src/models/domain/account-keys.spec.ts @@ -1,4 +1,4 @@ -import { makeStaticByteArray } from "../../../spec/utils"; +import { makeStaticByteArray } from "../../../spec"; import { Utils } from "../../misc/utils"; import { AccountKeys, EncryptionPair } from "./account"; diff --git a/libs/common/src/models/domain/account.ts b/libs/common/src/models/domain/account.ts index a7988d41d79..db98a17b42c 100644 --- a/libs/common/src/models/domain/account.ts +++ b/libs/common/src/models/domain/account.ts @@ -23,7 +23,7 @@ import { EventData } from "../data/event.data"; import { ServerConfigData } from "../data/server-config.data"; import { EncString } from "./enc-string"; -import { SymmetricCryptoKey } from "./symmetric-crypto-key"; +import { DeviceKey, SymmetricCryptoKey } from "./symmetric-crypto-key"; export class EncryptionPair { encrypted?: TEncrypted; @@ -107,6 +107,7 @@ export class AccountKeys { string, SymmetricCryptoKey >(); + deviceKey?: DeviceKey; organizationKeys?: EncryptionPair< { [orgId: string]: EncryptedOrganizationKeyData }, Record diff --git a/libs/common/spec/models/domain/encArrayBuffer.spec.ts b/libs/common/src/models/domain/enc-array-buffer.spec.ts similarity index 93% rename from libs/common/spec/models/domain/encArrayBuffer.spec.ts rename to libs/common/src/models/domain/enc-array-buffer.spec.ts index 03b02b0ee45..2a2052484fd 100644 --- a/libs/common/spec/models/domain/encArrayBuffer.spec.ts +++ b/libs/common/src/models/domain/enc-array-buffer.spec.ts @@ -1,7 +1,7 @@ -import { EncryptionType } from "@bitwarden/common/enums"; -import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer"; +import { makeStaticByteArray } from "../../../spec"; +import { EncryptionType } from "../../enums"; -import { makeStaticByteArray } from "../../utils"; +import { EncArrayBuffer } from "./enc-array-buffer"; describe("encArrayBuffer", () => { describe("parses the buffer", () => { diff --git a/libs/common/spec/models/domain/enc-string.spec.ts b/libs/common/src/models/domain/enc-string.spec.ts similarity index 93% rename from libs/common/spec/models/domain/enc-string.spec.ts rename to libs/common/src/models/domain/enc-string.spec.ts index 62e7434511c..08ff9c145d4 100644 --- a/libs/common/spec/models/domain/enc-string.spec.ts +++ b/libs/common/src/models/domain/enc-string.spec.ts @@ -2,12 +2,13 @@ import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { mock, MockProxy } from "jest-mock-extended"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { EncryptionType } from "@bitwarden/common/enums"; -import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { ContainerService } from "@bitwarden/common/services/container.service"; +import { CryptoService } from "../../abstractions/crypto.service"; +import { EncryptService } from "../../abstractions/encrypt.service"; +import { EncryptionType } from "../../enums"; +import { ContainerService } from "../../services/container.service"; + +import { EncString } from "./enc-string"; +import { SymmetricCryptoKey } from "./symmetric-crypto-key"; describe("EncString", () => { afterEach(() => { diff --git a/libs/common/spec/models/domain/symmetricCryptoKey.spec.ts b/libs/common/src/models/domain/symmetric-crypto-key.spec.ts similarity index 92% rename from libs/common/spec/models/domain/symmetricCryptoKey.spec.ts rename to libs/common/src/models/domain/symmetric-crypto-key.spec.ts index ddd1cb5b706..c371008bb36 100644 --- a/libs/common/spec/models/domain/symmetricCryptoKey.spec.ts +++ b/libs/common/src/models/domain/symmetric-crypto-key.spec.ts @@ -1,7 +1,7 @@ -import { EncryptionType } from "@bitwarden/common/enums"; -import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; +import { makeStaticByteArray } from "../../../spec"; +import { EncryptionType } from "../../enums"; -import { makeStaticByteArray } from "../../utils"; +import { SymmetricCryptoKey } from "./symmetric-crypto-key"; describe("SymmetricCryptoKey", () => { it("errors if no key", () => { diff --git a/libs/common/src/models/domain/symmetric-crypto-key.ts b/libs/common/src/models/domain/symmetric-crypto-key.ts index 3bd329dd21b..8c9920d1319 100644 --- a/libs/common/src/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/models/domain/symmetric-crypto-key.ts @@ -1,4 +1,4 @@ -import { Jsonify } from "type-fest"; +import { Jsonify, Opaque } from "type-fest"; import { EncryptionType } from "../../enums"; import { Utils } from "../../misc/utils"; @@ -75,3 +75,6 @@ export class SymmetricCryptoKey { return SymmetricCryptoKey.fromString(obj?.keyB64); } } + +// Setup all separate key types as opaque types +export type DeviceKey = Opaque; diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index e02bfb743fe..59e3755a04f 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -1110,14 +1110,6 @@ export class ApiService implements ApiServiceAbstraction { return new DeviceVerificationResponse(r); } - async getKnownDevice(email: string, deviceIdentifier: string): Promise { - const r = await this.send("GET", "/devices/knowndevice", null, false, true, null, (headers) => { - headers.set("X-Device-Identifier", deviceIdentifier); - headers.set("X-Request-Email", Utils.fromUtf8ToUrlB64(email)); - }); - return r as boolean; - } - // Emergency Access APIs async getEmergencyAccessTrusted(): Promise> { diff --git a/libs/common/src/services/config/config.service.ts b/libs/common/src/services/config/config.service.ts index 0dd6aa71d4a..dba5d1ca09c 100644 --- a/libs/common/src/services/config/config.service.ts +++ b/libs/common/src/services/config/config.service.ts @@ -1,33 +1,43 @@ -import { BehaviorSubject, concatMap, timer } from "rxjs"; +import { Injectable, OnDestroy } from "@angular/core"; +import { BehaviorSubject, Subject, concatMap, from, takeUntil, timer } from "rxjs"; import { ConfigApiServiceAbstraction } from "../../abstractions/config/config-api.service.abstraction"; import { ConfigServiceAbstraction } from "../../abstractions/config/config.service.abstraction"; import { ServerConfig } from "../../abstractions/config/server-config"; +import { EnvironmentService } from "../../abstractions/environment.service"; import { StateService } from "../../abstractions/state.service"; import { AuthService } from "../../auth/abstractions/auth.service"; import { AuthenticationStatus } from "../../auth/enums/authentication-status"; import { FeatureFlag } from "../../enums/feature-flag.enum"; import { ServerConfigData } from "../../models/data/server-config.data"; -export class ConfigService implements ConfigServiceAbstraction { +@Injectable() +export class ConfigService implements ConfigServiceAbstraction, OnDestroy { protected _serverConfig = new BehaviorSubject(null); serverConfig$ = this._serverConfig.asObservable(); + private destroy$ = new Subject(); constructor( private stateService: StateService, private configApiService: ConfigApiServiceAbstraction, - private authService: AuthService + private authService: AuthService, + private environmentService: EnvironmentService ) { // Re-fetch the server config every hour timer(0, 1000 * 3600) - .pipe( - concatMap(async () => { - return await this.fetchServerConfig(); - }) - ) + .pipe(concatMap(() => from(this.fetchServerConfig()))) .subscribe((serverConfig) => { this._serverConfig.next(serverConfig); }); + + this.environmentService.urls.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.fetchServerConfig(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); } async fetchServerConfig(): Promise { diff --git a/libs/common/spec/services/consoleLog.service.spec.ts b/libs/common/src/services/console-log.service.spec.ts similarity index 90% rename from libs/common/spec/services/consoleLog.service.spec.ts rename to libs/common/src/services/console-log.service.spec.ts index 3c961487f31..0b14ac559b1 100644 --- a/libs/common/spec/services/consoleLog.service.spec.ts +++ b/libs/common/src/services/console-log.service.spec.ts @@ -1,6 +1,6 @@ -import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; +import { interceptConsole, restoreConsole } from "../../spec"; -import { interceptConsole, restoreConsole } from "../shared/interceptConsole"; +import { ConsoleLogService } from "./consoleLog.service"; let caughtMessage: any; diff --git a/libs/common/spec/services/crypto.service.spec.ts b/libs/common/src/services/crypto.service.spec.ts similarity index 62% rename from libs/common/spec/services/crypto.service.spec.ts rename to libs/common/src/services/crypto.service.spec.ts index 64905008588..cc0d3ba212a 100644 --- a/libs/common/spec/services/crypto.service.spec.ts +++ b/libs/common/src/services/crypto.service.spec.ts @@ -1,11 +1,11 @@ import { mock, mockReset } from "jest-mock-extended"; -import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; -import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { CryptoService } from "@bitwarden/common/services/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { EncryptService } from "../abstractions/encrypt.service"; +import { LogService } from "../abstractions/log.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { StateService } from "../abstractions/state.service"; +import { CryptoService } from "../services/crypto.service"; describe("cryptoService", () => { let cryptoService: CryptoService; diff --git a/libs/common/src/services/device-crypto.service.implementation.ts b/libs/common/src/services/device-crypto.service.implementation.ts new file mode 100644 index 00000000000..ba50e300b42 --- /dev/null +++ b/libs/common/src/services/device-crypto.service.implementation.ts @@ -0,0 +1,85 @@ +import { AppIdService } from "../abstractions/appId.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { DeviceCryptoServiceAbstraction } from "../abstractions/device-crypto.service.abstraction"; +import { DevicesApiServiceAbstraction } from "../abstractions/devices/devices-api.service.abstraction"; +import { DeviceResponse } from "../abstractions/devices/responses/device.response"; +import { EncryptService } from "../abstractions/encrypt.service"; +import { StateService } from "../abstractions/state.service"; +import { DeviceKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { CsprngArray } from "../types/csprng"; + +export class DeviceCryptoService implements DeviceCryptoServiceAbstraction { + constructor( + protected cryptoFunctionService: CryptoFunctionService, + protected cryptoService: CryptoService, + protected encryptService: EncryptService, + protected stateService: StateService, + protected appIdService: AppIdService, + protected devicesApiService: DevicesApiServiceAbstraction + ) {} + + async trustDevice(): Promise { + // Attempt to get user symmetric key + const userSymKey: SymmetricCryptoKey = await this.cryptoService.getEncKey(); + + // If user symmetric key is not found, throw error + if (!userSymKey) { + throw new Error("User symmetric key not found"); + } + + // Generate deviceKey + const deviceKey = await this.makeDeviceKey(); + + // Generate asymmetric RSA key pair: devicePrivateKey, devicePublicKey + const [devicePublicKey, devicePrivateKey] = await this.cryptoFunctionService.rsaGenerateKeyPair( + 2048 + ); + + const [ + devicePublicKeyEncryptedUserSymKey, + userSymKeyEncryptedDevicePublicKey, + deviceKeyEncryptedDevicePrivateKey, + ] = await Promise.all([ + // Encrypt user symmetric key with the DevicePublicKey + this.cryptoService.rsaEncrypt(userSymKey.encKey, devicePublicKey), + + // Encrypt devicePublicKey with user symmetric key + this.encryptService.encrypt(devicePublicKey, userSymKey), + + // Encrypt devicePrivateKey with deviceKey + this.encryptService.encrypt(devicePrivateKey, deviceKey), + ]); + + // Send encrypted keys to server + const deviceIdentifier = await this.appIdService.getAppId(); + return this.devicesApiService.updateTrustedDeviceKeys( + deviceIdentifier, + devicePublicKeyEncryptedUserSymKey.encryptedString, + userSymKeyEncryptedDevicePublicKey.encryptedString, + deviceKeyEncryptedDevicePrivateKey.encryptedString + ); + } + + async getDeviceKey(): Promise { + // Check if device key is already stored + const existingDeviceKey = await this.stateService.getDeviceKey(); + + if (existingDeviceKey != null) { + return existingDeviceKey; + } else { + return this.makeDeviceKey(); + } + } + + private async makeDeviceKey(): Promise { + // Create 512-bit device key + const randomBytes: CsprngArray = await this.cryptoFunctionService.randomBytes(64); + const deviceKey = new SymmetricCryptoKey(randomBytes) as DeviceKey; + + // Save device key in secure storage + await this.stateService.setDeviceKey(deviceKey); + + return deviceKey; + } +} diff --git a/libs/common/src/services/device-crypto.service.spec.ts b/libs/common/src/services/device-crypto.service.spec.ts new file mode 100644 index 00000000000..7e14961cc2c --- /dev/null +++ b/libs/common/src/services/device-crypto.service.spec.ts @@ -0,0 +1,317 @@ +import { mock, mockReset } from "jest-mock-extended"; + +import { AppIdService } from "../abstractions/appId.service"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { DevicesApiServiceAbstraction } from "../abstractions/devices/devices-api.service.abstraction"; +import { DeviceResponse } from "../abstractions/devices/responses/device.response"; +import { EncryptService } from "../abstractions/encrypt.service"; +import { StateService } from "../abstractions/state.service"; +import { EncryptionType } from "../enums/encryption-type.enum"; +import { EncString } from "../models/domain/enc-string"; +import { DeviceKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { CryptoService } from "../services/crypto.service"; +import { CsprngArray } from "../types/csprng"; + +import { DeviceCryptoService } from "./device-crypto.service.implementation"; + +describe("deviceCryptoService", () => { + let deviceCryptoService: DeviceCryptoService; + + const cryptoFunctionService = mock(); + const cryptoService = mock(); + const encryptService = mock(); + const stateService = mock(); + const appIdService = mock(); + const devicesApiService = mock(); + + beforeEach(() => { + mockReset(cryptoFunctionService); + mockReset(encryptService); + mockReset(stateService); + mockReset(appIdService); + mockReset(devicesApiService); + + deviceCryptoService = new DeviceCryptoService( + cryptoFunctionService, + cryptoService, + encryptService, + stateService, + appIdService, + devicesApiService + ); + }); + + it("instantiates", () => { + expect(deviceCryptoService).not.toBeFalsy(); + }); + + describe("Trusted Device Encryption", () => { + const deviceKeyBytesLength = 64; + const userSymKeyBytesLength = 64; + + describe("getDeviceKey", () => { + let mockRandomBytes: CsprngArray; + let mockDeviceKey: SymmetricCryptoKey; + let existingDeviceKey: DeviceKey; + let stateSvcGetDeviceKeySpy: jest.SpyInstance; + let makeDeviceKeySpy: jest.SpyInstance; + + beforeEach(() => { + mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes); + existingDeviceKey = new SymmetricCryptoKey( + new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray + ) as DeviceKey; + + stateSvcGetDeviceKeySpy = jest.spyOn(stateService, "getDeviceKey"); + makeDeviceKeySpy = jest.spyOn(deviceCryptoService as any, "makeDeviceKey"); + }); + + it("gets a device key when there is not an existing device key", async () => { + stateSvcGetDeviceKeySpy.mockResolvedValue(null); + makeDeviceKeySpy.mockResolvedValue(mockDeviceKey); + + const deviceKey = await deviceCryptoService.getDeviceKey(); + + expect(stateSvcGetDeviceKeySpy).toHaveBeenCalledTimes(1); + expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1); + + expect(deviceKey).not.toBeNull(); + expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey); + expect(deviceKey).toEqual(mockDeviceKey); + }); + + it("returns the existing device key without creating a new one when there is an existing device key", async () => { + stateSvcGetDeviceKeySpy.mockResolvedValue(existingDeviceKey); + + const deviceKey = await deviceCryptoService.getDeviceKey(); + + expect(stateSvcGetDeviceKeySpy).toHaveBeenCalledTimes(1); + expect(makeDeviceKeySpy).not.toHaveBeenCalled(); + + expect(deviceKey).not.toBeNull(); + expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey); + expect(deviceKey).toEqual(existingDeviceKey); + }); + }); + + describe("makeDeviceKey", () => { + it("creates a new non-null 64 byte device key, securely stores it, and returns it", async () => { + const mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + + const cryptoFuncSvcRandomBytesSpy = jest + .spyOn(cryptoFunctionService, "randomBytes") + .mockResolvedValue(mockRandomBytes); + + const stateSvcSetDeviceKeySpy = jest.spyOn(stateService, "setDeviceKey"); + + // TypeScript will allow calling private methods if the object is of type 'any' + // This is a hacky workaround, but it allows for cleaner tests + const deviceKey = await (deviceCryptoService as any).makeDeviceKey(); + + expect(cryptoFuncSvcRandomBytesSpy).toHaveBeenCalledTimes(1); + expect(cryptoFuncSvcRandomBytesSpy).toHaveBeenCalledWith(deviceKeyBytesLength); + + expect(deviceKey).not.toBeNull(); + expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey); + + expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledTimes(1); + expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledWith(deviceKey); + }); + }); + + describe("trustDevice", () => { + let mockDeviceKeyRandomBytes: CsprngArray; + let mockDeviceKey: DeviceKey; + + let mockUserSymKeyRandomBytes: CsprngArray; + let mockUserSymKey: SymmetricCryptoKey; + + const deviceRsaKeyLength = 2048; + let mockDeviceRsaKeyPair: [ArrayBuffer, ArrayBuffer]; + let mockDevicePrivateKey: ArrayBuffer; + let mockDevicePublicKey: ArrayBuffer; + let mockDevicePublicKeyEncryptedUserSymKey: EncString; + let mockUserSymKeyEncryptedDevicePublicKey: EncString; + let mockDeviceKeyEncryptedDevicePrivateKey: EncString; + + const mockDeviceResponse: DeviceResponse = new DeviceResponse({ + Id: "mockId", + Name: "mockName", + Identifier: "mockIdentifier", + Type: "mockType", + CreationDate: "mockCreationDate", + }); + + const mockDeviceId = "mockDeviceId"; + + let makeDeviceKeySpy: jest.SpyInstance; + let rsaGenerateKeyPairSpy: jest.SpyInstance; + let cryptoSvcGetEncKeySpy: jest.SpyInstance; + let cryptoSvcRsaEncryptSpy: jest.SpyInstance; + let encryptServiceEncryptSpy: jest.SpyInstance; + let appIdServiceGetAppIdSpy: jest.SpyInstance; + let devicesApiServiceUpdateTrustedDeviceKeysSpy: jest.SpyInstance; + + beforeEach(() => { + // Setup all spies and default return values for the happy path + + mockDeviceKeyRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray; + mockDeviceKey = new SymmetricCryptoKey(mockDeviceKeyRandomBytes) as DeviceKey; + + mockUserSymKeyRandomBytes = new Uint8Array(userSymKeyBytesLength).buffer as CsprngArray; + mockUserSymKey = new SymmetricCryptoKey(mockUserSymKeyRandomBytes); + + mockDeviceRsaKeyPair = [ + new ArrayBuffer(deviceRsaKeyLength), + new ArrayBuffer(deviceRsaKeyLength), + ]; + + mockDevicePublicKey = mockDeviceRsaKeyPair[0]; + mockDevicePrivateKey = mockDeviceRsaKeyPair[1]; + + mockDevicePublicKeyEncryptedUserSymKey = new EncString( + EncryptionType.Rsa2048_OaepSha1_B64, + "mockDevicePublicKeyEncryptedUserSymKey" + ); + + mockUserSymKeyEncryptedDevicePublicKey = new EncString( + EncryptionType.AesCbc256_HmacSha256_B64, + "mockUserSymKeyEncryptedDevicePublicKey" + ); + + mockDeviceKeyEncryptedDevicePrivateKey = new EncString( + EncryptionType.AesCbc256_HmacSha256_B64, + "mockDeviceKeyEncryptedDevicePrivateKey" + ); + + // TypeScript will allow calling private methods if the object is of type 'any' + makeDeviceKeySpy = jest + .spyOn(deviceCryptoService as any, "makeDeviceKey") + .mockResolvedValue(mockDeviceKey); + + rsaGenerateKeyPairSpy = jest + .spyOn(cryptoFunctionService, "rsaGenerateKeyPair") + .mockResolvedValue(mockDeviceRsaKeyPair); + + cryptoSvcGetEncKeySpy = jest + .spyOn(cryptoService, "getEncKey") + .mockResolvedValue(mockUserSymKey); + + cryptoSvcRsaEncryptSpy = jest + .spyOn(cryptoService, "rsaEncrypt") + .mockResolvedValue(mockDevicePublicKeyEncryptedUserSymKey); + + encryptServiceEncryptSpy = jest + .spyOn(encryptService, "encrypt") + .mockImplementation((plainValue, key) => { + if (plainValue === mockDevicePublicKey && key === mockUserSymKey) { + return Promise.resolve(mockUserSymKeyEncryptedDevicePublicKey); + } + if (plainValue === mockDevicePrivateKey && key === mockDeviceKey) { + return Promise.resolve(mockDeviceKeyEncryptedDevicePrivateKey); + } + }); + + appIdServiceGetAppIdSpy = jest + .spyOn(appIdService, "getAppId") + .mockResolvedValue(mockDeviceId); + + devicesApiServiceUpdateTrustedDeviceKeysSpy = jest + .spyOn(devicesApiService, "updateTrustedDeviceKeys") + .mockResolvedValue(mockDeviceResponse); + }); + + it("calls the required methods with the correct arguments and returns a DeviceResponse", async () => { + const response = await deviceCryptoService.trustDevice(); + + expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1); + expect(rsaGenerateKeyPairSpy).toHaveBeenCalledTimes(1); + expect(cryptoSvcGetEncKeySpy).toHaveBeenCalledTimes(1); + + expect(cryptoSvcRsaEncryptSpy).toHaveBeenCalledTimes(1); + expect(encryptServiceEncryptSpy).toHaveBeenCalledTimes(2); + + expect(appIdServiceGetAppIdSpy).toHaveBeenCalledTimes(1); + expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledTimes(1); + expect(devicesApiServiceUpdateTrustedDeviceKeysSpy).toHaveBeenCalledWith( + mockDeviceId, + mockDevicePublicKeyEncryptedUserSymKey.encryptedString, + mockUserSymKeyEncryptedDevicePublicKey.encryptedString, + mockDeviceKeyEncryptedDevicePrivateKey.encryptedString + ); + + expect(response).toBeInstanceOf(DeviceResponse); + expect(response).toEqual(mockDeviceResponse); + }); + + it("throws specific error if user symmetric key is not found", async () => { + // setup the spy to return null + cryptoSvcGetEncKeySpy.mockResolvedValue(null); + // check if the expected error is thrown + await expect(deviceCryptoService.trustDevice()).rejects.toThrow( + "User symmetric key not found" + ); + + // reset the spy + cryptoSvcGetEncKeySpy.mockReset(); + + // setup the spy to return undefined + cryptoSvcGetEncKeySpy.mockResolvedValue(undefined); + // check if the expected error is thrown + await expect(deviceCryptoService.trustDevice()).rejects.toThrow( + "User symmetric key not found" + ); + }); + + const methodsToTestForErrorsOrInvalidReturns = [ + { + method: "makeDeviceKey", + spy: () => makeDeviceKeySpy, + errorText: "makeDeviceKey error", + }, + { + method: "rsaGenerateKeyPair", + spy: () => rsaGenerateKeyPairSpy, + errorText: "rsaGenerateKeyPair error", + }, + { + method: "getEncKey", + spy: () => cryptoSvcGetEncKeySpy, + errorText: "getEncKey error", + }, + { + method: "rsaEncrypt", + spy: () => cryptoSvcRsaEncryptSpy, + errorText: "rsaEncrypt error", + }, + { + method: "encryptService.encrypt", + spy: () => encryptServiceEncryptSpy, + errorText: "encryptService.encrypt error", + }, + ]; + + describe.each(methodsToTestForErrorsOrInvalidReturns)( + "trustDevice error handling and invalid return testing", + ({ method, spy, errorText }) => { + // ensures that error propagation works correctly + it(`throws an error if ${method} fails`, async () => { + const methodSpy = spy(); + methodSpy.mockRejectedValue(new Error(errorText)); + await expect(deviceCryptoService.trustDevice()).rejects.toThrow(errorText); + }); + + test.each([null, undefined])( + `throws an error if ${method} returns %s`, + async (invalidValue) => { + const methodSpy = spy(); + methodSpy.mockResolvedValue(invalidValue); + await expect(deviceCryptoService.trustDevice()).rejects.toThrow(); + } + ); + } + ); + }); + }); +}); diff --git a/libs/common/src/services/devices/devices-api.service.implementation.ts b/libs/common/src/services/devices/devices-api.service.implementation.ts new file mode 100644 index 00000000000..aa0d0f0c297 --- /dev/null +++ b/libs/common/src/services/devices/devices-api.service.implementation.ts @@ -0,0 +1,64 @@ +import { DevicesApiServiceAbstraction } from "../../abstractions/devices/devices-api.service.abstraction"; +import { DeviceResponse } from "../../abstractions/devices/responses/device.response"; +import { Utils } from "../../misc/utils"; +import { ApiService } from "../api.service"; + +import { TrustedDeviceKeysRequest } from "./requests/trusted-device-keys.request"; + +export class DevicesApiServiceImplementation implements DevicesApiServiceAbstraction { + constructor(private apiService: ApiService) {} + + async getKnownDevice(email: string, deviceIdentifier: string): Promise { + const r = await this.apiService.send( + "GET", + "/devices/knowndevice", + null, + false, + true, + null, + (headers) => { + headers.set("X-Device-Identifier", deviceIdentifier); + headers.set("X-Request-Email", Utils.fromUtf8ToUrlB64(email)); + } + ); + return r as boolean; + } + + /** + * Get device by identifier + * @param deviceIdentifier - client generated id (not device id in DB) + */ + async getDeviceByIdentifier(deviceIdentifier: string): Promise { + const r = await this.apiService.send( + "GET", + `/devices/identifier/${deviceIdentifier}`, + null, + true, + true + ); + return new DeviceResponse(r); + } + + async updateTrustedDeviceKeys( + deviceIdentifier: string, + devicePublicKeyEncryptedUserSymKey: string, + userSymKeyEncryptedDevicePublicKey: string, + deviceKeyEncryptedDevicePrivateKey: string + ): Promise { + const request = new TrustedDeviceKeysRequest( + devicePublicKeyEncryptedUserSymKey, + userSymKeyEncryptedDevicePublicKey, + deviceKeyEncryptedDevicePrivateKey + ); + + const result = await this.apiService.send( + "PUT", + `/devices/${deviceIdentifier}/keys`, + request, + true, + true + ); + + return new DeviceResponse(result); + } +} diff --git a/libs/common/src/services/devices/requests/trusted-device-keys.request.ts b/libs/common/src/services/devices/requests/trusted-device-keys.request.ts new file mode 100644 index 00000000000..da89de975b0 --- /dev/null +++ b/libs/common/src/services/devices/requests/trusted-device-keys.request.ts @@ -0,0 +1,7 @@ +export class TrustedDeviceKeysRequest { + constructor( + public encryptedUserKey: string, + public encryptedPublicKey: string, + public encryptedPrivateKey: string + ) {} +} diff --git a/libs/common/spec/services/encrypt.service.spec.ts b/libs/common/src/services/encrypt.service.spec.ts similarity index 90% rename from libs/common/spec/services/encrypt.service.spec.ts rename to libs/common/src/services/encrypt.service.spec.ts index 712ec04ebe0..8df3c170bf6 100644 --- a/libs/common/spec/services/encrypt.service.spec.ts +++ b/libs/common/src/services/encrypt.service.spec.ts @@ -1,15 +1,15 @@ import { mockReset, mock } from "jest-mock-extended"; -import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; -import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { EncryptionType } from "@bitwarden/common/enums"; -import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer"; -import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; +import { makeStaticByteArray } from "../../spec"; +import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; +import { LogService } from "../abstractions/log.service"; +import { EncryptionType } from "../enums"; +import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; +import { EncString } from "../models/domain/enc-string"; +import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { CsprngArray } from "../types/csprng"; -import { CsprngArray } from "../../src/types/csprng"; -import { makeStaticByteArray } from "../utils"; +import { EncryptServiceImplementation } from "./cryptography/encrypt.service.implementation"; describe("EncryptService", () => { const cryptoFunctionService = mock(); diff --git a/libs/common/src/services/environment.service.ts b/libs/common/src/services/environment.service.ts index e093f0891af..2c6df478ebe 100644 --- a/libs/common/src/services/environment.service.ts +++ b/libs/common/src/services/environment.service.ts @@ -218,6 +218,8 @@ export class EnvironmentService implements EnvironmentServiceAbstraction { return ![ "http://vault.bitwarden.com", "https://vault.bitwarden.com", + "http://vault.bitwarden.eu", + "https://vault.bitwarden.eu", "http://vault.qa.bitwarden.pw", "https://vault.qa.bitwarden.pw", ].includes(this.getWebVaultUrl()); diff --git a/libs/common/spec/services/organization-domain/org-domain-api.service.spec.ts b/libs/common/src/services/organization-domain/org-domain-api.service.spec.ts similarity index 88% rename from libs/common/spec/services/organization-domain/org-domain-api.service.spec.ts rename to libs/common/src/services/organization-domain/org-domain-api.service.spec.ts index e4dc94728f6..7fa6ed7855a 100644 --- a/libs/common/spec/services/organization-domain/org-domain-api.service.spec.ts +++ b/libs/common/src/services/organization-domain/org-domain-api.service.spec.ts @@ -1,14 +1,15 @@ import { mock } from "jest-mock-extended"; import { lastValueFrom } from "rxjs"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { OrganizationDomainSsoDetailsResponse } from "@bitwarden/common/abstractions/organization-domain/responses/organization-domain-sso-details.response"; -import { OrganizationDomainResponse } from "@bitwarden/common/abstractions/organization-domain/responses/organization-domain.response"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { OrgDomainApiService } from "@bitwarden/common/services/organization-domain/org-domain-api.service"; -import { OrgDomainService } from "@bitwarden/common/services/organization-domain/org-domain.service"; -import { OrganizationDomainSsoDetailsRequest } from "@bitwarden/common/services/organization-domain/requests/organization-domain-sso-details.request"; +import { ApiService } from "../../abstractions/api.service"; +import { I18nService } from "../../abstractions/i18n.service"; +import { OrganizationDomainSsoDetailsResponse } from "../../abstractions/organization-domain/responses/organization-domain-sso-details.response"; +import { OrganizationDomainResponse } from "../../abstractions/organization-domain/responses/organization-domain.response"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; + +import { OrgDomainApiService } from "./org-domain-api.service"; +import { OrgDomainService } from "./org-domain.service"; +import { OrganizationDomainSsoDetailsRequest } from "./requests/organization-domain-sso-details.request"; const mockedGetAllByOrgIdResponse: any = { data: [ diff --git a/libs/common/spec/services/organization-domain/org-domain.service.spec.ts b/libs/common/src/services/organization-domain/org-domain.service.spec.ts similarity index 93% rename from libs/common/spec/services/organization-domain/org-domain.service.spec.ts rename to libs/common/src/services/organization-domain/org-domain.service.spec.ts index e4e61d4e227..3bc8ae770f1 100644 --- a/libs/common/spec/services/organization-domain/org-domain.service.spec.ts +++ b/libs/common/src/services/organization-domain/org-domain.service.spec.ts @@ -1,10 +1,11 @@ import { mock, mockReset } from "jest-mock-extended"; import { lastValueFrom } from "rxjs"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { OrganizationDomainResponse } from "@bitwarden/common/abstractions/organization-domain/responses/organization-domain.response"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { OrgDomainService } from "@bitwarden/common/services/organization-domain/org-domain.service"; +import { I18nService } from "../../abstractions/i18n.service"; +import { OrganizationDomainResponse } from "../../abstractions/organization-domain/responses/organization-domain.response"; +import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; + +import { OrgDomainService } from "./org-domain.service"; const mockedUnverifiedDomainServerResponse = { creationDate: "2022-12-13T23:16:43.7066667Z", diff --git a/libs/common/spec/services/policy.service.spec.ts b/libs/common/src/services/policy.service.spec.ts similarity index 88% rename from libs/common/spec/services/policy.service.spec.ts rename to libs/common/src/services/policy.service.spec.ts index 61ab3dcb073..b806a4502c4 100644 --- a/libs/common/spec/services/policy.service.spec.ts +++ b/libs/common/src/services/policy.service.spec.ts @@ -2,22 +2,23 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { BehaviorSubject, firstValueFrom } from "rxjs"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums"; -import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; -import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; -import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { ResetPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/reset-password-policy-options"; -import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; -import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service"; -import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { ContainerService } from "@bitwarden/common/services/container.service"; -import { StateService } from "@bitwarden/common/services/state.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { EncryptService } from "../abstractions/encrypt.service"; +import { OrganizationService } from "../admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationUserStatusType, PolicyType } from "../admin-console/enums"; +import { PermissionsApi } from "../admin-console/models/api/permissions.api"; +import { OrganizationData } from "../admin-console/models/data/organization.data"; +import { PolicyData } from "../admin-console/models/data/policy.data"; +import { MasterPasswordPolicyOptions } from "../admin-console/models/domain/master-password-policy-options"; +import { Organization } from "../admin-console/models/domain/organization"; +import { Policy } from "../admin-console/models/domain/policy"; +import { ResetPasswordPolicyOptions } from "../admin-console/models/domain/reset-password-policy-options"; +import { PolicyResponse } from "../admin-console/models/response/policy.response"; +import { PolicyService } from "../admin-console/services/policy/policy.service"; +import { ListResponse } from "../models/response/list.response"; + +import { ContainerService } from "./container.service"; +import { StateService } from "./state.service"; describe("PolicyService", () => { let policyService: PolicyService; diff --git a/libs/common/spec/services/settings.service.spec.ts b/libs/common/src/services/settings.service.spec.ts similarity index 86% rename from libs/common/spec/services/settings.service.spec.ts rename to libs/common/src/services/settings.service.spec.ts index 7ef088798e5..1f23252e243 100644 --- a/libs/common/spec/services/settings.service.spec.ts +++ b/libs/common/src/services/settings.service.spec.ts @@ -2,11 +2,12 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { BehaviorSubject, firstValueFrom } from "rxjs"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { ContainerService } from "@bitwarden/common/services/container.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; -import { StateService } from "@bitwarden/common/services/state.service"; +import { CryptoService } from "../abstractions/crypto.service"; +import { EncryptService } from "../abstractions/encrypt.service"; + +import { ContainerService } from "./container.service"; +import { SettingsService } from "./settings.service"; +import { StateService } from "./state.service"; describe("SettingsService", () => { let settingsService: SettingsService; diff --git a/libs/common/spec/services/stateMigration.service.spec.ts b/libs/common/src/services/state-migration.service.spec.ts similarity index 93% rename from libs/common/spec/services/stateMigration.service.spec.ts rename to libs/common/src/services/state-migration.service.spec.ts index f94fa58c461..13727c96dd6 100644 --- a/libs/common/spec/services/stateMigration.service.spec.ts +++ b/libs/common/src/services/state-migration.service.spec.ts @@ -2,12 +2,13 @@ import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { MockProxy, any, mock } from "jest-mock-extended"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; -import { StateVersion } from "@bitwarden/common/enums"; -import { StateFactory } from "@bitwarden/common/factories/stateFactory"; -import { Account } from "@bitwarden/common/models/domain/account"; -import { GlobalState } from "@bitwarden/common/models/domain/global-state"; -import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service"; +import { AbstractStorageService } from "../abstractions/storage.service"; +import { StateVersion } from "../enums"; +import { StateFactory } from "../factories/stateFactory"; +import { Account } from "../models/domain/account"; +import { GlobalState } from "../models/domain/global-state"; + +import { StateMigrationService } from "./stateMigration.service"; const userId = "USER_ID"; diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index 4a1592e733c..dc32c688e30 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -35,7 +35,7 @@ import { EncString } from "../models/domain/enc-string"; import { GlobalState } from "../models/domain/global-state"; import { State } from "../models/domain/state"; import { StorageOptions } from "../models/domain/storage-options"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { DeviceKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { WindowState } from "../models/domain/window-state"; import { GeneratedPasswordHistory } from "../tools/generator/password"; import { SendData } from "../tools/send/models/data/send.data"; @@ -1054,6 +1054,32 @@ export class StateService< : await this.secureStorageService.save(DDG_SHARED_KEY, value, options); } + async getDeviceKey(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()); + + if (options?.userId == null) { + return null; + } + + const account = await this.getAccount(options); + + return account?.keys?.deviceKey as DeviceKey; + } + + async setDeviceKey(value: DeviceKey, options?: StorageOptions): Promise { + options = this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()); + + if (options?.userId == null) { + return; + } + + const account = await this.getAccount(options); + + account.keys.deviceKey = value; + + await this.saveAccount(account, options); + } + async getEmail(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions())) @@ -2751,7 +2777,10 @@ export class StateService< // settings persist even on reset, and are not effected by this method protected resetAccount(account: TAccount) { - const persistentAccountInformation = { settings: account.settings }; + const persistentAccountInformation = { + settings: account.settings, + keys: { deviceKey: account.keys.deviceKey }, + }; return Object.assign(this.createAccount(), persistentAccountInformation); } @@ -2830,7 +2859,7 @@ export class StateService< return this.reconcileOptions(options, defaultOptions); } - private async saveSecureStorageKey( + protected async saveSecureStorageKey( key: string, value: T, options?: StorageOptions diff --git a/libs/common/spec/web/services/webCryptoFunction.service.spec.ts b/libs/common/src/services/web-crypto-function.service.spec.ts similarity index 98% rename from libs/common/spec/web/services/webCryptoFunction.service.spec.ts rename to libs/common/src/services/web-crypto-function.service.spec.ts index cc2dd952342..519ba2eddbd 100644 --- a/libs/common/spec/web/services/webCryptoFunction.service.spec.ts +++ b/libs/common/src/services/web-crypto-function.service.spec.ts @@ -1,10 +1,11 @@ // eslint-disable-next-line no-restricted-imports import { Substitute } from "@fluffy-spoon/substitute"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { Utils } from "@bitwarden/common/misc/utils"; -import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service"; +import { PlatformUtilsService } from "../abstractions/platformUtils.service"; +import { Utils } from "../misc/utils"; +import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; + +import { WebCryptoFunctionService } from "./webCryptoFunction.service"; const RsaPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + diff --git a/libs/common/src/tools/send/models/domain/send-access.spec.ts b/libs/common/src/tools/send/models/domain/send-access.spec.ts index 3935cdf67aa..8c59bc51eda 100644 --- a/libs/common/src/tools/send/models/domain/send-access.spec.ts +++ b/libs/common/src/tools/send/models/domain/send-access.spec.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-restricted-imports import { Substitute, Arg } from "@fluffy-spoon/substitute"; -import { mockEnc } from "../../../../../spec/utils"; +import { mockEnc } from "../../../../../spec"; import { SendType } from "../../enums/send-type"; import { SendAccessResponse } from "../response/send-access.response"; diff --git a/libs/common/src/tools/send/models/domain/send-file.spec.ts b/libs/common/src/tools/send/models/domain/send-file.spec.ts index 8e0381434c6..44a84cdc639 100644 --- a/libs/common/src/tools/send/models/domain/send-file.spec.ts +++ b/libs/common/src/tools/send/models/domain/send-file.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc } from "../../../../../spec/utils"; +import { mockEnc } from "../../../../../spec"; import { SendFileData } from "../data/send-file.data"; import { SendFile } from "./send-file"; diff --git a/libs/common/src/tools/send/models/domain/send-text.spec.ts b/libs/common/src/tools/send/models/domain/send-text.spec.ts index 7285447d8af..6af143ec594 100644 --- a/libs/common/src/tools/send/models/domain/send-text.spec.ts +++ b/libs/common/src/tools/send/models/domain/send-text.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc } from "../../../../../spec/utils"; +import { mockEnc } from "../../../../../spec"; import { SendTextData } from "../data/send-text.data"; import { SendText } from "./send-text"; diff --git a/libs/common/src/tools/send/models/domain/send.spec.ts b/libs/common/src/tools/send/models/domain/send.spec.ts index 4497551e04b..8bd832a501f 100644 --- a/libs/common/src/tools/send/models/domain/send.spec.ts +++ b/libs/common/src/tools/send/models/domain/send.spec.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-restricted-imports import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute"; -import { makeStaticByteArray, mockEnc } from "../../../../../spec/utils"; +import { makeStaticByteArray, mockEnc } from "../../../../../spec"; import { CryptoService } from "../../../../abstractions/crypto.service"; import { EncryptService } from "../../../../abstractions/encrypt.service"; import { EncString } from "../../../../models/domain/enc-string"; diff --git a/libs/common/src/types/csprng.d.ts b/libs/common/src/types/csprng.d.ts index b62f8b37a68..ec0a31a9f78 100644 --- a/libs/common/src/types/csprng.d.ts +++ b/libs/common/src/types/csprng.d.ts @@ -1,5 +1,9 @@ import { Opaque } from "type-fest"; +// You would typically use these types when you want to create a type that +// represents an array or string value generated from a +// cryptographic secure pseudorandom number generator (CSPRNG) + type CsprngArray = Opaque; type CsprngString = Opaque; diff --git a/libs/common/src/vault/models/domain/attachment.spec.ts b/libs/common/src/vault/models/domain/attachment.spec.ts index e061569f024..c1397ad3ca4 100644 --- a/libs/common/src/vault/models/domain/attachment.spec.ts +++ b/libs/common/src/vault/models/domain/attachment.spec.ts @@ -1,6 +1,6 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils"; +import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec"; import { CryptoService } from "../../../abstractions/crypto.service"; import { EncryptService } from "../../../abstractions/encrypt.service"; import { EncString } from "../../../models/domain/enc-string"; diff --git a/libs/common/src/vault/models/domain/card.spec.ts b/libs/common/src/vault/models/domain/card.spec.ts index a1556fefc51..a80d57a587f 100644 --- a/libs/common/src/vault/models/domain/card.spec.ts +++ b/libs/common/src/vault/models/domain/card.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { EncString } from "../../../models/domain/enc-string"; import { CardData } from "../../../vault/models/data/card.data"; import { Card } from "../../models/domain/card"; diff --git a/libs/common/src/vault/models/domain/cipher.spec.ts b/libs/common/src/vault/models/domain/cipher.spec.ts index c484af26d3c..b23b15e3ef7 100644 --- a/libs/common/src/vault/models/domain/cipher.spec.ts +++ b/libs/common/src/vault/models/domain/cipher.spec.ts @@ -2,7 +2,7 @@ import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { Jsonify } from "type-fest"; -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { FieldType, SecureNoteType, UriMatchType } from "../../../enums"; import { EncString } from "../../../models/domain/enc-string"; import { InitializerKey } from "../../../services/cryptography/initializer-key"; diff --git a/libs/common/src/vault/models/domain/field.spec.ts b/libs/common/src/vault/models/domain/field.spec.ts index 3f07f6905c3..0754c0b3b68 100644 --- a/libs/common/src/vault/models/domain/field.spec.ts +++ b/libs/common/src/vault/models/domain/field.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { FieldType } from "../../../enums"; import { EncString } from "../../../models/domain/enc-string"; import { FieldData } from "../../models/data/field.data"; diff --git a/libs/common/src/vault/models/domain/folder.spec.ts b/libs/common/src/vault/models/domain/folder.spec.ts index cc08518a90a..dc8b490e72a 100644 --- a/libs/common/src/vault/models/domain/folder.spec.ts +++ b/libs/common/src/vault/models/domain/folder.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { EncString } from "../../../models/domain/enc-string"; import { FolderData } from "../../models/data/folder.data"; import { Folder } from "../../models/domain/folder"; diff --git a/libs/common/src/vault/models/domain/identity.spec.ts b/libs/common/src/vault/models/domain/identity.spec.ts index 8b53ec016ff..a3fdbd580bb 100644 --- a/libs/common/src/vault/models/domain/identity.spec.ts +++ b/libs/common/src/vault/models/domain/identity.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { EncString } from "../../../models/domain/enc-string"; import { IdentityData } from "../../models/data/identity.data"; import { Identity } from "../../models/domain/identity"; diff --git a/libs/common/src/vault/models/domain/login-uri.spec.ts b/libs/common/src/vault/models/domain/login-uri.spec.ts index 70fb24360f4..28378e73c4a 100644 --- a/libs/common/src/vault/models/domain/login-uri.spec.ts +++ b/libs/common/src/vault/models/domain/login-uri.spec.ts @@ -1,6 +1,6 @@ import { Jsonify } from "type-fest"; -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { UriMatchType } from "../../../enums"; import { EncString } from "../../../models/domain/enc-string"; import { LoginUriData } from "../data/login-uri.data"; diff --git a/libs/common/src/vault/models/domain/login.spec.ts b/libs/common/src/vault/models/domain/login.spec.ts index 654c66f8cf1..4cc774bdc3e 100644 --- a/libs/common/src/vault/models/domain/login.spec.ts +++ b/libs/common/src/vault/models/domain/login.spec.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-restricted-imports import { Substitute, Arg } from "@fluffy-spoon/substitute"; -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { UriMatchType } from "../../../enums"; import { EncString } from "../../../models/domain/enc-string"; import { LoginData } from "../../models/data/login.data"; diff --git a/libs/common/src/vault/models/domain/password.spec.ts b/libs/common/src/vault/models/domain/password.spec.ts index 6128e37f284..4e42904ad96 100644 --- a/libs/common/src/vault/models/domain/password.spec.ts +++ b/libs/common/src/vault/models/domain/password.spec.ts @@ -1,4 +1,4 @@ -import { mockEnc, mockFromJson } from "../../../../spec/utils"; +import { mockEnc, mockFromJson } from "../../../../spec"; import { EncString } from "../../../models/domain/enc-string"; import { PasswordHistoryData } from "../../models/data/password-history.data"; import { Password } from "../../models/domain/password"; diff --git a/libs/common/src/vault/models/view/attachment.view.spec.ts b/libs/common/src/vault/models/view/attachment.view.spec.ts index 7900465b030..dc81fe3f78e 100644 --- a/libs/common/src/vault/models/view/attachment.view.spec.ts +++ b/libs/common/src/vault/models/view/attachment.view.spec.ts @@ -1,4 +1,4 @@ -import { mockFromJson } from "../../../../spec/utils"; +import { mockFromJson } from "../../../../spec"; import { SymmetricCryptoKey } from "../../../models/domain/symmetric-crypto-key"; import { AttachmentView } from "./attachment.view"; diff --git a/libs/common/src/vault/models/view/cipher.view.spec.ts b/libs/common/src/vault/models/view/cipher.view.spec.ts index a2de5b4a3ac..3ab2706d356 100644 --- a/libs/common/src/vault/models/view/cipher.view.spec.ts +++ b/libs/common/src/vault/models/view/cipher.view.spec.ts @@ -1,4 +1,4 @@ -import { mockFromJson } from "../../../../spec/utils"; +import { mockFromJson } from "../../../../spec"; import { CipherType } from "../../enums/cipher-type"; import { AttachmentView } from "./attachment.view"; diff --git a/libs/common/src/vault/models/view/login.view.spec.ts b/libs/common/src/vault/models/view/login.view.spec.ts index 92beb18bd6f..728a62deb9d 100644 --- a/libs/common/src/vault/models/view/login.view.spec.ts +++ b/libs/common/src/vault/models/view/login.view.spec.ts @@ -1,4 +1,4 @@ -import { mockFromJson } from "../../../../spec/utils"; +import { mockFromJson } from "../../../../spec"; import { LoginUriView } from "./login-uri.view"; import { LoginView } from "./login.view"; diff --git a/libs/common/test.setup.ts b/libs/common/test.setup.ts index 17254ea34c6..dfcf7c86101 100644 --- a/libs/common/test.setup.ts +++ b/libs/common/test.setup.ts @@ -1,6 +1,6 @@ import { webcrypto } from "crypto"; -import { toEqualBuffer } from "./spec/matchers/toEqualBuffer"; +import { toEqualBuffer } from "./spec"; Object.defineProperty(window, "crypto", { value: webcrypto, diff --git a/libs/components/jest.config.js b/libs/components/jest.config.js index e90c663ce90..2f4b1ed15d4 100644 --- a/libs/components/jest.config.js +++ b/libs/components/jest.config.js @@ -2,8 +2,9 @@ const { pathsToModuleNameMapper } = require("ts-jest"); const { compilerOptions } = require("./tsconfig"); -const sharedConfig = require("../../libs/shared/jest.config.base"); +const sharedConfig = require("../../libs/shared/jest.config.angular"); +/** @type {import('jest').Config} */ module.exports = { ...sharedConfig, displayName: "libs/components tests", diff --git a/libs/components/src/async-actions/in-forms.stories.mdx b/libs/components/src/async-actions/in-forms.stories.mdx index 07db5f4e2a0..e0715fed419 100644 --- a/libs/components/src/async-actions/in-forms.stories.mdx +++ b/libs/components/src/async-actions/in-forms.stories.mdx @@ -4,10 +4,12 @@ import { Meta } from "@storybook/addon-docs"; # Async Actions In Forms -These directives should be used when building forms with buttons that trigger long running tasks in the background, -eg. Submit or Delete buttons. For buttons that are not associated with a form see [Standalone Async Actions](?path=/story/component-library-async-actions-standalone-documentation--page). +These directives should be used when building forms with buttons that trigger long running tasks in +the background, eg. Submit or Delete buttons. For buttons that are not associated with a form see +[Standalone Async Actions](?path=/story/component-library-async-actions-standalone-documentation--page). -There are two separately supported use-cases: Submit buttons and standalone form buttons (eg. Delete buttons). +There are two separately supported use-cases: Submit buttons and standalone form buttons (eg. Delete +buttons). ## Usage: Submit buttons @@ -15,17 +17,19 @@ Adding async actions to submit buttons requires the following 3 steps ### 1. Add a handler to your `Component` -A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is -useful because `return;` can be used to abort an action. +A handler is a function that returns a promise or an observable. Functions that return `void` are +also supported which is useful because `return;` can be used to abort an action. -**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent -component using the variable `this`. +**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler +needs access to the parent component using the variable `this`. -**NOTE:** `formGroup.invalid` will always return `true` after the first `await` operation, event if the form is not actually -invalid. This is due to the form getting disabled by the `bitSubmit` directive while waiting for the async action to complete. +**NOTE:** `formGroup.invalid` will always return `true` after the first `await` operation, event if +the form is not actually invalid. This is due to the form getting disabled by the `bitSubmit` +directive while waiting for the async action to complete. -**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against -users attempting to trigger new actions before the previous ones have finished. +**NOTE:** Handlers do not need to check if any previous requests have finished because the +directives have built in protection against users attempting to trigger new actions before the +previous ones have finished. ```ts @Component({...}) @@ -51,8 +55,8 @@ class Component { Add the `bitSubmit` directive and supply the handler defined in step 1. -**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. -This is different from how submit handlers are usually defined with the output syntax `(ngSubmit)="handler()"`. +**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different +from how submit handlers are usually defined with the output syntax `(ngSubmit)="handler()"`. **NOTE:** `[bitSubmit]` is used instead of `(ngSubmit)`. Using both is not supported. @@ -76,14 +80,15 @@ Adding async actions to standalone form buttons requires the following 3 steps. ### 1. Add a handler to your `Component` -A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is -useful for aborting an action. +A handler is a function that returns a promise or an observable. Functions that return `void` are +also supported which is useful for aborting an action. -**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent -component using the variable `this`. +**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler +needs access to the parent component using the variable `this`. -**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against -users attempting to trigger new actions before the previous ones have finished. +**NOTE:** Handlers do not need to check if any previous requests have finished because the +directives have built in protection against users attempting to trigger new actions before the +previous ones have finished. ```ts @Component({...}) @@ -113,7 +118,8 @@ The `bitSubmit` directive is required because of its coordinating role inside of ### 3. Add directives to the `button` element -Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sure to supply a handler. +Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sure to supply a +handler. **NOTE:** A summary of what each directive does can be found inside the source code. @@ -124,7 +130,8 @@ Add `bitButton`, `bitFormButton`, `bitAction` directives to the button. Make sur ## `[bitSubmit]` Disabled Form Submit -If you need your form to be able to submit even when the form is disabled, then add `[allowDisabledFormSubmit]="true"` to your `` +If you need your form to be able to submit even when the form is disabled, then add +`[allowDisabledFormSubmit]="true"` to your `` ```html ... diff --git a/libs/components/src/async-actions/overview.stories.mdx b/libs/components/src/async-actions/overview.stories.mdx index 9ec792aefdd..7cffd379b84 100644 --- a/libs/components/src/async-actions/overview.stories.mdx +++ b/libs/components/src/async-actions/overview.stories.mdx @@ -4,14 +4,14 @@ import { Meta } from "@storybook/addon-docs"; # Async Actions -The directives in this module makes it easier for developers to reflect the progress of async actions in the UI when using -buttons, while also providing robust and standardized error handling. +The directives in this module makes it easier for developers to reflect the progress of async +actions in the UI when using buttons, while also providing robust and standardized error handling. -These buttons can either be standalone (such as Refresh buttons), submit buttons for forms or as standalone buttons -that are part of a form (such as Delete buttons). +These buttons can either be standalone (such as Refresh buttons), submit buttons for forms or as +standalone buttons that are part of a form (such as Delete buttons). -These directives are meant to replace the older `appApiAction` directive, providing the option to use `observables` and reduce -clutter inside our view `components`. +These directives are meant to replace the older `appApiAction` directive, providing the option to +use `observables` and reduce clutter inside our view `components`. ## When to use? diff --git a/libs/components/src/async-actions/standalone.stories.mdx b/libs/components/src/async-actions/standalone.stories.mdx index 9ff5753388d..efde494f2dd 100644 --- a/libs/components/src/async-actions/standalone.stories.mdx +++ b/libs/components/src/async-actions/standalone.stories.mdx @@ -4,8 +4,9 @@ import { Meta } from "@storybook/addon-docs"; # Standalone Async Actions -These directives should be used when building a standalone button that triggers a long running task in the background, -eg. Refresh buttons. For non-submit buttons that are associated with forms see [Async Actions In Forms](?path=/story/component-library-async-actions-in-forms-documentation--page). +These directives should be used when building a standalone button that triggers a long running task +in the background, eg. Refresh buttons. For non-submit buttons that are associated with forms see +[Async Actions In Forms](?path=/story/component-library-async-actions-in-forms-documentation--page). ## Usage @@ -13,14 +14,15 @@ Adding async actions to standalone buttons requires the following 2 steps ### 1. Add a handler to your `Component` -A handler is a function that returns a promise or an observable. Functions that return `void` are also supported which is -useful because `return;` can be used to abort an action. +A handler is a function that returns a promise or an observable. Functions that return `void` are +also supported which is useful because `return;` can be used to abort an action. -**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler needs access to the parent -component using the variable `this`. +**NOTE:** Defining the handlers as arrow-functions assigned to variables is mandatory if the handler +needs access to the parent component using the variable `this`. -**NOTE:** Handlers do not need to check if any previous requests have finished because the directives have built in protection against -users attempting to trigger new actions before the previous ones have finished. +**NOTE:** Handlers do not need to check if any previous requests have finished because the +directives have built in protection against users attempting to trigger new actions before the +previous ones have finished. #### Example using promises @@ -48,8 +50,8 @@ class Component { Add the `bitAction` directive and supply the handler defined in step 1. -**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. -This is different from how click handlers are usually defined with the output syntax `(click)="handler()"`. +**NOTE:** The `directive` is defined using the input syntax: `[input]="handler"`. This is different +from how click handlers are usually defined with the output syntax `(click)="handler()"`. **NOTE:** `[bitAction]` is used instead of `(click)`. Using both is not supported. diff --git a/libs/components/src/banner/banner.stories.ts b/libs/components/src/banner/banner.stories.ts index 55477314f73..e636124fe01 100644 --- a/libs/components/src/banner/banner.stories.ts +++ b/libs/components/src/banner/banner.stories.ts @@ -3,6 +3,7 @@ import { Meta, moduleMetadata, Story } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { IconButtonModule } from "../icon-button"; +import { LinkModule } from "../link"; import { SharedModule } from "../shared/shared.module"; import { I18nMockService } from "../utils/i18n-mock.service"; @@ -13,7 +14,7 @@ export default { component: BannerComponent, decorators: [ moduleMetadata({ - imports: [SharedModule, IconButtonModule], + imports: [SharedModule, IconButtonModule, LinkModule], providers: [ { provide: I18nService, @@ -45,7 +46,7 @@ const Template: Story = (args: BannerComponent) => ({ template: ` Content Really Long Text Lorem Ipsum Ipsum Ipsum - + `, }); diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index bb3721795fb..773b764b22e 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -20,8 +20,23 @@ > -
- +
+
+ +
+
+ +
= (args: DialogComponent) => ({ props: args, template: ` - - {{title}} - Dialog body text goes here. - - - - - - + + {{title}} + Dialog body text goes here. + + + + + + `, }); @@ -94,23 +96,30 @@ Large.args = { title: "Large", }; +export const Loading = Template.bind({}); +Loading.args = { + dialogSize: "large", + loading: true, + title: "Loading", +}; + const TemplateScrolling: Story = (args: DialogComponent) => ({ props: args, template: ` - - Scrolling Example - - Dialog body text goes here.
- - repeating lines of characters
+ + Scrolling Example + + Dialog body text goes here.
+ + repeating lines of characters
+
+ end of sequence! +
+ + + - end of sequence! -
- - - - -
+ `, }); @@ -122,20 +131,20 @@ ScrollingContent.args = { const TemplateTabbed: Story = (args: DialogComponent) => ({ props: args, template: ` - - Tab Content Example - - - First Tab Content - Second Tab Content - Third Tab Content - - - - - - - + + Tab Content Example + + + First Tab Content + Second Tab Content + Third Tab Content + + + + + + + `, }); diff --git a/libs/components/src/form-field/form-field.stories.ts b/libs/components/src/form-field/form-field.stories.ts index 64080acc666..1b46e6e6c50 100644 --- a/libs/components/src/form-field/form-field.stories.ts +++ b/libs/components/src/form-field/form-field.stories.ts @@ -95,18 +95,12 @@ const Template: Story = (args: BitFormFieldComponent) => ...args, }, template: ` -
+ - Name + Label + Optional Hint - - - Email - - - -
`, }); diff --git a/libs/components/src/input/input.directive.ts b/libs/components/src/input/input.directive.ts index 2c4cb61eb2b..60589208d52 100644 --- a/libs/components/src/input/input.directive.ts +++ b/libs/components/src/input/input.directive.ts @@ -78,15 +78,9 @@ export class BitInputDirective implements BitFormFieldControl { return this.id; } - private isActive = true; - @HostListener("blur") - onBlur() { - this.isActive = true; - } - @HostListener("input") onInput() { - this.isActive = false; + this.ngControl?.control?.markAsUntouched(); } get hasError() { @@ -97,7 +91,7 @@ export class BitInputDirective implements BitFormFieldControl { this.ngControl?.errors != null ); } else { - return this.ngControl?.status === "INVALID" && this.ngControl?.touched && this.isActive; + return this.ngControl?.status === "INVALID" && this.ngControl?.touched; } } diff --git a/libs/components/src/radio-button/radio-button.stories.ts b/libs/components/src/radio-button/radio-button.stories.ts index abc7ddb92a5..340e76c4219 100644 --- a/libs/components/src/radio-button/radio-button.stories.ts +++ b/libs/components/src/radio-button/radio-button.stories.ts @@ -1,5 +1,4 @@ -import { Component, Input } from "@angular/core"; -import { FormsModule, ReactiveFormsModule, FormBuilder } from "@angular/forms"; +import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from "@angular/forms"; import { Meta, moduleMetadata, Story } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @@ -7,70 +6,13 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nMockService } from "../utils/i18n-mock.service"; import { RadioButtonModule } from "./radio-button.module"; - -const template = ` -
- - Group of radio buttons - - {{ option.key }} - This is a hint for the {{option.key}} option - - -
`; - -const TestValue = { - First: 0, - Second: 1, - Third: 2, -}; - -const reverseObject = (obj: Record) => - Object.fromEntries(Object.entries(obj).map(([key, value]) => [value, key])); - -@Component({ - selector: "app-example", - template: template, -}) -class ExampleComponent { - protected TestValue = TestValue; - - protected formObj = this.formBuilder.group({ - radio: TestValue.First, - }); - - @Input() layout: "block" | "inline" = "inline"; - - @Input() label: boolean; - - @Input() set selected(value: number) { - this.formObj.patchValue({ radio: value }); - } - - @Input() set groupDisabled(disable: boolean) { - if (disable) { - this.formObj.disable(); - } else { - this.formObj.enable(); - } - } - - @Input() optionDisabled: number[] = []; - - get blockLayout() { - return this.layout === "block"; - } - - constructor(private formBuilder: FormBuilder) {} -} +import { RadioGroupComponent } from "./radio-group.component"; export default { title: "Component Library/Form/Radio Button", - component: ExampleComponent, + component: RadioGroupComponent, decorators: [ moduleMetadata({ - declarations: [ExampleComponent], imports: [FormsModule, ReactiveFormsModule, RadioButtonModule], providers: [ { @@ -92,56 +34,65 @@ export default { url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=3930%3A16850&t=xXPx6GJYsJfuMQPE-4", }, }, - args: { - selected: TestValue.First, - groupDisabled: false, - optionDisabled: null, - label: true, - layout: "inline", - }, - argTypes: { - selected: { - options: Object.values(TestValue), - control: { - type: "inline-radio", - labels: reverseObject(TestValue), - }, - }, - optionDisabled: { - options: Object.values(TestValue), - control: { - type: "check", - labels: reverseObject(TestValue), - }, - }, - layout: { - options: ["inline", "block"], - control: { - type: "inline-radio", - labels: ["inline", "block"], - }, - }, - }, } as Meta; -const storyTemplate = ``; +const InlineTemplate: Story = (args: RadioGroupComponent) => ({ + props: { + formObj: new FormGroup({ + radio: new FormControl(0), + }), + }, + template: ` +
+ + Group of radio buttons -const InlineTemplate: Story = (args: ExampleComponent) => ({ - props: args, - template: storyTemplate, + + First + + + + Second + + + + Third + + +
+ `, }); export const Inline = InlineTemplate.bind({}); -Inline.args = { - layout: "inline", -}; -const BlockTemplate: Story = (args: ExampleComponent) => ({ - props: args, - template: storyTemplate, +const BlockTemplate: Story = (args: RadioGroupComponent) => ({ + props: { + formObj: new FormGroup({ + radio: new FormControl(0), + }), + }, + template: ` +
+ + Group of radio buttons + + + First + This is a hint for the first option + + + + Second + This is a hint for the second option + + + + Third + This is a hint for the third option + + +
+ `, }); export const Block = BlockTemplate.bind({}); -Block.args = { - layout: "block", -}; diff --git a/libs/components/src/search/close-button-white.svg b/libs/components/src/search/close-button-white.svg new file mode 100644 index 00000000000..8eff9f53f39 --- /dev/null +++ b/libs/components/src/search/close-button-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/libs/components/src/search/search.component.css b/libs/components/src/search/search.component.css new file mode 100644 index 00000000000..87c38348bce --- /dev/null +++ b/libs/components/src/search/search.component.css @@ -0,0 +1,19 @@ +/** + * Tailwind doesn't have a good way to style search-cancel-button. + */ +bit-search input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + appearance: none; + height: 21px; + width: 21px; + margin: 0; + cursor: pointer; + background-repeat: no-repeat; + mask-image: url("./close-button-white.svg"); + -webkit-mask-image: url("./close-button-white.svg"); + background-color: rgba(var(--color-text-muted)); +} + +bit-search input[type="search"]::-webkit-search-cancel-button:hover { + background-color: rgba(var(--color-text-main)); +} diff --git a/libs/components/src/stories/Introduction.stories.mdx b/libs/components/src/stories/Introduction.stories.mdx index f9cc3813982..4fa3af08088 100644 --- a/libs/components/src/stories/Introduction.stories.mdx +++ b/libs/components/src/stories/Introduction.stories.mdx @@ -81,13 +81,13 @@ import { Meta } from "@storybook/addon-docs"; # Bitwarden Component Library -The Bitwarden Component Library is a collection of reusable low level components which empowers designers and -developers to work more efficiently. The primary goal is to ensure a consistent design and behavior across the -different clients and platforms. Currently the primary focus is the web based clients, namely _web_, _browser_ and -_desktop_. +The Bitwarden Component Library is a collection of reusable low level components which empowers +designers and developers to work more efficiently. The primary goal is to ensure a consistent design +and behavior across the different clients and platforms. Currently the primary focus is the web +based clients, namely _web_, _browser_ and _desktop_. -**Roll out status:** we are currently in the process of transitioning the Web Vault to utilize the component library -and the other clients will follow once this work is completed. +**Roll out status:** we are currently in the process of transitioning the Web Vault to utilize the +component library and the other clients will follow once this work is completed.
Configure
diff --git a/libs/components/src/stories/banner-docs.stories.mdx b/libs/components/src/stories/banner-docs.stories.mdx index 7459a5ddf34..61b216523bd 100644 --- a/libs/components/src/stories/banner-docs.stories.mdx +++ b/libs/components/src/stories/banner-docs.stories.mdx @@ -4,19 +4,21 @@ import { Meta, Story } from "@storybook/addon-docs"; # Banner -Banners are used for important communication with the user that needs to be seen right away, but has little effect on the experience. Banners appear at the top of the user's screen on page load and persist across all pages a user navigates to. - -- They should always be dismissable and never use a timeout. If a user dismisses a banner, it should not reappear during that same active session. - -- Use banners sparingly, as they can feel intrusive to the user if they appear unexpectedly. Their effectiveness may decrease if too many are used. +Banners are used for important communication with the user that needs to be seen right away, but has +little effect on the experience. Banners appear at the top of the user's screen on page load and +persist across all pages a user navigates to. +- They should always be dismissable and never use a timeout. If a user dismisses a banner, it should + not reappear during that same active session. +- Use banners sparingly, as they can feel intrusive to the user if they appear unexpectedly. Their + effectiveness may decrease if too many are used. - Avoid stacking multiple banners. - - Banners support a button link (text button). ## Types -Icons should remain consistent across these types. Do not change the icon without consulting designers. +Icons should remain consistent across these types. Do not change the icon without consulting +designers. Use the following guidelines to help choose the correct type of banner. @@ -46,4 +48,6 @@ Rarely used, but may be used to alert users over critical messages or very outda ## Accessibility -Include `role="status" aria-live="polite"` attributes to ensure screen readers announce the content prior to the test of the page. +Banners sets the `role="status"` and `aria-live="polite"` attributes to ensure screen readers +announce the content prior to the test of the page. This behaviour can be disabled by setting +`[useAlertRole]="false"`. diff --git a/libs/components/src/stories/button-docs.stories.mdx b/libs/components/src/stories/button-docs.stories.mdx index c598f2d2221..3551be8cf6b 100644 --- a/libs/components/src/stories/button-docs.stories.mdx +++ b/libs/components/src/stories/button-docs.stories.mdx @@ -4,30 +4,54 @@ import { Meta, Story } from "@storybook/addon-docs"; # Button -Use buttons for actions in forms, dialogs, and more with support for style, block, icon, and state. +Buttons are interactive elements that can be triggered using a mouse, keyboard, or touch. They are +used to indicate actions that can be performed by a user such as submitting a form. -For pairings in the bottom left corner of a page or component, the `primary` call to action will go on the left side of a button group with the `secondary` call to action option on the left. +## Guidelines -Pairings in the top right corner of a page, should have the `primary` call to action on the right. +### Choosing the `` or `