mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 05:53:42 +00:00
Merge branch 'main' into km/disable-legacy-ciphers
This commit is contained in:
166
.github/workflows/build-browser.yml
vendored
166
.github/workflows/build-browser.yml
vendored
@@ -114,8 +114,8 @@ jobs:
|
||||
fi
|
||||
|
||||
|
||||
build:
|
||||
name: Build
|
||||
build-source:
|
||||
name: Build browser source
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- setup
|
||||
@@ -127,7 +127,7 @@ jobs:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
@@ -169,21 +169,91 @@ jobs:
|
||||
zip -r browser-source.zip browser-source
|
||||
|
||||
- name: Upload browser source
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: browser-source-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source.zip
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- setup
|
||||
- locales-test
|
||||
- build-source
|
||||
env:
|
||||
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
|
||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- name: "chrome"
|
||||
npm_command: "dist:chrome"
|
||||
archive_name: "dist-chrome.zip"
|
||||
artifact_name: "dist-chrome-MV3"
|
||||
- name: "edge"
|
||||
npm_command: "dist:edge"
|
||||
archive_name: "dist-edge.zip"
|
||||
artifact_name: "dist-edge"
|
||||
- name: "edge-mv3"
|
||||
npm_command: "dist:edge:mv3"
|
||||
archive_name: "dist-edge.zip"
|
||||
artifact_name: "DO-NOT-USE-FOR-PROD-dist-edge-MV3"
|
||||
- name: "firefox"
|
||||
npm_command: "dist:firefox"
|
||||
archive_name: "dist-firefox.zip"
|
||||
artifact_name: "dist-firefox"
|
||||
- name: "firefox-mv3"
|
||||
npm_command: "dist:firefox:mv3"
|
||||
archive_name: "dist-firefox.zip"
|
||||
artifact_name: "DO-NOT-USE-FOR-PROD-dist-firefox-MV3"
|
||||
- name: "opera"
|
||||
npm_command: "dist:opera"
|
||||
archive_name: "dist-opera.zip"
|
||||
artifact_name: "dist-opera"
|
||||
- name: "opera-mv3"
|
||||
npm_command: "dist:opera:mv3"
|
||||
archive_name: "dist-opera.zip"
|
||||
artifact_name: "DO-NOT-USE-FOR-PROD-dist-opera-MV3"
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
node-version: ${{ env._NODE_VERSION }}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
- name: Download browser source
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: browser-source-${{ env._BUILD_NUMBER }}.zip
|
||||
|
||||
- name: Unzip browser source artifact
|
||||
run: |
|
||||
unzip browser-source.zip
|
||||
rm browser-source.zip
|
||||
|
||||
- name: NPM setup
|
||||
run: npm ci
|
||||
working-directory: browser-source/
|
||||
|
||||
- name: Download SDK Artifacts
|
||||
- name: Download SDK artifacts
|
||||
if: ${{ inputs.sdk_branch != '' }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
workflow: build-wasm-internal.yml
|
||||
workflow_conclusion: success
|
||||
branch: ${{ inputs.sdk_branch }}
|
||||
@@ -195,85 +265,19 @@ jobs:
|
||||
- name: Override SDK
|
||||
if: ${{ inputs.sdk_branch != '' }}
|
||||
working-directory: browser-source/
|
||||
run: |
|
||||
npm link ../sdk-internal
|
||||
run: npm link ../sdk-internal
|
||||
|
||||
- name: Build Chrome
|
||||
run: npm run dist:chrome
|
||||
- name: Build extension
|
||||
run: npm run ${{ matrix.npm_command }}
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Chrome MV3 artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
- name: Upload extension artifact
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-chrome.zip
|
||||
name: ${{ matrix.artifact_name }}-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/${{ matrix.archive_name }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Edge
|
||||
run: npm run dist:edge
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Edge artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: dist-edge-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-edge.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Edge (MV3)
|
||||
run: npm run dist:edge:mv3
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Edge MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-edge.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Firefox
|
||||
run: npm run dist:firefox
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Firefox artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: dist-firefox-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-firefox.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Firefox (MV3)
|
||||
run: npm run dist:firefox:mv3
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Firefox MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-firefox-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-firefox.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Opera
|
||||
run: npm run dist:opera
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Opera artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: dist-opera-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-opera.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Opera (MV3)
|
||||
run: npm run dist:opera:mv3
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Opera MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-opera.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build-safari:
|
||||
name: Build Safari
|
||||
@@ -405,7 +409,7 @@ jobs:
|
||||
ls -la
|
||||
|
||||
- name: Upload Safari artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: dist-safari-${{ env._BUILD_NUMBER }}.zip
|
||||
path: apps/browser/dist/dist-safari.zip
|
||||
@@ -448,6 +452,7 @@ jobs:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
||||
|
||||
check-failures:
|
||||
name: Check for failures
|
||||
if: always()
|
||||
@@ -455,6 +460,7 @@ jobs:
|
||||
needs:
|
||||
- setup
|
||||
- locales-test
|
||||
- build-source
|
||||
- build
|
||||
- build-safari
|
||||
- crowdin-push
|
||||
|
||||
16
.github/workflows/build-cli.yml
vendored
16
.github/workflows/build-cli.yml
vendored
@@ -163,14 +163,14 @@ jobs:
|
||||
matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
|
||||
- name: Upload unix zip asset
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip
|
||||
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload unix checksum asset
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
@@ -324,14 +324,14 @@ jobs:
|
||||
-t sha256 | Out-File -Encoding ASCII ./dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${env:_PACKAGE_VERSION}.txt
|
||||
|
||||
- name: Upload windows zip asset
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip
|
||||
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload windows checksum asset
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
@@ -339,7 +339,7 @@ jobs:
|
||||
|
||||
- name: Upload Chocolatey asset
|
||||
if: matrix.license_type.build_prefix == 'bit'
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
|
||||
path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
|
||||
@@ -350,7 +350,7 @@ jobs:
|
||||
|
||||
- name: Upload NPM Build Directory asset
|
||||
if: matrix.license_type.build_prefix == 'bit'
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
|
||||
path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
|
||||
@@ -421,14 +421,14 @@ jobs:
|
||||
run: sudo snap remove bw
|
||||
|
||||
- name: Upload snap asset
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
|
||||
82
.github/workflows/build-desktop.yml
vendored
82
.github/workflows/build-desktop.yml
vendored
@@ -207,7 +207,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -232,42 +232,42 @@ jobs:
|
||||
run: npm run dist:lin
|
||||
|
||||
- name: Upload .deb artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}-linux.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml
|
||||
@@ -280,7 +280,7 @@ jobs:
|
||||
sudo npm run pack:lin:flatpak
|
||||
|
||||
- name: Upload flatpak artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: com.bitwarden.desktop.flatpak
|
||||
path: apps/desktop/dist/com.bitwarden.desktop.flatpak
|
||||
@@ -373,7 +373,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -428,91 +428,91 @@ jobs:
|
||||
-NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
|
||||
|
||||
- name: Upload portable exe artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}.yml
|
||||
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
|
||||
@@ -561,14 +561,14 @@ jobs:
|
||||
|
||||
- name: Cache Build
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Cache Safari
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -681,7 +681,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -749,14 +749,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -869,7 +869,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -918,28 +918,28 @@ jobs:
|
||||
run: npm run pack:mac
|
||||
|
||||
- name: Upload .zip artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}-mac.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml
|
||||
@@ -990,14 +990,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -1117,7 +1117,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -1166,7 +1166,7 @@ jobs:
|
||||
run: npm run pack:mac:mas
|
||||
|
||||
- name: Upload .pkg artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
@@ -1193,7 +1193,7 @@ jobs:
|
||||
if: |
|
||||
github.event_name != 'pull_request_target'
|
||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop')
|
||||
uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0
|
||||
uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
|
||||
with:
|
||||
channel-id: C074F5UESQ0
|
||||
payload: |
|
||||
@@ -1252,14 +1252,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -1372,7 +1372,7 @@ jobs:
|
||||
npm link ../sdk-internal
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -1424,7 +1424,7 @@ jobs:
|
||||
zip -r Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app
|
||||
|
||||
- name: Upload masdev artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
|
||||
path: apps/desktop/dist/mas-dev-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
|
||||
|
||||
8
.github/workflows/build-web.yml
vendored
8
.github/workflows/build-web.yml
vendored
@@ -164,7 +164,7 @@ jobs:
|
||||
run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build
|
||||
|
||||
- name: Upload ${{ matrix.name }} artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: web-${{ env._VERSION }}-${{ matrix.name }}.zip
|
||||
path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip
|
||||
@@ -274,7 +274,7 @@ jobs:
|
||||
|
||||
- name: Build Docker image
|
||||
id: build-docker
|
||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
with:
|
||||
context: apps/web
|
||||
file: apps/web/Dockerfile
|
||||
@@ -303,14 +303,14 @@ jobs:
|
||||
|
||||
- name: Scan Docker image
|
||||
id: container-scan
|
||||
uses: anchore/scan-action@5ed195cc06065322983cae4bb31e2a751feb86fd # v5.2.0
|
||||
uses: anchore/scan-action@869c549e657a088dc0441b08ce4fc0ecdac2bb65 # v5.3.0
|
||||
with:
|
||||
image: ${{ steps.image-name.outputs.name }}
|
||||
fail-build: false
|
||||
output-format: sarif
|
||||
|
||||
- name: Upload Grype results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||
with:
|
||||
sarif_file: ${{ steps.container-scan.outputs.sarif }}
|
||||
|
||||
|
||||
4
.github/workflows/chromatic.yml
vendored
4
.github/workflows/chromatic.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
- name: Cache NPM
|
||||
id: npm-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }}
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
run: npm run build-storybook:ci
|
||||
|
||||
- name: Publish to Chromatic
|
||||
uses: chromaui/action@dd2eecb9bef44f54774581f4163b0327fd8cf607 # v11.16.3
|
||||
uses: chromaui/action@64a9c0ca3bfb724389b0d536e544f56b7b5ff5b3 # v11.20.2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
|
||||
2
.github/workflows/crowdin-pull.yml
vendored
2
.github/workflows/crowdin-pull.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
crowdin_project_id: "308189"
|
||||
steps:
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
|
||||
60
.github/workflows/release-desktop-beta.yml
vendored
60
.github/workflows/release-desktop-beta.yml
vendored
@@ -158,42 +158,42 @@ jobs:
|
||||
run: npm run dist:lin
|
||||
|
||||
- name: Upload .deb artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}-linux.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-linux.yml
|
||||
@@ -299,91 +299,91 @@ jobs:
|
||||
-NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
|
||||
|
||||
- name: Upload portable exe artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}.yml
|
||||
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release-channel }}.yml
|
||||
@@ -426,14 +426,14 @@ jobs:
|
||||
|
||||
- name: Cache Build
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Cache Safari
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -560,14 +560,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -707,28 +707,28 @@ jobs:
|
||||
run: npm run pack:mac
|
||||
|
||||
- name: Upload .zip artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}-mac.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-mac.yml
|
||||
@@ -773,14 +773,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -915,7 +915,7 @@ jobs:
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: Upload .pkg artifact
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
|
||||
6
.github/workflows/repository-management.yml
vendored
6
.github/workflows/repository-management.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
version: ${{ inputs.version_number_override }}
|
||||
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
@@ -452,7 +452,7 @@ jobs:
|
||||
- setup
|
||||
steps:
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
|
||||
4
.github/workflows/scan.yml
vendored
4
.github/workflows/scan.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Scan with Checkmarx
|
||||
uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36
|
||||
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
|
||||
env:
|
||||
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
|
||||
with:
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
--output-path . ${{ env.INCREMENTAL }}
|
||||
|
||||
- name: Upload Checkmarx results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||
with:
|
||||
sarif_file: cx_result.sarif
|
||||
|
||||
|
||||
2
.github/workflows/version-auto-bump.yml
vendored
2
.github/workflows/version-auto-bump.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Generate GH App token
|
||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||
|
||||
@@ -2105,7 +2105,7 @@
|
||||
"message": "Aikakatkaisutoiminnon vahvistus"
|
||||
},
|
||||
"autoFillAndSave": {
|
||||
"message": "Täytä automaattisesti ja tallenna"
|
||||
"message": "Automaattitäytä ja tallenna"
|
||||
},
|
||||
"fillAndSave": {
|
||||
"message": "Täytä ja tallenna"
|
||||
@@ -3482,7 +3482,7 @@
|
||||
"description": "Aria label for the totp code displayed in the inline menu for autofill"
|
||||
},
|
||||
"totpSecondsSpanAria": {
|
||||
"message": "Time remaining before current TOTP expires",
|
||||
"message": "Aika jäljellä, ennen kuin nykyinen TOTP vanhenee",
|
||||
"description": "Aria label for the totp seconds displayed in the inline menu for autofill"
|
||||
},
|
||||
"fillCredentialsFor": {
|
||||
@@ -4810,7 +4810,7 @@
|
||||
"message": "Määritä kaksivaiheinen kirjautuminen"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden lähettää tilisi sähköpostiosoitteeseen koodin, jolla voit vahvistaa kirjautumiset uusista laitteista helmikuusta 2025 alkaen."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "Prikazuj identitete za jednostavnu auto-ispunu."
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "Klikni stavke za auto-ispunu na prikazu trezora"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "Očisti međuspremnik",
|
||||
|
||||
@@ -20,16 +20,16 @@
|
||||
"message": "Crea account"
|
||||
},
|
||||
"newToBitwarden": {
|
||||
"message": "New to Bitwarden?"
|
||||
"message": "Nuovo in Bitwarden?"
|
||||
},
|
||||
"logInWithPasskey": {
|
||||
"message": "Log in with passkey"
|
||||
"message": "Accedi con passkey"
|
||||
},
|
||||
"useSingleSignOn": {
|
||||
"message": "Use single sign-on"
|
||||
"message": "Usa il Single Sign-On"
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "Welcome back"
|
||||
"message": "Bentornato"
|
||||
},
|
||||
"setAStrongPassword": {
|
||||
"message": "Imposta una password robusta"
|
||||
@@ -120,7 +120,7 @@
|
||||
"message": "Copia password"
|
||||
},
|
||||
"copyPassphrase": {
|
||||
"message": "Copy passphrase"
|
||||
"message": "Copia passphrase"
|
||||
},
|
||||
"copyNote": {
|
||||
"message": "Copia nota"
|
||||
@@ -153,13 +153,13 @@
|
||||
"message": "Copia numero licenza"
|
||||
},
|
||||
"copyPrivateKey": {
|
||||
"message": "Copy private key"
|
||||
"message": "Copia chiave privata"
|
||||
},
|
||||
"copyPublicKey": {
|
||||
"message": "Copy public key"
|
||||
"message": "Copia chiave pubblica"
|
||||
},
|
||||
"copyFingerprint": {
|
||||
"message": "Copy fingerprint"
|
||||
"message": "Copia impronta"
|
||||
},
|
||||
"copyCustomField": {
|
||||
"message": "Copia $FIELD$",
|
||||
@@ -177,7 +177,7 @@
|
||||
"message": "Copia note"
|
||||
},
|
||||
"fill": {
|
||||
"message": "Fill",
|
||||
"message": "Riempi",
|
||||
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
||||
},
|
||||
"autoFill": {
|
||||
@@ -193,10 +193,10 @@
|
||||
"message": "Riempi automaticamente identità"
|
||||
},
|
||||
"fillVerificationCode": {
|
||||
"message": "Fill verification code"
|
||||
"message": "Riempi codice di verifica"
|
||||
},
|
||||
"fillVerificationCodeAria": {
|
||||
"message": "Fill Verification Code",
|
||||
"message": "Riempi Codice di Verifica",
|
||||
"description": "Aria label for the heading displayed the inline menu for totp code autofill"
|
||||
},
|
||||
"generatePasswordCopied": {
|
||||
@@ -239,7 +239,7 @@
|
||||
"message": "Aggiungi elemento"
|
||||
},
|
||||
"accountEmail": {
|
||||
"message": "Account email"
|
||||
"message": "Email dell'account"
|
||||
},
|
||||
"requestHint": {
|
||||
"message": "Richiedi suggerimento"
|
||||
@@ -443,7 +443,7 @@
|
||||
"message": "Genera password"
|
||||
},
|
||||
"generatePassphrase": {
|
||||
"message": "Generate passphrase"
|
||||
"message": "Genera passphrase"
|
||||
},
|
||||
"regeneratePassword": {
|
||||
"message": "Rigenera password"
|
||||
@@ -530,7 +530,7 @@
|
||||
"description": "Label for the avoid ambiguous characters checkbox."
|
||||
},
|
||||
"generatorPolicyInEffect": {
|
||||
"message": "Enterprise policy requirements have been applied to your generator options.",
|
||||
"message": "I requisiti di politica aziendale sono stati applicati alle opzioni del generatore.",
|
||||
"description": "Indicates that a policy limits the credential generator screen."
|
||||
},
|
||||
"searchVault": {
|
||||
@@ -576,7 +576,7 @@
|
||||
"message": "Note"
|
||||
},
|
||||
"privateNote": {
|
||||
"message": "Private note"
|
||||
"message": "Nota privata"
|
||||
},
|
||||
"note": {
|
||||
"message": "Nota"
|
||||
@@ -600,7 +600,7 @@
|
||||
"message": "Avvia il sito web"
|
||||
},
|
||||
"launchWebsiteName": {
|
||||
"message": "Launch website $ITEMNAME$",
|
||||
"message": "Apri sito web $ITEMNAME$",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
"content": "$1",
|
||||
@@ -633,7 +633,7 @@
|
||||
"message": "Timeout della sessione"
|
||||
},
|
||||
"vaultTimeoutHeader": {
|
||||
"message": "Vault timeout"
|
||||
"message": "Timeout cassaforte"
|
||||
},
|
||||
"otherOptions": {
|
||||
"message": "Altre opzioni"
|
||||
@@ -651,13 +651,13 @@
|
||||
"message": "La tua cassaforte è bloccata. Verifica la tua identità per continuare."
|
||||
},
|
||||
"yourVaultIsLockedV2": {
|
||||
"message": "Your vault is locked"
|
||||
"message": "Cassaforte bloccata"
|
||||
},
|
||||
"yourAccountIsLocked": {
|
||||
"message": "Your account is locked"
|
||||
"message": "Il tuo account è bloccato"
|
||||
},
|
||||
"or": {
|
||||
"message": "or"
|
||||
"message": "o"
|
||||
},
|
||||
"unlock": {
|
||||
"message": "Sblocca"
|
||||
@@ -852,7 +852,7 @@
|
||||
"message": "Accedi"
|
||||
},
|
||||
"logInToBitwarden": {
|
||||
"message": "Log in to Bitwarden"
|
||||
"message": "Accedi a Bitwarden"
|
||||
},
|
||||
"restartRegistration": {
|
||||
"message": "Riprova la registrazione"
|
||||
@@ -888,10 +888,10 @@
|
||||
"message": "La verifica in due passaggi rende il tuo account più sicuro richiedendoti di verificare il tuo login usando un altro dispositivo come una chiave di sicurezza, app di autenticazione, SMS, telefonata, o email. Può essere abilitata nella cassaforte web su bitwarden.com. Vuoi visitare il sito?"
|
||||
},
|
||||
"twoStepLoginConfirmationContent": {
|
||||
"message": "Make your account more secure by setting up two-step login in the Bitwarden web app."
|
||||
"message": "Rendi il tuo account più sicuro impostando l'autenticazione a due fattori nell'app web di Bitwarden."
|
||||
},
|
||||
"twoStepLoginConfirmationTitle": {
|
||||
"message": "Continue to web app?"
|
||||
"message": "Aprire web app?"
|
||||
},
|
||||
"editedFolder": {
|
||||
"message": "Cartella salvata"
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "Mostra le identità nella sezione Scheda per riempirle automaticamente."
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "Clicca gli oggetti da riempire dalla sezione Cassaforte"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "Cancella appunti",
|
||||
@@ -1126,7 +1126,7 @@
|
||||
"description": "WARNING (should stay in capitalized letters if the language permits)"
|
||||
},
|
||||
"warningCapitalized": {
|
||||
"message": "Warning",
|
||||
"message": "Attenzione",
|
||||
"description": "Warning (should maintain locale-relevant capitalization)"
|
||||
},
|
||||
"confirmVaultExport": {
|
||||
@@ -1206,7 +1206,7 @@
|
||||
"message": "File"
|
||||
},
|
||||
"fileToShare": {
|
||||
"message": "File to share"
|
||||
"message": "File da condividere"
|
||||
},
|
||||
"selectFile": {
|
||||
"message": "Seleziona un file"
|
||||
@@ -1317,10 +1317,10 @@
|
||||
"message": "Inserisci il codice di verifica a 6 cifre dalla tua app di autenticazione."
|
||||
},
|
||||
"authenticationTimeout": {
|
||||
"message": "Authentication timeout"
|
||||
"message": "Timeout autenticazione"
|
||||
},
|
||||
"authenticationSessionTimedOut": {
|
||||
"message": "The authentication session timed out. Please restart the login process."
|
||||
"message": "La sessione di autenticazione è scaduta. Accedi di nuovo."
|
||||
},
|
||||
"enterVerificationCodeEmail": {
|
||||
"message": "Inserisci il codice di verifica a 6 cifre inviato a $EMAIL$.",
|
||||
@@ -1440,7 +1440,7 @@
|
||||
"message": "URL del server"
|
||||
},
|
||||
"selfHostBaseUrl": {
|
||||
"message": "Self-host server URL",
|
||||
"message": "URL server autogestito",
|
||||
"description": "Label for field requesting a self-hosted integration service URL"
|
||||
},
|
||||
"apiUrl": {
|
||||
@@ -1472,10 +1472,10 @@
|
||||
"message": "Mostra suggerimenti di riempimento automatico nei campi del modulo"
|
||||
},
|
||||
"showInlineMenuIdentitiesLabel": {
|
||||
"message": "Display identities as suggestions"
|
||||
"message": "Mostra identità come consigli"
|
||||
},
|
||||
"showInlineMenuCardsLabel": {
|
||||
"message": "Display cards as suggestions"
|
||||
"message": "Mostra carte come consigli"
|
||||
},
|
||||
"showInlineMenuOnIconSelectionLabel": {
|
||||
"message": "Mostra suggerimenti quando l'icona è selezionata"
|
||||
@@ -1768,7 +1768,7 @@
|
||||
"message": "Identità"
|
||||
},
|
||||
"typeSshKey": {
|
||||
"message": "SSH key"
|
||||
"message": "Chiave SSH"
|
||||
},
|
||||
"newItemHeader": {
|
||||
"message": "Nuovo $TYPE$",
|
||||
@@ -1801,13 +1801,13 @@
|
||||
"message": "Cronologia delle password"
|
||||
},
|
||||
"generatorHistory": {
|
||||
"message": "Generator history"
|
||||
"message": "Cronologia generatore"
|
||||
},
|
||||
"clearGeneratorHistoryTitle": {
|
||||
"message": "Clear generator history"
|
||||
"message": "Cancella cronologia generatore"
|
||||
},
|
||||
"cleargGeneratorHistoryDescription": {
|
||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||
"message": "Se continui, tutte le voci verranno eliminate definitivamente dalla cronologia del generatore. Vuoi continuare?"
|
||||
},
|
||||
"back": {
|
||||
"message": "Indietro"
|
||||
@@ -1846,7 +1846,7 @@
|
||||
"message": "Note sicure"
|
||||
},
|
||||
"sshKeys": {
|
||||
"message": "SSH Keys"
|
||||
"message": "Chiavi SSH"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Cancella",
|
||||
@@ -1929,10 +1929,10 @@
|
||||
"message": "Cancella cronologia"
|
||||
},
|
||||
"nothingToShow": {
|
||||
"message": "Nothing to show"
|
||||
"message": "Niente da mostrare"
|
||||
},
|
||||
"nothingGeneratedRecently": {
|
||||
"message": "You haven't generated anything recently"
|
||||
"message": "Non hai generato niente di recente"
|
||||
},
|
||||
"remove": {
|
||||
"message": "Rimuovi"
|
||||
@@ -1993,16 +1993,16 @@
|
||||
"message": "Sblocca con PIN"
|
||||
},
|
||||
"setYourPinTitle": {
|
||||
"message": "Set PIN"
|
||||
"message": "Imposta PIN"
|
||||
},
|
||||
"setYourPinButton": {
|
||||
"message": "Set PIN"
|
||||
"message": "Imposta PIN"
|
||||
},
|
||||
"setYourPinCode": {
|
||||
"message": "Imposta il tuo codice PIN per sbloccare Bitwarden. Le tue impostazioni PIN saranno resettate se esci completamente dall'app."
|
||||
},
|
||||
"setYourPinCode1": {
|
||||
"message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden."
|
||||
"message": "Il tuo PIN sarà usato per sbloccare Bitwarden invece della password principale. Il PIN sarà ripristinato se ti disconnetterai completamente da Bitwarden."
|
||||
},
|
||||
"pinRequired": {
|
||||
"message": "Codice PIN obbligatorio."
|
||||
@@ -2017,7 +2017,7 @@
|
||||
"message": "Sblocca con i dati biometrici"
|
||||
},
|
||||
"unlockWithMasterPassword": {
|
||||
"message": "Unlock with master password"
|
||||
"message": "Sblocca con password principale"
|
||||
},
|
||||
"awaitDesktop": {
|
||||
"message": "In attesa di conferma dal desktop"
|
||||
@@ -2029,7 +2029,7 @@
|
||||
"message": "Blocca con la password principale al riavvio del browser"
|
||||
},
|
||||
"lockWithMasterPassOnRestart1": {
|
||||
"message": "Require master password on browser restart"
|
||||
"message": "Richiedi password principale al riavvio del browser"
|
||||
},
|
||||
"selectOneCollection": {
|
||||
"message": "Devi selezionare almeno una raccolta."
|
||||
@@ -2067,7 +2067,7 @@
|
||||
"message": "Azione timeout cassaforte"
|
||||
},
|
||||
"vaultTimeoutAction1": {
|
||||
"message": "Timeout action"
|
||||
"message": "Azione al timeout"
|
||||
},
|
||||
"lock": {
|
||||
"message": "Blocca",
|
||||
@@ -2355,14 +2355,14 @@
|
||||
"message": "Modifiche del dominio escluso salvate"
|
||||
},
|
||||
"limitSendViews": {
|
||||
"message": "Limit views"
|
||||
"message": "Limita visualizzazioni"
|
||||
},
|
||||
"limitSendViewsHint": {
|
||||
"message": "No one can view this Send after the limit is reached.",
|
||||
"message": "Nessuno potrà vedere questo Send al raggiungimento del limite.",
|
||||
"description": "Displayed under the limit views field on Send"
|
||||
},
|
||||
"limitSendViewsCount": {
|
||||
"message": "$ACCESSCOUNT$ views left",
|
||||
"message": "$ACCESSCOUNT$ visualizzazioni rimaste",
|
||||
"description": "Displayed under the limit views field on Send",
|
||||
"placeholders": {
|
||||
"accessCount": {
|
||||
@@ -2376,14 +2376,14 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendDetails": {
|
||||
"message": "Send details",
|
||||
"message": "Dettagli Send",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendTypeText": {
|
||||
"message": "Testo"
|
||||
},
|
||||
"sendTypeTextToShare": {
|
||||
"message": "Text to share"
|
||||
"message": "Testo da condividere"
|
||||
},
|
||||
"sendTypeFile": {
|
||||
"message": "File"
|
||||
@@ -2393,7 +2393,7 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"hideTextByDefault": {
|
||||
"message": "Hide text by default"
|
||||
"message": "Nascondi testo come default"
|
||||
},
|
||||
"expired": {
|
||||
"message": "Scaduto"
|
||||
@@ -2440,7 +2440,7 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"deleteSendPermanentConfirmation": {
|
||||
"message": "Are you sure you want to permanently delete this Send?",
|
||||
"message": "Sicuro di voler eliminare definitivamente questo Send?",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"editSend": {
|
||||
@@ -2451,7 +2451,7 @@
|
||||
"message": "Data di eliminazione"
|
||||
},
|
||||
"deletionDateDescV2": {
|
||||
"message": "The Send will be permanently deleted on this date.",
|
||||
"message": "Il Send sarà cancellato definitivamente in questa data.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"expirationDate": {
|
||||
@@ -2473,7 +2473,7 @@
|
||||
"message": "Personalizzato"
|
||||
},
|
||||
"sendPasswordDescV3": {
|
||||
"message": "Add an optional password for recipients to access this Send.",
|
||||
"message": "Richiedi ai destinatari una password opzionale per aprire questo Send.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"createSend": {
|
||||
@@ -2500,11 +2500,11 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHoursSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 hour.",
|
||||
"message": "Il Send sarà disponibile a chiunque con il link per la prossima ora.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInHours": {
|
||||
"message": "The Send will be available to anyone with the link for the next $HOURS$ hours.",
|
||||
"message": "Il Send sarà disponibile a chiunque con il link per le prossime $HOURS$ ore.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
@@ -2514,11 +2514,11 @@
|
||||
}
|
||||
},
|
||||
"sendExpiresInDaysSingle": {
|
||||
"message": "The Send will be available to anyone with the link for the next 1 day.",
|
||||
"message": "Il Send sarà disponibile a chiunque con il link per il prossimo giorno.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendExpiresInDays": {
|
||||
"message": "The Send will be available to anyone with the link for the next $DAYS$ days.",
|
||||
"message": "Il Send sarà disponibile a chiunque con il link per i prossimi $DAYS$ giorni.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.",
|
||||
"placeholders": {
|
||||
"days": {
|
||||
@@ -2536,11 +2536,11 @@
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendFilePopoutDialogText": {
|
||||
"message": "Pop out extension?",
|
||||
"message": "Scollegare estensione?",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendFilePopoutDialogDesc": {
|
||||
"message": "To create a file Send, you need to pop out the extension to a new window.",
|
||||
"message": "Per creare un file Send, devi scollegare l'estensione in una nuova finestra.",
|
||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||
},
|
||||
"sendLinuxChromiumFileWarning": {
|
||||
@@ -2553,7 +2553,7 @@
|
||||
"message": "Per scegliere un file usando Safari, apri una nuova finestra cliccando questo banner."
|
||||
},
|
||||
"popOut": {
|
||||
"message": "Pop out"
|
||||
"message": "Scollega"
|
||||
},
|
||||
"sendFileCalloutHeader": {
|
||||
"message": "Prima di iniziare"
|
||||
@@ -2574,7 +2574,7 @@
|
||||
"message": "Si è verificato un errore durante il salvataggio delle date di eliminazione e scadenza."
|
||||
},
|
||||
"hideYourEmail": {
|
||||
"message": "Hide your email address from viewers."
|
||||
"message": "Nascondi il tuo indirizzo email ai visualizzatori."
|
||||
},
|
||||
"passwordPrompt": {
|
||||
"message": "Richiedi di inserire la password principale di nuovo per visualizzare questo elemento"
|
||||
@@ -2631,7 +2631,7 @@
|
||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||
},
|
||||
"cardMetrics": {
|
||||
"message": "out of $TOTAL$",
|
||||
"message": "di $TOTAL$",
|
||||
"placeholders": {
|
||||
"total": {
|
||||
"content": "$1",
|
||||
@@ -2650,7 +2650,7 @@
|
||||
"message": "Minuti"
|
||||
},
|
||||
"vaultTimeoutPolicyAffectingOptions": {
|
||||
"message": "Enterprise policy requirements have been applied to your timeout options"
|
||||
"message": "I requisiti di politica aziendale sono stati applicati alle opzioni di timeout"
|
||||
},
|
||||
"vaultTimeoutPolicyInEffect": {
|
||||
"message": "Le politiche della tua organizzazione hanno impostato il timeout massimo consentito della tua cassaforte su $HOURS$ ore e $MINUTES$ minuti.",
|
||||
@@ -2666,7 +2666,7 @@
|
||||
}
|
||||
},
|
||||
"vaultTimeoutPolicyInEffect1": {
|
||||
"message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.",
|
||||
"message": "Al massimo $HOURS$ ora/e e $MINUTES$ minuto/i.",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
@@ -2679,7 +2679,7 @@
|
||||
}
|
||||
},
|
||||
"vaultTimeoutPolicyMaximumError": {
|
||||
"message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum",
|
||||
"message": "Il timeout supera la restrizione impostata dalla tua organizzazione: massimo $HOURS$ ora/e e $MINUTES$ minuto/i",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
@@ -2793,10 +2793,10 @@
|
||||
"message": "Genera nome utente"
|
||||
},
|
||||
"generateEmail": {
|
||||
"message": "Generate email"
|
||||
"message": "Genera email"
|
||||
},
|
||||
"spinboxBoundariesHint": {
|
||||
"message": "Value must be between $MIN$ and $MAX$.",
|
||||
"message": "Il valore deve essere compreso tra $MIN$ e $MAX$.",
|
||||
"description": "Explains spin box minimum and maximum values to the user",
|
||||
"placeholders": {
|
||||
"min": {
|
||||
@@ -2810,7 +2810,7 @@
|
||||
}
|
||||
},
|
||||
"passwordLengthRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
|
||||
"message": " Usa $RECOMMENDED$ caratteri o più per generare una password forte.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2820,7 +2820,7 @@
|
||||
}
|
||||
},
|
||||
"passphraseNumWordsRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
|
||||
"message": " Usa $RECOMMENDED$ parole o più per generare una passphrase forte.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2861,11 +2861,11 @@
|
||||
"message": "Genera un alias email con un servizio di inoltro esterno."
|
||||
},
|
||||
"forwarderDomainName": {
|
||||
"message": "Email domain",
|
||||
"message": "Dominio email",
|
||||
"description": "Labels the domain name email forwarder service option"
|
||||
},
|
||||
"forwarderDomainNameHint": {
|
||||
"message": "Choose a domain that is supported by the selected service",
|
||||
"message": "Scegli un dominio supportato dal servizio selezionato",
|
||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||
},
|
||||
"forwarderError": {
|
||||
@@ -3068,25 +3068,25 @@
|
||||
"message": "Invia notifica di nuovo"
|
||||
},
|
||||
"viewAllLogInOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "Visualizza tutte le opzioni di accesso"
|
||||
},
|
||||
"viewAllLoginOptionsV1": {
|
||||
"message": "View all log in options"
|
||||
"message": "Visualizza tutte le opzioni di accesso"
|
||||
},
|
||||
"notificationSentDevice": {
|
||||
"message": "Una notifica è stata inviata al tuo dispositivo."
|
||||
},
|
||||
"aNotificationWasSentToYourDevice": {
|
||||
"message": "A notification was sent to your device"
|
||||
"message": "Una notifica è stata inviata al tuo dispositivo"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "Assicurati che il tuo account sia sbloccato e che la frase dell'impronta digitale corrisponda nell'altro dispositivo"
|
||||
},
|
||||
"youWillBeNotifiedOnceTheRequestIsApproved": {
|
||||
"message": "You will be notified once the request is approved"
|
||||
"message": "Sarai notificato una volta che la richiesta sarà approvata"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Need another option?"
|
||||
"message": "Bisogno di un'altra opzione?"
|
||||
},
|
||||
"loginInitiated": {
|
||||
"message": "Accesso avviato"
|
||||
@@ -3182,16 +3182,16 @@
|
||||
"message": "Si apre in una nuova finestra"
|
||||
},
|
||||
"rememberThisDeviceToMakeFutureLoginsSeamless": {
|
||||
"message": "Remember this device to make future logins seamless"
|
||||
"message": "Ricorda questo dispositivo per rendere immediati i futuri accessi"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Approvazione del dispositivo obbligatoria. Seleziona un'opzione di approvazione:"
|
||||
},
|
||||
"deviceApprovalRequiredV2": {
|
||||
"message": "Device approval required"
|
||||
"message": "Approvazione dispositivo richiesta"
|
||||
},
|
||||
"selectAnApprovalOptionBelow": {
|
||||
"message": "Select an approval option below"
|
||||
"message": "Seleziona un'opzione di approvazione sotto"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Ricorda questo dispositivo"
|
||||
@@ -3267,7 +3267,7 @@
|
||||
"message": "Email utente mancante"
|
||||
},
|
||||
"activeUserEmailNotFoundLoggingYouOut": {
|
||||
"message": "Active user email not found. Logging you out."
|
||||
"message": "Email utente attiva non trovata. Logout in corso."
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "Dispositivo fidato"
|
||||
@@ -3478,11 +3478,11 @@
|
||||
"description": "Screen reader text (aria-label) for unlock account button in overlay"
|
||||
},
|
||||
"totpCodeAria": {
|
||||
"message": "Time-based One-Time Password Verification Code",
|
||||
"message": "Codice di Verifica One-Time a tempo",
|
||||
"description": "Aria label for the totp code displayed in the inline menu for autofill"
|
||||
},
|
||||
"totpSecondsSpanAria": {
|
||||
"message": "Time remaining before current TOTP expires",
|
||||
"message": "Tempo rimasto prima che l'attuale TOTP scada",
|
||||
"description": "Aria label for the totp seconds displayed in the inline menu for autofill"
|
||||
},
|
||||
"fillCredentialsFor": {
|
||||
@@ -3711,10 +3711,10 @@
|
||||
"message": "Passkey"
|
||||
},
|
||||
"accessing": {
|
||||
"message": "Accessing"
|
||||
"message": "Accedendo a"
|
||||
},
|
||||
"loggedInExclamation": {
|
||||
"message": "Logged in!"
|
||||
"message": "Accesso effettuato!"
|
||||
},
|
||||
"passkeyNotCopied": {
|
||||
"message": "La passkey non sarà copiata"
|
||||
@@ -3741,7 +3741,7 @@
|
||||
"message": "Nessun login corrispondente per questo sito"
|
||||
},
|
||||
"searchSavePasskeyNewLogin": {
|
||||
"message": "Search or save passkey as new login"
|
||||
"message": "Cerca o salva la passkey come nuovo login"
|
||||
},
|
||||
"confirm": {
|
||||
"message": "Conferma"
|
||||
@@ -4208,13 +4208,13 @@
|
||||
"message": "Filtri"
|
||||
},
|
||||
"filterVault": {
|
||||
"message": "Filter vault"
|
||||
"message": "Filtra cassaforte"
|
||||
},
|
||||
"filterApplied": {
|
||||
"message": "One filter applied"
|
||||
"message": "Un filtro applicato"
|
||||
},
|
||||
"filterAppliedPlural": {
|
||||
"message": "$COUNT$ filters applied",
|
||||
"message": "$COUNT$ filtri applicati",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@@ -4328,7 +4328,7 @@
|
||||
"message": "Abilita animazioni"
|
||||
},
|
||||
"showAnimations": {
|
||||
"message": "Show animations"
|
||||
"message": "Mostra animazioni"
|
||||
},
|
||||
"addAccount": {
|
||||
"message": "Aggiungi account"
|
||||
@@ -4546,13 +4546,13 @@
|
||||
"message": "Posizione elemento"
|
||||
},
|
||||
"fileSend": {
|
||||
"message": "File Send"
|
||||
"message": "Send di File"
|
||||
},
|
||||
"fileSends": {
|
||||
"message": "Send File"
|
||||
},
|
||||
"textSend": {
|
||||
"message": "Text Send"
|
||||
"message": "Send di Testo"
|
||||
},
|
||||
"textSends": {
|
||||
"message": "Send Testo"
|
||||
@@ -4570,7 +4570,7 @@
|
||||
"message": "Mostra il numero di suggerimenti di riempimento automatico sull'icona dell'estensione"
|
||||
},
|
||||
"showQuickCopyActions": {
|
||||
"message": "Show quick copy actions on Vault"
|
||||
"message": "Mostra azioni di copia rapida nella Cassaforte"
|
||||
},
|
||||
"systemDefault": {
|
||||
"message": "Predefinito del sistema"
|
||||
@@ -4579,37 +4579,37 @@
|
||||
"message": "I requisiti della policy aziendale sono stati applicati a questa impostazione"
|
||||
},
|
||||
"sshPrivateKey": {
|
||||
"message": "Private key"
|
||||
"message": "Chiave privata"
|
||||
},
|
||||
"sshPublicKey": {
|
||||
"message": "Public key"
|
||||
"message": "Chiave pubblica"
|
||||
},
|
||||
"sshFingerprint": {
|
||||
"message": "Fingerprint"
|
||||
"message": "Impronta digitale"
|
||||
},
|
||||
"sshKeyAlgorithm": {
|
||||
"message": "Key type"
|
||||
"message": "Tipo di chiave"
|
||||
},
|
||||
"sshKeyAlgorithmED25519": {
|
||||
"message": "ED25519"
|
||||
},
|
||||
"sshKeyAlgorithmRSA2048": {
|
||||
"message": "RSA 2048-Bit"
|
||||
"message": "RSA a 2048 bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA3072": {
|
||||
"message": "RSA 3072-Bit"
|
||||
"message": "RSA a 3072 bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA4096": {
|
||||
"message": "RSA 4096-Bit"
|
||||
"message": "RSA a 4096 bit"
|
||||
},
|
||||
"retry": {
|
||||
"message": "Retry"
|
||||
"message": "Riprova"
|
||||
},
|
||||
"vaultCustomTimeoutMinimum": {
|
||||
"message": "Minimum custom timeout is 1 minute."
|
||||
"message": "Il timeout personalizzato minimo è 1 minuto."
|
||||
},
|
||||
"additionalContentAvailable": {
|
||||
"message": "Additional content is available"
|
||||
"message": "Sono disponibili ulteriori contenuti"
|
||||
},
|
||||
"fileSavedToDevice": {
|
||||
"message": "File salvato sul dispositivo. Gestisci dai download del dispositivo."
|
||||
@@ -4642,22 +4642,22 @@
|
||||
"message": "Non hai i permessi per modificare questo elemento"
|
||||
},
|
||||
"authenticating": {
|
||||
"message": "Authenticating"
|
||||
"message": "Autenticazione"
|
||||
},
|
||||
"fillGeneratedPassword": {
|
||||
"message": "Fill generated password",
|
||||
"message": "Riempi password generata",
|
||||
"description": "Heading for the password generator within the inline menu"
|
||||
},
|
||||
"passwordRegenerated": {
|
||||
"message": "Password regenerated",
|
||||
"message": "Password rigenerata",
|
||||
"description": "Notification message for when a password has been regenerated"
|
||||
},
|
||||
"saveLoginToBitwarden": {
|
||||
"message": "Save login to Bitwarden?",
|
||||
"message": "Salvare il login su Bitwarden?",
|
||||
"description": "Confirmation message for saving a login to Bitwarden"
|
||||
},
|
||||
"spaceCharacterDescriptor": {
|
||||
"message": "Space",
|
||||
"message": "Spazio",
|
||||
"description": "Represents the space key in screen reader content as a readable word"
|
||||
},
|
||||
"tildeCharacterDescriptor": {
|
||||
@@ -4669,157 +4669,157 @@
|
||||
"description": "Represents the ` key in screen reader content as a readable word"
|
||||
},
|
||||
"exclamationCharacterDescriptor": {
|
||||
"message": "Exclamation mark",
|
||||
"message": "Punto esclamativo",
|
||||
"description": "Represents the ! key in screen reader content as a readable word"
|
||||
},
|
||||
"atSignCharacterDescriptor": {
|
||||
"message": "At sign",
|
||||
"message": "Chiocciola",
|
||||
"description": "Represents the @ key in screen reader content as a readable word"
|
||||
},
|
||||
"hashSignCharacterDescriptor": {
|
||||
"message": "Hash sign",
|
||||
"message": "Cancelletto",
|
||||
"description": "Represents the # key in screen reader content as a readable word"
|
||||
},
|
||||
"dollarSignCharacterDescriptor": {
|
||||
"message": "Dollar sign",
|
||||
"message": "Simbolo del dollaro",
|
||||
"description": "Represents the $ key in screen reader content as a readable word"
|
||||
},
|
||||
"percentSignCharacterDescriptor": {
|
||||
"message": "Percent sign",
|
||||
"message": "Segno di percentuale",
|
||||
"description": "Represents the % key in screen reader content as a readable word"
|
||||
},
|
||||
"caretCharacterDescriptor": {
|
||||
"message": "Caret",
|
||||
"message": "Accento circonflesso",
|
||||
"description": "Represents the ^ key in screen reader content as a readable word"
|
||||
},
|
||||
"ampersandCharacterDescriptor": {
|
||||
"message": "Ampersand",
|
||||
"message": "E commerciale",
|
||||
"description": "Represents the & key in screen reader content as a readable word"
|
||||
},
|
||||
"asteriskCharacterDescriptor": {
|
||||
"message": "Asterisk",
|
||||
"message": "Asterisco",
|
||||
"description": "Represents the * key in screen reader content as a readable word"
|
||||
},
|
||||
"parenLeftCharacterDescriptor": {
|
||||
"message": "Left parenthesis",
|
||||
"message": "Parentesi sinistra",
|
||||
"description": "Represents the ( key in screen reader content as a readable word"
|
||||
},
|
||||
"parenRightCharacterDescriptor": {
|
||||
"message": "Right parenthesis",
|
||||
"message": "Parentesi destra",
|
||||
"description": "Represents the ) key in screen reader content as a readable word"
|
||||
},
|
||||
"hyphenCharacterDescriptor": {
|
||||
"message": "Underscore",
|
||||
"message": "Trattino basso",
|
||||
"description": "Represents the _ key in screen reader content as a readable word"
|
||||
},
|
||||
"underscoreCharacterDescriptor": {
|
||||
"message": "Hyphen",
|
||||
"message": "Trattino",
|
||||
"description": "Represents the - key in screen reader content as a readable word"
|
||||
},
|
||||
"plusCharacterDescriptor": {
|
||||
"message": "Plus",
|
||||
"message": "Più",
|
||||
"description": "Represents the + key in screen reader content as a readable word"
|
||||
},
|
||||
"equalsCharacterDescriptor": {
|
||||
"message": "Equals",
|
||||
"message": "Uguale",
|
||||
"description": "Represents the = key in screen reader content as a readable word"
|
||||
},
|
||||
"braceLeftCharacterDescriptor": {
|
||||
"message": "Left brace",
|
||||
"message": "Parentesi graffa aperta",
|
||||
"description": "Represents the { key in screen reader content as a readable word"
|
||||
},
|
||||
"braceRightCharacterDescriptor": {
|
||||
"message": "Right brace",
|
||||
"message": "Parentesi graffa chiusa",
|
||||
"description": "Represents the } key in screen reader content as a readable word"
|
||||
},
|
||||
"bracketLeftCharacterDescriptor": {
|
||||
"message": "Left bracket",
|
||||
"message": "Parentesi quadra aperta",
|
||||
"description": "Represents the [ key in screen reader content as a readable word"
|
||||
},
|
||||
"bracketRightCharacterDescriptor": {
|
||||
"message": "Right bracket",
|
||||
"message": "Parentesi quadra chiusa",
|
||||
"description": "Represents the ] key in screen reader content as a readable word"
|
||||
},
|
||||
"pipeCharacterDescriptor": {
|
||||
"message": "Pipe",
|
||||
"message": "Barra verticale",
|
||||
"description": "Represents the | key in screen reader content as a readable word"
|
||||
},
|
||||
"backSlashCharacterDescriptor": {
|
||||
"message": "Back slash",
|
||||
"message": "Barra rovesciata",
|
||||
"description": "Represents the back slash key in screen reader content as a readable word"
|
||||
},
|
||||
"colonCharacterDescriptor": {
|
||||
"message": "Colon",
|
||||
"message": "Due punti",
|
||||
"description": "Represents the : key in screen reader content as a readable word"
|
||||
},
|
||||
"semicolonCharacterDescriptor": {
|
||||
"message": "Semicolon",
|
||||
"message": "Punto e virgola",
|
||||
"description": "Represents the ; key in screen reader content as a readable word"
|
||||
},
|
||||
"doubleQuoteCharacterDescriptor": {
|
||||
"message": "Double quote",
|
||||
"message": "Doppi apici",
|
||||
"description": "Represents the double quote key in screen reader content as a readable word"
|
||||
},
|
||||
"singleQuoteCharacterDescriptor": {
|
||||
"message": "Single quote",
|
||||
"message": "Apostrofo",
|
||||
"description": "Represents the ' key in screen reader content as a readable word"
|
||||
},
|
||||
"lessThanCharacterDescriptor": {
|
||||
"message": "Less than",
|
||||
"message": "Minore",
|
||||
"description": "Represents the < key in screen reader content as a readable word"
|
||||
},
|
||||
"greaterThanCharacterDescriptor": {
|
||||
"message": "Greater than",
|
||||
"message": "Maggiore",
|
||||
"description": "Represents the > key in screen reader content as a readable word"
|
||||
},
|
||||
"commaCharacterDescriptor": {
|
||||
"message": "Comma",
|
||||
"message": "Virgola",
|
||||
"description": "Represents the , key in screen reader content as a readable word"
|
||||
},
|
||||
"periodCharacterDescriptor": {
|
||||
"message": "Period",
|
||||
"message": "Punto",
|
||||
"description": "Represents the . key in screen reader content as a readable word"
|
||||
},
|
||||
"questionCharacterDescriptor": {
|
||||
"message": "Question mark",
|
||||
"message": "Punto interrogativo",
|
||||
"description": "Represents the ? key in screen reader content as a readable word"
|
||||
},
|
||||
"forwardSlashCharacterDescriptor": {
|
||||
"message": "Forward slash",
|
||||
"message": "Slash",
|
||||
"description": "Represents the / key in screen reader content as a readable word"
|
||||
},
|
||||
"lowercaseAriaLabel": {
|
||||
"message": "Lowercase"
|
||||
"message": "Minuscolo"
|
||||
},
|
||||
"uppercaseAriaLabel": {
|
||||
"message": "Uppercase"
|
||||
"message": "Maiuscolo"
|
||||
},
|
||||
"generatedPassword": {
|
||||
"message": "Generated password"
|
||||
"message": "Password generata"
|
||||
},
|
||||
"compactMode": {
|
||||
"message": "Compact mode"
|
||||
"message": "Modalità compatta"
|
||||
},
|
||||
"beta": {
|
||||
"message": "Beta"
|
||||
},
|
||||
"importantNotice": {
|
||||
"message": "Important notice"
|
||||
"message": "Avviso importante"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Set up two-step login"
|
||||
"message": "Imposta accesso in due passaggi"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden invierà un codice all'email del tuo account per verificare gli accessi da nuovi dispositivi a partire da febbraio 2025."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
"message": "Puoi impostare l'accesso in due passaggi come modo alternativo per proteggere il tuo account, o cambiare la tua e-mail in una alla quale puoi accedere."
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
"message": "Ricordamelo più tardi"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "Do you have reliable access to your email, $EMAIL$?",
|
||||
"message": "Riesci ancora ad accedere a questa email, $EMAIL$?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -4828,24 +4828,24 @@
|
||||
}
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessNo": {
|
||||
"message": "No, I do not"
|
||||
"message": "No, non riesco"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "Yes, I can reliably access my email"
|
||||
"message": "Sì, riesco ad accedere a questa email"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "Turn on two-step login"
|
||||
"message": "Attiva accesso in due passaggi"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
"message": "Cambia l'email dell'account"
|
||||
},
|
||||
"extensionWidth": {
|
||||
"message": "Extension width"
|
||||
"message": "Larghezza estensione"
|
||||
},
|
||||
"wide": {
|
||||
"message": "Wide"
|
||||
"message": "Larga"
|
||||
},
|
||||
"extraWide": {
|
||||
"message": "Extra wide"
|
||||
"message": "Molto larga"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "自動入力を簡単にするために、タブページに ID アイテムを表示します"
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "保管庫で、自動入力するアイテムをクリックしてください"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "クリップボードの消去",
|
||||
@@ -4810,10 +4810,10 @@
|
||||
"message": "2段階認証を設定する"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden は2025年2月以降、新しいデバイスからのログイン時にアカウントのメールアドレスに確認コードを送信します。"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
"message": "代わりに2段階認証によるログインでアカウントを保護するか、メールアドレスをあなたがアクセスできるものに変更できます。"
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "後で再通知"
|
||||
@@ -4831,13 +4831,13 @@
|
||||
"message": "いいえ、違います。"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "Yes, I can reliably access my email"
|
||||
"message": "はい、メールアドレスには私が確実にアクセスできます"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "Turn on two-step login"
|
||||
"message": "2段階認証によるログインを有効にする"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
"message": "アカウントのメールアドレスを変更する"
|
||||
},
|
||||
"extensionWidth": {
|
||||
"message": "拡張機能の幅"
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
||||
},
|
||||
"autoFill": {
|
||||
"message": "Auto-invullen"
|
||||
"message": "Automatisch invullen"
|
||||
},
|
||||
"autoFillLogin": {
|
||||
"message": "Login automatisch invullen"
|
||||
@@ -688,7 +688,7 @@
|
||||
"message": "Nu vergrendelen"
|
||||
},
|
||||
"lockAll": {
|
||||
"message": "Vergrendel alles"
|
||||
"message": "Alles vergrendelen"
|
||||
},
|
||||
"immediately": {
|
||||
"message": "Onmiddellijk"
|
||||
@@ -1043,7 +1043,7 @@
|
||||
"message": "Ja, nu bijwerken"
|
||||
},
|
||||
"notificationUnlockDesc": {
|
||||
"message": "Ontgrendel je Bitwarden-kluis om het auto-invulverzoek te voltooien."
|
||||
"message": "Ontgrendel je Bitwarden-kluis om het automatisch invullen verzoek te voltooien."
|
||||
},
|
||||
"notificationUnlock": {
|
||||
"message": "Ontgrendelen"
|
||||
@@ -1200,7 +1200,7 @@
|
||||
"message": "Geen bijlagen."
|
||||
},
|
||||
"attachmentSaved": {
|
||||
"message": "De bijlage is opgeslagen."
|
||||
"message": "Bijlage opgeslagen"
|
||||
},
|
||||
"file": {
|
||||
"message": "Bestand"
|
||||
@@ -1209,7 +1209,7 @@
|
||||
"message": "Bestand om te delen"
|
||||
},
|
||||
"selectFile": {
|
||||
"message": "Selecteer een bestand."
|
||||
"message": "Selecteer een bestand"
|
||||
},
|
||||
"maxFileSize": {
|
||||
"message": "Maximale bestandsgrootte is 500 MB."
|
||||
@@ -1242,7 +1242,7 @@
|
||||
"message": "1 GB versleutelde opslag voor bijlagen."
|
||||
},
|
||||
"premiumSignUpEmergency": {
|
||||
"message": "Noodtoegang"
|
||||
"message": "Noodtoegang."
|
||||
},
|
||||
"premiumSignUpTwoStepOptions": {
|
||||
"message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo."
|
||||
@@ -1425,10 +1425,10 @@
|
||||
"message": "Specificeer de basis-URL van je zelfgehoste Bitwarden-installatie. Bijvoorbeeld: https://bitwarden.company.com"
|
||||
},
|
||||
"selfHostedCustomEnvHeader": {
|
||||
"message": "For advanced configuration, you can specify the base URL of each service independently."
|
||||
"message": "Voor geavanceerde configuratie kun je de basis URL van elke service onafhankelijk opgeven."
|
||||
},
|
||||
"selfHostedEnvFormInvalid": {
|
||||
"message": "You must add either the base Server URL or at least one custom environment."
|
||||
"message": "Je moet de basis URL van de server of ten minste één aangepaste omgeving toevoegen."
|
||||
},
|
||||
"customEnvironment": {
|
||||
"message": "Aangepaste omgeving"
|
||||
@@ -1459,14 +1459,14 @@
|
||||
"message": "Pictogrammenserver-URL"
|
||||
},
|
||||
"environmentSaved": {
|
||||
"message": "De omgeving-URL's zijn opgeslagen."
|
||||
"message": "Omgeving-URL's opgeslagen"
|
||||
},
|
||||
"showAutoFillMenuOnFormFields": {
|
||||
"message": "Auto-invulmenu op formuliervelden weergeven",
|
||||
"message": "Automatisch invullen menu op formuliervelden weergeven",
|
||||
"description": "Represents the message for allowing the user to enable the autofill overlay"
|
||||
},
|
||||
"autofillSuggestionsSectionTitle": {
|
||||
"message": "Suggesties voor automatisch invullen"
|
||||
"message": "Suggesties automatisch invullen"
|
||||
},
|
||||
"showInlineMenuLabel": {
|
||||
"message": "Suggesties voor automatisch invullen op formuliervelden weergeven"
|
||||
@@ -1511,7 +1511,7 @@
|
||||
"message": "Als een inlogformulier wordt gedetecteerd, dan worden de inloggegevens automatisch ingevuld."
|
||||
},
|
||||
"experimentalFeature": {
|
||||
"message": "Gehackte of onbetrouwbare websites kunnen auto-invullen tijdens het laden van de pagina misbruiken."
|
||||
"message": "Gehackte of onbetrouwbare websites kunnen automatisch invullen tijdens het laden van de pagina misbruiken."
|
||||
},
|
||||
"learnMoreAboutAutofillOnPageLoadLinkText": {
|
||||
"message": "Meer over risico's lezen"
|
||||
@@ -1535,7 +1535,7 @@
|
||||
"message": "Automatisch invullen bij laden van pagina"
|
||||
},
|
||||
"autoFillOnPageLoadNo": {
|
||||
"message": "Niet Automatisch invullen bij laden van pagina"
|
||||
"message": "Niet automatisch invullen bij laden van pagina"
|
||||
},
|
||||
"commandOpenPopup": {
|
||||
"message": "Open kluis in pop-up"
|
||||
@@ -1606,7 +1606,7 @@
|
||||
"message": "Een herkenbare afbeelding naast iedere login weergeven."
|
||||
},
|
||||
"faviconDescAlt": {
|
||||
"message": "Show a recognizable image next to each login. Applies to all logged in accounts."
|
||||
"message": "Toon een herkenbare afbeelding naast elke login. Geldt voor alle ingelogde accounts."
|
||||
},
|
||||
"enableBadgeCounter": {
|
||||
"message": "Teller weergeven"
|
||||
@@ -1690,7 +1690,7 @@
|
||||
"message": "Dr."
|
||||
},
|
||||
"mx": {
|
||||
"message": "Mx"
|
||||
"message": "Mx."
|
||||
},
|
||||
"firstName": {
|
||||
"message": "Voornaam"
|
||||
@@ -1993,10 +1993,10 @@
|
||||
"message": "Ontgrendelen met PIN"
|
||||
},
|
||||
"setYourPinTitle": {
|
||||
"message": "PIN-code instellen"
|
||||
"message": "PIN instellen"
|
||||
},
|
||||
"setYourPinButton": {
|
||||
"message": "PIN-code instellen"
|
||||
"message": "PIN instellen"
|
||||
},
|
||||
"setYourPinCode": {
|
||||
"message": "Stel je PIN-code in voor het ontgrendelen van Bitwarden. Je PIN-code wordt opnieuw ingesteld als je je ooit volledig afmeldt bij de applicatie."
|
||||
@@ -2111,10 +2111,10 @@
|
||||
"message": "Invullen en opslaan"
|
||||
},
|
||||
"autoFillSuccessAndSavedUri": {
|
||||
"message": "Automatisch gevuld item en opgeslagen URI"
|
||||
"message": "Automatisch ingevuld item en opgeslagen URI"
|
||||
},
|
||||
"autoFillSuccess": {
|
||||
"message": "Automatisch gevuld item"
|
||||
"message": "Item automatisch ingevuld "
|
||||
},
|
||||
"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."
|
||||
@@ -2225,7 +2225,7 @@
|
||||
"message": "Fout bij vernieuwen toegangstoken"
|
||||
},
|
||||
"errorRefreshingAccessTokenDesc": {
|
||||
"message": "No refresh token or API keys found. Please try logging out and logging back in."
|
||||
"message": "Geen verversingstoken of API-sleutels gevonden. Probeer uit te loggen en weer in te loggen."
|
||||
},
|
||||
"desktopSyncVerificationTitle": {
|
||||
"message": "Desktopsynchronisatieverificatie"
|
||||
@@ -2282,10 +2282,10 @@
|
||||
"message": "Dit apparaat ondersteunt geen browserbiometrie."
|
||||
},
|
||||
"biometricsNotUnlockedTitle": {
|
||||
"message": "User locked or logged out"
|
||||
"message": "Gebruiker vergrendeld of uitgelogd"
|
||||
},
|
||||
"biometricsNotUnlockedDesc": {
|
||||
"message": "Please unlock this user in the desktop application and try again."
|
||||
"message": "Ontgrendel deze gebruiker in de desktopapplicatie en probeer het opnieuw."
|
||||
},
|
||||
"biometricsNotAvailableTitle": {
|
||||
"message": "Biometrisch ontgrendelen niet beschikbaar"
|
||||
@@ -2623,11 +2623,11 @@
|
||||
"description": "Used as a message within the notification bar when no folders are found"
|
||||
},
|
||||
"orgPermissionsUpdatedMustSetPassword": {
|
||||
"message": "Your organization permissions were updated, requiring you to set a master password.",
|
||||
"message": "De machtigingen van je organisatie zijn bijgewerkt, waardoor je een hoofdwachtwoord moet instellen.",
|
||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||
},
|
||||
"orgRequiresYouToSetPassword": {
|
||||
"message": "Your organization requires you to set a master password.",
|
||||
"message": "Je organisatie vereist dat je een hoofdwachtwoord instelt.",
|
||||
"description": "Used as a card title description on the set password page to explain why the user is there"
|
||||
},
|
||||
"cardMetrics": {
|
||||
@@ -2766,7 +2766,7 @@
|
||||
"message": "Persoonlijke kluis exporteren"
|
||||
},
|
||||
"exportingIndividualVaultDescription": {
|
||||
"message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.",
|
||||
"message": "Alleen de individuele kluisitems die gekoppeld zijn aan $EMAIL$ worden geëxporteerd. Kluisitems van organisaties worden niet meegenomen. Alleen de informatie over het kluisitem wordt geëxporteerd en niet de bijbehorende bijlagen.",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -2869,7 +2869,7 @@
|
||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||
},
|
||||
"forwarderError": {
|
||||
"message": "$SERVICENAME$ error: $ERRORMESSAGE$",
|
||||
"message": "$SERVICENAME$ fout: $ERRORMESSAGE$",
|
||||
"description": "Reports an error returned by a forwarding service to the user.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2887,7 +2887,7 @@
|
||||
"description": "Displayed with the address on the forwarding service's configuration screen."
|
||||
},
|
||||
"forwarderGeneratedByWithWebsite": {
|
||||
"message": "Website: $WEBSITE$. Generated by Bitwarden.",
|
||||
"message": "Website: $WEBSITE$. Gegenereerd door Bitwarden.",
|
||||
"description": "Displayed with the address on the forwarding service's configuration screen.",
|
||||
"placeholders": {
|
||||
"WEBSITE": {
|
||||
@@ -2897,7 +2897,7 @@
|
||||
}
|
||||
},
|
||||
"forwaderInvalidToken": {
|
||||
"message": "Invalid $SERVICENAME$ API token",
|
||||
"message": "Ongeldig $SERVICENAME$ API token",
|
||||
"description": "Displayed when the user's API token is empty or rejected by the forwarding service.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2907,7 +2907,7 @@
|
||||
}
|
||||
},
|
||||
"forwaderInvalidTokenWithMessage": {
|
||||
"message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$",
|
||||
"message": "Ongeldige $SERVICENAME$ API token: $ERRORMESSAGE$",
|
||||
"description": "Displayed when the user's API token is rejected by the forwarding service with an error message.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2921,7 +2921,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderNoAccountId": {
|
||||
"message": "Unable to obtain $SERVICENAME$ masked email account ID.",
|
||||
"message": "Kan $SERVICENAME$ gemaskeerde e-mailaccount-ID niet verkrijgen.",
|
||||
"description": "Displayed when the forwarding service fails to return an account ID.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2931,7 +2931,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderNoDomain": {
|
||||
"message": "Invalid $SERVICENAME$ domain.",
|
||||
"message": "Ongeldig $SERVICENAME$ domein.",
|
||||
"description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2941,7 +2941,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderNoUrl": {
|
||||
"message": "Invalid $SERVICENAME$ url.",
|
||||
"message": "Ongeldige $SERVICENAME$ url.",
|
||||
"description": "Displayed when the url of the forwarding service wasn't supplied.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2951,7 +2951,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderUnknownError": {
|
||||
"message": "Unknown $SERVICENAME$ error occurred.",
|
||||
"message": "Onbekende $SERVICENAME$ fout opgetreden.",
|
||||
"description": "Displayed when the forwarding service failed due to an unknown error.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -2961,7 +2961,7 @@
|
||||
}
|
||||
},
|
||||
"forwarderUnknownForwarder": {
|
||||
"message": "Unknown forwarder: '$SERVICENAME$'.",
|
||||
"message": "Onbekende doorstuurder: '$SERVICENAME$'.",
|
||||
"description": "Displayed when the forwarding service is not supported.",
|
||||
"placeholders": {
|
||||
"servicename": {
|
||||
@@ -3017,7 +3017,7 @@
|
||||
"message": "zelfgehost"
|
||||
},
|
||||
"thirdParty": {
|
||||
"message": "van derden"
|
||||
"message": "Derde partij"
|
||||
},
|
||||
"thirdPartyServerMessage": {
|
||||
"message": "Verbonden met server implementatie van derden, $SERVERNAME$. Reproduceer bugs via de officiële server, of meld ze bij het serverproject.",
|
||||
@@ -3071,7 +3071,7 @@
|
||||
"message": "Alle inlogopties bekijken"
|
||||
},
|
||||
"viewAllLoginOptionsV1": {
|
||||
"message": "View all log in options"
|
||||
"message": "Alle inlogopties weergeven"
|
||||
},
|
||||
"notificationSentDevice": {
|
||||
"message": "Er is een melding naar je apparaat verzonden."
|
||||
@@ -3125,10 +3125,10 @@
|
||||
"message": "Je organisatiebeleid heeft het automatisch invullen bij laden van pagina ingeschakeld."
|
||||
},
|
||||
"howToAutofill": {
|
||||
"message": "Hoe automatisch aanvullen"
|
||||
"message": "Hoe automatisch invullen"
|
||||
},
|
||||
"autofillSelectInfoWithCommand": {
|
||||
"message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.",
|
||||
"message": "Selecteer een item in dit scherm, gebruik de sneltoets $COMMAND$ of verken andere opties in de instellingen.",
|
||||
"placeholders": {
|
||||
"command": {
|
||||
"content": "$1",
|
||||
@@ -3137,16 +3137,16 @@
|
||||
}
|
||||
},
|
||||
"autofillSelectInfoWithoutCommand": {
|
||||
"message": "Select an item from this screen, or explore other options in settings."
|
||||
"message": "Selecteer een item in dit scherm of verken andere opties in de instellingen."
|
||||
},
|
||||
"gotIt": {
|
||||
"message": "Ik snap het"
|
||||
"message": "Oké"
|
||||
},
|
||||
"autofillSettings": {
|
||||
"message": "Instellingen automatisch invullen"
|
||||
},
|
||||
"autofillKeyboardShortcutSectionTitle": {
|
||||
"message": "Shortcut voor automatisch invullen"
|
||||
"message": "Snelkoppeling automatisch invullen"
|
||||
},
|
||||
"autofillKeyboardShortcutUpdateLabel": {
|
||||
"message": "Snelkoppeling wijzigen"
|
||||
@@ -3243,7 +3243,7 @@
|
||||
"message": "Algemeen"
|
||||
},
|
||||
"display": {
|
||||
"message": "Display"
|
||||
"message": "Weergave"
|
||||
},
|
||||
"accountSuccessfullyCreated": {
|
||||
"message": "Account succesvol aangemaakt!"
|
||||
@@ -3372,7 +3372,7 @@
|
||||
"message": "-- Type om te filteren --"
|
||||
},
|
||||
"multiSelectLoading": {
|
||||
"message": "Opties ophalen..."
|
||||
"message": "Opties ophalen…"
|
||||
},
|
||||
"multiSelectNotFound": {
|
||||
"message": "Geen items gevonden"
|
||||
@@ -3413,7 +3413,7 @@
|
||||
"description": "Notification button text for starting a fileless import."
|
||||
},
|
||||
"importing": {
|
||||
"message": "Importeren...",
|
||||
"message": "Importeren…",
|
||||
"description": "Notification message for when an import is in progress."
|
||||
},
|
||||
"dataSuccessfullyImported": {
|
||||
@@ -3432,33 +3432,33 @@
|
||||
"message": "Aliasdomein"
|
||||
},
|
||||
"passwordRepromptDisabledAutofillOnPageLoad": {
|
||||
"message": "Items with master password re-prompt cannot be autofilled on page load. Autofill on page load turned off.",
|
||||
"message": "Items met hoofdwachtwoord re-prompt kunnen niet automatisch worden ingevuld bij het laden van de pagina. Automatisch invullen bij laden van pagina uitgeschakeld.",
|
||||
"description": "Toast message for describing that master password re-prompt cannot be autofilled on page load."
|
||||
},
|
||||
"autofillOnPageLoadSetToDefault": {
|
||||
"message": "Autofill on page load set to use default setting.",
|
||||
"message": "Automatisch invullen bij het laden van een pagina ingesteld op de standaardinstelling.",
|
||||
"description": "Toast message for informing the user that autofill on page load has been set to the default setting."
|
||||
},
|
||||
"turnOffMasterPasswordPromptToEditField": {
|
||||
"message": "Turn off master password re-prompt to edit this field",
|
||||
"message": "Hoofdwachtwoord herhaalprompt uitschakelen om dit veld te bewerken",
|
||||
"description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item."
|
||||
},
|
||||
"toggleSideNavigation": {
|
||||
"message": "Zijnavigatie schakelen"
|
||||
},
|
||||
"skipToContent": {
|
||||
"message": "Skip to content"
|
||||
"message": "Overslaan naar inhoud"
|
||||
},
|
||||
"bitwardenOverlayButton": {
|
||||
"message": "Menuknop Bitwarden automatisch invullen",
|
||||
"description": "Page title for the iframe containing the overlay button"
|
||||
},
|
||||
"toggleBitwardenVaultOverlay": {
|
||||
"message": "Bitwarden auto-invulmenu in- en uitschakelen",
|
||||
"message": "Bitwarden automatisch invullen menu in- en uitschakelen",
|
||||
"description": "Screen reader and tool tip label for the overlay button"
|
||||
},
|
||||
"bitwardenVault": {
|
||||
"message": "Bitwarden auto-invulmenu",
|
||||
"message": "Bitwarden automatisch invullen menu",
|
||||
"description": "Page title in overlay"
|
||||
},
|
||||
"unlockYourAccountToViewMatchingLogins": {
|
||||
@@ -3486,7 +3486,7 @@
|
||||
"description": "Aria label for the totp seconds displayed in the inline menu for autofill"
|
||||
},
|
||||
"fillCredentialsFor": {
|
||||
"message": "Inloggegevens invullen voor",
|
||||
"message": "Referenties invullen voor",
|
||||
"description": "Screen reader text for when overlay item is in focused"
|
||||
},
|
||||
"partialUsername": {
|
||||
@@ -3530,7 +3530,7 @@
|
||||
"description": "Screen reader text (aria-label) for new identity button within inline menu"
|
||||
},
|
||||
"bitwardenOverlayMenuAvailable": {
|
||||
"message": "Bitwarden auto-invulmenu beschikbaar. Druk op de pijltjestoets omlaag om te selecteren.",
|
||||
"message": "Bitwarden automatisch invullen menu beschikbaar. Druk op de pijltjestoets omlaag om te selecteren.",
|
||||
"description": "Screen reader text for announcing when the overlay opens on the page"
|
||||
},
|
||||
"turnOn": {
|
||||
@@ -3574,7 +3574,7 @@
|
||||
"message": "Deze actie vereist verificatie actie. Stel een pincode in om door te gaan."
|
||||
},
|
||||
"setPin": {
|
||||
"message": "Pincode instellen"
|
||||
"message": "PIN instellen"
|
||||
},
|
||||
"verifyWithBiometrics": {
|
||||
"message": "Biometrisch ontgrendelen"
|
||||
@@ -3619,7 +3619,7 @@
|
||||
"message": "Fout bij het verbinden met de Duo-service. Gebruik een andere tweestapsaanmeldingsmethode of neem contact op met Duo voor hulp."
|
||||
},
|
||||
"launchDuoAndFollowStepsToFinishLoggingIn": {
|
||||
"message": "Launch Duo and follow the steps to finish logging in."
|
||||
"message": "Start Duo en volg de stappen om het inloggen te voltooien."
|
||||
},
|
||||
"duoRequiredForAccount": {
|
||||
"message": "Jouw account vereist Duo-tweestapsaanmelding."
|
||||
@@ -3628,10 +3628,10 @@
|
||||
"message": "Open de extensie om in te loggen."
|
||||
},
|
||||
"popoutExtension": {
|
||||
"message": "Popout extension"
|
||||
"message": "Pop-out extensie"
|
||||
},
|
||||
"launchDuo": {
|
||||
"message": "Launch Duo"
|
||||
"message": "Duo starten"
|
||||
},
|
||||
"importFormatError": {
|
||||
"message": "De gegevens zijn niet correct opgemaakt. Controleer je importbestand en probeer het opnieuw."
|
||||
@@ -3759,7 +3759,7 @@
|
||||
"message": "Kies een passkey om mee in te loggen"
|
||||
},
|
||||
"passkeyItem": {
|
||||
"message": "Passkey-Item"
|
||||
"message": "Passkey item"
|
||||
},
|
||||
"overwritePasskey": {
|
||||
"message": "Passkey overschrijven?"
|
||||
@@ -3801,7 +3801,7 @@
|
||||
"message": "LastPass Email"
|
||||
},
|
||||
"importingYourAccount": {
|
||||
"message": "Account impoteren..."
|
||||
"message": "Account impoteren…"
|
||||
},
|
||||
"lastPassMFARequired": {
|
||||
"message": "LastPass multifactor-authenticatie vereist"
|
||||
@@ -3825,10 +3825,10 @@
|
||||
"message": "Wacht op SSO-authenticatie"
|
||||
},
|
||||
"awaitingSSODesc": {
|
||||
"message": "Ga door met inloggen met de inloggegevens van je bedrijf."
|
||||
"message": "Ga door met inloggen met de referenties van je bedrijf."
|
||||
},
|
||||
"seeDetailedInstructions": {
|
||||
"message": "See detailed instructions on our help site at",
|
||||
"message": "Zie gedetailleerde instructies op onze helpsite op",
|
||||
"description": "This is followed a by a hyperlink to the help website."
|
||||
},
|
||||
"importDirectlyFromLastPass": {
|
||||
@@ -3844,52 +3844,52 @@
|
||||
"message": "Collectie"
|
||||
},
|
||||
"lastPassYubikeyDesc": {
|
||||
"message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button."
|
||||
"message": "Steek de YubiKey die bij je LastPass account hoort in de USB poort van je computer en druk dan op de knop."
|
||||
},
|
||||
"switchAccount": {
|
||||
"message": "Switch account"
|
||||
"message": "Account wisselen"
|
||||
},
|
||||
"switchAccounts": {
|
||||
"message": "Switch accounts"
|
||||
"message": "Accounts wisselen"
|
||||
},
|
||||
"switchToAccount": {
|
||||
"message": "Switch to account"
|
||||
"message": "Wisselen naar account"
|
||||
},
|
||||
"activeAccount": {
|
||||
"message": "Active account"
|
||||
"message": "Actief account"
|
||||
},
|
||||
"availableAccounts": {
|
||||
"message": "Beschikbare accounts"
|
||||
},
|
||||
"accountLimitReached": {
|
||||
"message": "Account limit reached. Log out of an account to add another."
|
||||
"message": "Accountlimiet bereikt. Log uit bij een account om een andere toe te voegen."
|
||||
},
|
||||
"active": {
|
||||
"message": "active"
|
||||
"message": "actief"
|
||||
},
|
||||
"locked": {
|
||||
"message": "locked"
|
||||
"message": "vergrendeld"
|
||||
},
|
||||
"unlocked": {
|
||||
"message": "unlocked"
|
||||
"message": "ontgrendeld"
|
||||
},
|
||||
"server": {
|
||||
"message": "server"
|
||||
},
|
||||
"hostedAt": {
|
||||
"message": "hosted at"
|
||||
"message": "gehost op"
|
||||
},
|
||||
"useDeviceOrHardwareKey": {
|
||||
"message": "Use your device or hardware key"
|
||||
"message": "Gebruik je apparaat of hardwaresleutel"
|
||||
},
|
||||
"justOnce": {
|
||||
"message": "Just once"
|
||||
"message": "Eenmalig"
|
||||
},
|
||||
"alwaysForThisSite": {
|
||||
"message": "Always for this site"
|
||||
"message": "Altijd voor deze site"
|
||||
},
|
||||
"domainAddedToExcludedDomains": {
|
||||
"message": "$DOMAIN$ added to excluded domains.",
|
||||
"message": "$DOMAIN$ toegevoegd aan uitgesloten domeinen.",
|
||||
"placeholders": {
|
||||
"domain": {
|
||||
"content": "$1",
|
||||
@@ -3950,7 +3950,7 @@
|
||||
"description": "Button text for the setting that allows overriding the default browser autofill settings"
|
||||
},
|
||||
"saveCipherAttemptSuccess": {
|
||||
"message": "Credentials saved successfully!",
|
||||
"message": "Referenties succesvol opgeslagen!",
|
||||
"description": "Notification message for when saving credentials has succeeded."
|
||||
},
|
||||
"passwordSaved": {
|
||||
@@ -3958,7 +3958,7 @@
|
||||
"description": "Notification message for when saving credentials has succeeded."
|
||||
},
|
||||
"updateCipherAttemptSuccess": {
|
||||
"message": "Credentials updated successfully!",
|
||||
"message": "Referenties succesvol bijgewerkt!",
|
||||
"description": "Notification message for when updating credentials has succeeded."
|
||||
},
|
||||
"passwordUpdated": {
|
||||
@@ -3966,7 +3966,7 @@
|
||||
"description": "Notification message for when updating credentials has succeeded."
|
||||
},
|
||||
"saveCipherAttemptFailed": {
|
||||
"message": "Error saving credentials. Check console for details.",
|
||||
"message": "Fout bij het opslaan van referenties. Controleer console voor details.",
|
||||
"description": "Notification message for when saving credentials has failed."
|
||||
},
|
||||
"success": {
|
||||
@@ -3979,7 +3979,7 @@
|
||||
"message": "Passkey verwijderd"
|
||||
},
|
||||
"autofillSuggestions": {
|
||||
"message": "Suggesties voor automatisch invullen"
|
||||
"message": "Suggesties automatisch invullen"
|
||||
},
|
||||
"autofillSuggestionsTip": {
|
||||
"message": "Inlogitem opslaan voor automatisch invullen op deze site"
|
||||
@@ -3994,7 +3994,7 @@
|
||||
"message": "Wis filters of probeer een andere zoekterm"
|
||||
},
|
||||
"copyInfoTitle": {
|
||||
"message": "Copy info - $ITEMNAME$",
|
||||
"message": "Info kopiëren - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with options to copy information from an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@@ -4004,7 +4004,7 @@
|
||||
}
|
||||
},
|
||||
"copyNoteTitle": {
|
||||
"message": "Copy Note - $ITEMNAME$",
|
||||
"message": "Notitie kopiëren - $ITEMNAME$",
|
||||
"description": "Title for a button copies a note to the clipboard.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@@ -4014,7 +4014,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsLabel": {
|
||||
"message": "More options, $ITEMNAME$",
|
||||
"message": "Meer opties, $ITEMNAME$",
|
||||
"description": "Aria label for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@@ -4024,7 +4024,7 @@
|
||||
}
|
||||
},
|
||||
"moreOptionsTitle": {
|
||||
"message": "More options - $ITEMNAME$",
|
||||
"message": "Meer opties - $ITEMNAME$",
|
||||
"description": "Title for a button that opens a menu with more options for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@@ -4034,7 +4034,7 @@
|
||||
}
|
||||
},
|
||||
"viewItemTitle": {
|
||||
"message": "View item - $ITEMNAME$",
|
||||
"message": "Item bekijken - $ITEMNAME$",
|
||||
"description": "Title for a link that opens a view for an item.",
|
||||
"placeholders": {
|
||||
"itemname": {
|
||||
@@ -4060,22 +4060,22 @@
|
||||
"message": "Aan collecties toewijzen"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
"message": "E-mail kopiëren"
|
||||
},
|
||||
"copyPhone": {
|
||||
"message": "Copy phone"
|
||||
"message": "Telefoon kopiëren"
|
||||
},
|
||||
"copyAddress": {
|
||||
"message": "Copy address"
|
||||
"message": "Adres kopiëren"
|
||||
},
|
||||
"adminConsole": {
|
||||
"message": "Admin Console"
|
||||
"message": "Beheerconsole"
|
||||
},
|
||||
"accountSecurity": {
|
||||
"message": "Accountbeveiliging"
|
||||
},
|
||||
"notifications": {
|
||||
"message": "Notifications"
|
||||
"message": "Meldingen"
|
||||
},
|
||||
"appearance": {
|
||||
"message": "Uiterlijk"
|
||||
@@ -4107,7 +4107,7 @@
|
||||
}
|
||||
},
|
||||
"new": {
|
||||
"message": "New"
|
||||
"message": "Nieuw"
|
||||
},
|
||||
"removeItem": {
|
||||
"message": "Verwijder $NAME$",
|
||||
@@ -4245,7 +4245,7 @@
|
||||
"description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher."
|
||||
},
|
||||
"loginCredentials": {
|
||||
"message": "Inloggegevens"
|
||||
"message": "Login referenties"
|
||||
},
|
||||
"authenticatorKey": {
|
||||
"message": "Authenticatiesleutel"
|
||||
@@ -4546,10 +4546,10 @@
|
||||
"message": "Itemlocatie"
|
||||
},
|
||||
"fileSend": {
|
||||
"message": "Bestand-Sends"
|
||||
"message": "Bestand verzenden"
|
||||
},
|
||||
"fileSends": {
|
||||
"message": "Bestand-Sends"
|
||||
"message": "Bestanden verzenden"
|
||||
},
|
||||
"textSend": {
|
||||
"message": "Tekst-Sends"
|
||||
@@ -4567,7 +4567,7 @@
|
||||
"message": "Accountacties"
|
||||
},
|
||||
"showNumberOfAutofillSuggestions": {
|
||||
"message": "Aantal login-autofill-suggesties op het extensie-pictogram weergeven"
|
||||
"message": "Aantal login automatisch invullen suggesties op het extensie-pictogram weergeven"
|
||||
},
|
||||
"showQuickCopyActions": {
|
||||
"message": "Toon snelle kopieeracties in de kluis"
|
||||
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "Pokaż elementy tożsamości na stronie głównej, aby ułatwić autouzupełnianie."
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "Kliknij na dane logowania, aby autouzupełnić w widoku Sejfu"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "Wyczyść schowek",
|
||||
|
||||
@@ -803,7 +803,7 @@
|
||||
"message": "Código de verificação inválido"
|
||||
},
|
||||
"valueCopied": {
|
||||
"message": "$VALUE$ copiado",
|
||||
"message": "$VALUE$ copiado(a)",
|
||||
"description": "Value has been copied to the clipboard.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
|
||||
@@ -193,10 +193,10 @@
|
||||
"message": "Ауто-пуњење идентитета"
|
||||
},
|
||||
"fillVerificationCode": {
|
||||
"message": "Fill verification code"
|
||||
"message": "Пуни верификациони кôд"
|
||||
},
|
||||
"fillVerificationCodeAria": {
|
||||
"message": "Fill Verification Code",
|
||||
"message": "Пуни верификациони кôд",
|
||||
"description": "Aria label for the heading displayed the inline menu for totp code autofill"
|
||||
},
|
||||
"generatePasswordCopied": {
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "Прикажи ставке идентитета на страници за лакше аутоматско допуњавање."
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "Кликните на ставке за ауто-попуњавање у приказу сефа"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "Обриши привремену меморију",
|
||||
@@ -3071,7 +3071,7 @@
|
||||
"message": "Погледајте сав извештај у опције"
|
||||
},
|
||||
"viewAllLoginOptionsV1": {
|
||||
"message": "View all log in options"
|
||||
"message": "Погледајте сав извештај у опције"
|
||||
},
|
||||
"notificationSentDevice": {
|
||||
"message": "Обавештење је послато на ваш уређај."
|
||||
@@ -3478,11 +3478,11 @@
|
||||
"description": "Screen reader text (aria-label) for unlock account button in overlay"
|
||||
},
|
||||
"totpCodeAria": {
|
||||
"message": "Time-based One-Time Password Verification Code",
|
||||
"message": "Једнократни верификациони кôд заснован на времену",
|
||||
"description": "Aria label for the totp code displayed in the inline menu for autofill"
|
||||
},
|
||||
"totpSecondsSpanAria": {
|
||||
"message": "Time remaining before current TOTP expires",
|
||||
"message": "Преостало време до истека актуелног ТОТП-а",
|
||||
"description": "Aria label for the totp seconds displayed in the inline menu for autofill"
|
||||
},
|
||||
"fillCredentialsFor": {
|
||||
@@ -4570,7 +4570,7 @@
|
||||
"message": "Прикажи број предлога за ауто-попуњавање пријаве на икони додатка"
|
||||
},
|
||||
"showQuickCopyActions": {
|
||||
"message": "Show quick copy actions on Vault"
|
||||
"message": "Приказати брзе радње копирања у Сефу"
|
||||
},
|
||||
"systemDefault": {
|
||||
"message": "Системски подразумевано"
|
||||
@@ -4804,22 +4804,22 @@
|
||||
"message": "Бета"
|
||||
},
|
||||
"importantNotice": {
|
||||
"message": "Important notice"
|
||||
"message": "Важно обавештење"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Set up two-step login"
|
||||
"message": "Поставити дво-степенску пријаву"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden ће послати кôд на имејл вашег налога за верификовање пријављивања са нових уређаја почевши од фебруара 2025."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
"message": "Можете да подесите пријаву у два корака као алтернативни начин да заштитите свој налог или да промените свој имејл у један који можете да приступите."
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
"message": "Подсети ме касније"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "Do you have reliable access to your email, $EMAIL$?",
|
||||
"message": "Да ли имате поуздан приступ својим имејлом, $EMAIL$?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -4828,16 +4828,16 @@
|
||||
}
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessNo": {
|
||||
"message": "No, I do not"
|
||||
"message": "Не, ненам"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "Yes, I can reliably access my email"
|
||||
"message": "Да, могу поуздано да приступим овим имејлом"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "Turn on two-step login"
|
||||
"message": "Упалити дво-степенску пријаву"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
"message": "Променити имејл налога"
|
||||
},
|
||||
"extensionWidth": {
|
||||
"message": "Ширина додатка"
|
||||
|
||||
@@ -4154,7 +4154,7 @@
|
||||
"message": "Ek bilgiler"
|
||||
},
|
||||
"itemHistory": {
|
||||
"message": "Öğe geçmişi"
|
||||
"message": "Kayıt geçmişi"
|
||||
},
|
||||
"lastEdited": {
|
||||
"message": "Son düzenlenme"
|
||||
|
||||
@@ -1005,7 +1005,7 @@
|
||||
"message": "Показувати список посвідчень на сторінці вкладки для легкого автозаповнення."
|
||||
},
|
||||
"clickToAutofillOnVault": {
|
||||
"message": "Click items to autofill on Vault view"
|
||||
"message": "Натисніть на запис у режимі перегляду сховища для автозаповнення"
|
||||
},
|
||||
"clearClipboard": {
|
||||
"message": "Очистити буфер обміну",
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
"message": "前往网页 App 吗?"
|
||||
},
|
||||
"continueToWebAppDesc": {
|
||||
"message": "在网页应用上探索 Bitwarden 账户的更多功能。"
|
||||
"message": "在网页 App 上探索 Bitwarden 账户的更多功能。"
|
||||
},
|
||||
"continueToHelpCenter": {
|
||||
"message": "前往帮助中心吗?"
|
||||
@@ -299,7 +299,7 @@
|
||||
"message": "帮助别人了解 Bitwarden 是否适合他们。立即访问浏览器的扩展程序商店并留下评分。"
|
||||
},
|
||||
"changeMasterPasswordOnWebConfirmation": {
|
||||
"message": "您可以在 Bitwarden 网页应用上更改您的主密码。"
|
||||
"message": "您可以在 Bitwarden 网页 App 上更改您的主密码。"
|
||||
},
|
||||
"fingerprintPhrase": {
|
||||
"message": "指纹短语",
|
||||
@@ -428,7 +428,7 @@
|
||||
"description": "Short for 'credential generator'."
|
||||
},
|
||||
"passGenInfo": {
|
||||
"message": "自动生成安全可靠唯一的登录密码。"
|
||||
"message": "自动为您的登录生成强大且唯一的密码。"
|
||||
},
|
||||
"bitWebVaultApp": {
|
||||
"message": "Bitwarden 网页 App"
|
||||
@@ -597,7 +597,7 @@
|
||||
"message": "前往"
|
||||
},
|
||||
"launchWebsite": {
|
||||
"message": "启动网站"
|
||||
"message": "打开网站"
|
||||
},
|
||||
"launchWebsiteName": {
|
||||
"message": "前往 $ITEMNAME$ 的网站",
|
||||
@@ -763,7 +763,7 @@
|
||||
"message": "必须填写确认主密码。"
|
||||
},
|
||||
"masterPasswordMinlength": {
|
||||
"message": "主密码必须至少 $VALUE$ 个字符长度。",
|
||||
"message": "主密码长度必须至少为 $VALUE$ 个字符。",
|
||||
"description": "The Master Password must be at least a specific number of characters long.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
@@ -888,7 +888,7 @@
|
||||
"message": "两步登录要求您从其他设备(例如安全钥匙、验证器 App、短信、电话或者电子邮件)来验证您的登录,这能使您的账户更加安全。两步登录需要在 bitwarden.com 网页版密码库中设置。现在访问此网站吗?"
|
||||
},
|
||||
"twoStepLoginConfirmationContent": {
|
||||
"message": "通过在 Bitwarden 网页 App 中设置两步登录,可以使您的账户更加安全。"
|
||||
"message": "在 Bitwarden 网页 App 中设置两步登录,让您的账户更加安全。"
|
||||
},
|
||||
"twoStepLoginConfirmationTitle": {
|
||||
"message": "前往网页 App 吗?"
|
||||
@@ -1071,7 +1071,7 @@
|
||||
"message": "主题"
|
||||
},
|
||||
"themeDesc": {
|
||||
"message": "更改本应用程序的颜色主题。"
|
||||
"message": "更改应用程序的颜色主题。"
|
||||
},
|
||||
"themeDescAlt": {
|
||||
"message": "更改应用程序的颜色主题。适用于所有已登录的账户。"
|
||||
@@ -1133,7 +1133,7 @@
|
||||
"message": "确认密码库导出"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "导出的密码库数据包含未加密格式。您不应该通过不安全的渠道(例如电子邮件)来存储或发送导出的文件。用完后请立即将其删除。"
|
||||
"message": "此导出包含未加密格式的密码库数据。您不应该通过不安全的渠道(例如电子邮件)来存储或发送此导出文件。使用完后请立即将其删除。"
|
||||
},
|
||||
"encExportKeyWarningDesc": {
|
||||
"message": "此导出将使用您账户的加密密钥来加密您的数据。如果您曾经轮换过账户的加密密钥,您应将其重新导出,否则您将无法解密导出的文件。"
|
||||
@@ -2123,7 +2123,7 @@
|
||||
"message": "您仍然想要填充此登录信息吗?"
|
||||
},
|
||||
"autofillIframeWarning": {
|
||||
"message": "该表单由不同于您保存的登录的 URI 域名托管。选择「确定」以自动填充,或选择「取消」停止填充。"
|
||||
"message": "该表单由与您保存的登录 URI 不同的域名托管。选择「确定」继续自动填充,或选择「取消」停止自动填充。"
|
||||
},
|
||||
"autofillIframeWarningTip": {
|
||||
"message": "要防止以后出现此警告,请将此站点的 URI $HOSTNAME$ 保存到您的 Bitwarden 登录项目中。",
|
||||
@@ -3982,7 +3982,7 @@
|
||||
"message": "自动填充建议"
|
||||
},
|
||||
"autofillSuggestionsTip": {
|
||||
"message": "保存此站点的登录项目用来自动填充"
|
||||
"message": "将此站点保存为登录项目以用于自动填充"
|
||||
},
|
||||
"yourVaultIsEmpty": {
|
||||
"message": "您的密码库是空的"
|
||||
@@ -4810,7 +4810,7 @@
|
||||
"message": "设置两步登录"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "从 2025 年 02 月开始,Bitwarden 将向您的账户电子邮箱发送一个代码,以验证来自新设备的登录。"
|
||||
"message": "从 2025 年 02 月起,当有来自新设备的登录时,Bitwarden 将向您的账户电子邮箱发送验证码。"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "您可以设置两步登录作为保护账户的替代方法,或将您的电子邮箱更改为您可以访问的电子邮箱。"
|
||||
@@ -4819,7 +4819,7 @@
|
||||
"message": "稍后提醒我"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "您能可靠地访问您的电子邮箱 $EMAIL$ 吗?",
|
||||
"message": "您能可正常访问您的电子邮箱 $EMAIL$ 吗?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -4831,7 +4831,7 @@
|
||||
"message": "不,我不能"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "是的,我可以可靠地访问我的电子邮箱"
|
||||
"message": "是的,我可以正常访问我的电子邮箱"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "开启两步登录"
|
||||
|
||||
@@ -1,182 +1,78 @@
|
||||
<ng-container *ngIf="extensionRefreshFlag">
|
||||
<popup-page [loading]="loading">
|
||||
<popup-header slot="header" pageTitle="{{ 'accountActions' | i18n }}" showBackButton>
|
||||
<ng-container slot="end">
|
||||
<app-pop-out></app-pop-out>
|
||||
<app-current-account></app-current-account>
|
||||
</ng-container>
|
||||
</popup-header>
|
||||
|
||||
<ng-container *ngIf="availableAccounts$ | async as availableAccounts">
|
||||
<bit-section [disableMargin]="!enableAccountSwitching">
|
||||
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
|
||||
<div *ngIf="availableAccount.isActive" [ngClass]="{ 'tw-mb-6': enableAccountSwitching }">
|
||||
<auth-account
|
||||
[account]="availableAccount"
|
||||
[extensionRefreshFlag]="extensionRefreshFlag"
|
||||
(loading)="loading = $event"
|
||||
></auth-account>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="enableAccountSwitching">
|
||||
<bit-section-header *ngIf="isFirst">
|
||||
<h2 bitTypography="h6" class="tw-font-semibold">{{ "availableAccounts" | i18n }}</h2>
|
||||
</bit-section-header>
|
||||
|
||||
<div *ngIf="!availableAccount.isActive">
|
||||
<auth-account
|
||||
[account]="availableAccount"
|
||||
[extensionRefreshFlag]="extensionRefreshFlag"
|
||||
(loading)="loading = $event"
|
||||
></auth-account>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!--
|
||||
If the user has not reached the account limit, the last 'availableAccount' will have an 'id' of
|
||||
'SPECIAL_ADD_ACCOUNT_ID'. Since we don't want to count this as one of the actual accounts,
|
||||
we check to make sure the 'id' of the last 'availableAccount' is not equal to 'SPECIAL_ADD_ACCOUNT_ID'
|
||||
-->
|
||||
<p
|
||||
class="tw-text-sm tw-text-muted"
|
||||
*ngIf="
|
||||
availableAccounts.length >= accountLimit &&
|
||||
availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId
|
||||
"
|
||||
>
|
||||
{{ "accountLimitReached" | i18n }}
|
||||
</p>
|
||||
</bit-section>
|
||||
<popup-page [loading]="loading">
|
||||
<popup-header slot="header" pageTitle="{{ 'accountActions' | i18n }}" showBackButton>
|
||||
<ng-container slot="end">
|
||||
<app-pop-out></app-pop-out>
|
||||
<app-current-account></app-current-account>
|
||||
</ng-container>
|
||||
</popup-header>
|
||||
|
||||
<div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount">
|
||||
<bit-section>
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6" class="tw-font-semibold">
|
||||
{{ "options" | i18n }}
|
||||
</h2>
|
||||
</bit-section-header>
|
||||
<ng-container *ngIf="availableAccounts$ | async as availableAccounts">
|
||||
<bit-section [disableMargin]="!enableAccountSwitching">
|
||||
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
|
||||
<div *ngIf="availableAccount.isActive" [ngClass]="{ 'tw-mb-6': enableAccountSwitching }">
|
||||
<auth-account [account]="availableAccount" (loading)="loading = $event"></auth-account>
|
||||
</div>
|
||||
|
||||
<bit-item>
|
||||
<button
|
||||
type="button"
|
||||
bit-item-content
|
||||
(click)="lock(currentAccount.id)"
|
||||
[disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
|
||||
[title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''"
|
||||
>
|
||||
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ "lockNow" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<button type="button" bit-item-content (click)="logOut(currentAccount.id)">
|
||||
<i
|
||||
slot="start"
|
||||
class="bwi bwi-sign-out tw-text-2xl tw-text-main"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item *ngIf="showLockAll$ | async">
|
||||
<button type="button" bit-item-content (click)="lockAll()">
|
||||
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ "lockAll" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
</bit-section>
|
||||
</div>
|
||||
</popup-page>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="enableAccountSwitching">
|
||||
<bit-section-header *ngIf="isFirst">
|
||||
<h2 bitTypography="h6" class="tw-font-semibold">{{ "availableAccounts" | i18n }}</h2>
|
||||
</bit-section-header>
|
||||
|
||||
<ng-container *ngIf="!extensionRefreshFlag">
|
||||
<app-header>
|
||||
<div class="left">
|
||||
<button type="button" (click)="back()">{{ "close" | i18n }}</button>
|
||||
</div>
|
||||
<div class="center tw-font-bold">{{ "switchAccounts" | i18n }}</div>
|
||||
</app-header>
|
||||
<div *ngIf="!availableAccount.isActive">
|
||||
<auth-account [account]="availableAccount" (loading)="loading = $event"></auth-account>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<main
|
||||
*ngIf="loading"
|
||||
class="tw-absolute tw-z-50 tw-box-border tw-flex tw-cursor-not-allowed tw-items-center tw-justify-center tw-bg-background tw-opacity-60"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-2x bwi-spin" aria-hidden="true"></i>
|
||||
</main>
|
||||
<main>
|
||||
<div class="tw-p-2">
|
||||
<div *ngIf="availableAccounts$ | async as availableAccounts">
|
||||
<ul class="tw-grid tw-list-none tw-gap-2" role="listbox">
|
||||
<ng-container *ngFor="let availableAccount of availableAccounts; first as isFirst">
|
||||
<li *ngIf="availableAccount.isActive" class="tw-mb-4" role="option">
|
||||
<auth-account
|
||||
[account]="availableAccount"
|
||||
(loading)="loading = $event"
|
||||
></auth-account>
|
||||
</li>
|
||||
<ng-container *ngIf="enableAccountSwitching">
|
||||
<div *ngIf="isFirst" class="tw-uppercase tw-text-muted">
|
||||
{{ "availableAccounts" | i18n }}
|
||||
</div>
|
||||
<li *ngIf="!availableAccount.isActive" role="option">
|
||||
<auth-account
|
||||
[account]="availableAccount"
|
||||
(loading)="loading = $event"
|
||||
></auth-account>
|
||||
</li>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ul>
|
||||
<!--
|
||||
<!--
|
||||
If the user has not reached the account limit, the last 'availableAccount' will have an 'id' of
|
||||
'SPECIAL_ADD_ACCOUNT_ID'. Since we don't want to count this as one of the actual accounts,
|
||||
we check to make sure the 'id' of the last 'availableAccount' is not equal to 'SPECIAL_ADD_ACCOUNT_ID'
|
||||
-->
|
||||
<p
|
||||
class="tw-text-sm tw-text-muted"
|
||||
*ngIf="
|
||||
availableAccounts.length >= accountLimit &&
|
||||
availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId
|
||||
"
|
||||
>
|
||||
{{ "accountLimitReached" | i18n }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="tw-text-sm tw-text-muted"
|
||||
*ngIf="
|
||||
availableAccounts.length >= accountLimit &&
|
||||
availableAccounts[availableAccounts.length - 1].id !== specialAddAccountId
|
||||
"
|
||||
>
|
||||
{{ "accountLimitReached" | i18n }}
|
||||
</p>
|
||||
</bit-section>
|
||||
</ng-container>
|
||||
|
||||
<div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount">
|
||||
<div class="tw-mb-2 tw-uppercase tw-text-muted">{{ "options" | i18n }}</div>
|
||||
<div class="tw-grid tw-gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3 disabled:tw-cursor-not-allowed disabled:tw-border-text-muted/60 disabled:!tw-text-muted/60"
|
||||
(click)="lock(currentAccount.id)"
|
||||
[disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
|
||||
[title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''"
|
||||
>
|
||||
<i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i>
|
||||
{{ "lockNow" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3"
|
||||
(click)="logOut(currentAccount.id)"
|
||||
>
|
||||
<i class="bwi bwi-sign-out tw-text-2xl" aria-hidden="true"></i>
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-p-3"
|
||||
(click)="lockAll()"
|
||||
*ngIf="showLockAll$ | async"
|
||||
>
|
||||
<i class="bwi bwi-lock tw-text-2xl" aria-hidden="true"></i>
|
||||
{{ "lockAll" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</ng-container>
|
||||
<div class="tw-mt-8" *ngIf="currentAccount$ | async as currentAccount">
|
||||
<bit-section>
|
||||
<bit-section-header>
|
||||
<h2 bitTypography="h6" class="tw-font-semibold">
|
||||
{{ "options" | i18n }}
|
||||
</h2>
|
||||
</bit-section-header>
|
||||
|
||||
<bit-item>
|
||||
<button
|
||||
type="button"
|
||||
bit-item-content
|
||||
(click)="lock(currentAccount.id)"
|
||||
[disabled]="currentAccount.status === lockedStatus || !activeUserCanLock"
|
||||
[title]="!activeUserCanLock ? ('unlockMethodNeeded' | i18n) : ''"
|
||||
>
|
||||
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ "lockNow" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<button type="button" bit-item-content (click)="logOut(currentAccount.id)">
|
||||
<i slot="start" class="bwi bwi-sign-out tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ "logOut" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item *ngIf="showLockAll$ | async">
|
||||
<button type="button" bit-item-content (click)="lockAll()">
|
||||
<i slot="start" class="bwi bwi-lock tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ "lockAll" | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
</bit-section>
|
||||
</div>
|
||||
</popup-page>
|
||||
|
||||
@@ -10,9 +10,7 @@ import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeou
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
AvatarModule,
|
||||
@@ -25,7 +23,6 @@ import {
|
||||
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
import { HeaderComponent } from "../../../platform/popup/header.component";
|
||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||
|
||||
@@ -44,7 +41,6 @@ import { AccountSwitcherService } from "./services/account-switcher.service";
|
||||
AvatarModule,
|
||||
PopupPageComponent,
|
||||
PopupHeaderComponent,
|
||||
HeaderComponent,
|
||||
PopOutComponent,
|
||||
CurrentAccountComponent,
|
||||
AccountComponent,
|
||||
@@ -58,7 +54,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
|
||||
loading = false;
|
||||
activeUserCanLock = false;
|
||||
extensionRefreshFlag = false;
|
||||
enableAccountSwitching = true;
|
||||
|
||||
constructor(
|
||||
@@ -70,7 +65,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private authService: AuthService,
|
||||
private configService: ConfigService,
|
||||
private lockService: LockService,
|
||||
) {}
|
||||
|
||||
@@ -109,9 +103,6 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
|
||||
async ngOnInit() {
|
||||
this.enableAccountSwitching = enableAccountSwitching();
|
||||
this.extensionRefreshFlag = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ExtensionRefresh,
|
||||
);
|
||||
|
||||
const availableVaultTimeoutActions = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||
|
||||
@@ -1,109 +1,44 @@
|
||||
<ng-container *ngIf="extensionRefreshFlag">
|
||||
<bit-item *ngIf="account.id !== specialAccountAddId">
|
||||
<button bit-item-content type="button" (click)="selectAccount(account.id)">
|
||||
<bit-avatar
|
||||
slot="start"
|
||||
[id]="account.id"
|
||||
[text]="account.name"
|
||||
[color]="account.avatarColor"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
></bit-avatar>
|
||||
<bit-item *ngIf="account.id !== specialAccountAddId">
|
||||
<button bit-item-content type="button" (click)="selectAccount(account.id)">
|
||||
<bit-avatar
|
||||
slot="start"
|
||||
[id]="account.id"
|
||||
[text]="account.name"
|
||||
[color]="account.avatarColor"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
></bit-avatar>
|
||||
|
||||
<span class="tw-sr-only" *ngIf="status.text === 'active'">
|
||||
{{ "activeAccount" | i18n }}:
|
||||
</span>
|
||||
<span class="tw-sr-only" *ngIf="status.text !== 'active'">
|
||||
{{ "switchToAccount" | i18n }}
|
||||
</span>
|
||||
<div class="tw-max-w-64 tw-truncate">
|
||||
{{ account.email }}
|
||||
</div>
|
||||
|
||||
<ng-container slot="secondary">
|
||||
<div class="tw-max-w-64 tw-truncate tw-text-sm">
|
||||
<span class="tw-sr-only">{{ "hostedAt" | i18n }}</span>
|
||||
{{ account.server }}
|
||||
</div>
|
||||
|
||||
<div class="tw-text-sm tw-italic" [attr.aria-hidden]="status.text === 'active'">
|
||||
<span class="tw-sr-only">(</span>
|
||||
<span [ngClass]="status.text === 'active' ? 'tw-font-bold tw-text-success' : ''">{{
|
||||
status.text
|
||||
}}</span>
|
||||
<span class="tw-sr-only">)</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<i slot="end" class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-item>
|
||||
|
||||
<bit-item *ngIf="account.id === specialAccountAddId">
|
||||
<button type="button" bit-item-content (click)="selectAccount(account.id)">
|
||||
<i slot="start" class="bwi bwi-plus tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ account.name | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!extensionRefreshFlag">
|
||||
<button
|
||||
*ngIf="account.id !== specialAccountAddId"
|
||||
type="button"
|
||||
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3"
|
||||
(click)="selectAccount(account.id)"
|
||||
>
|
||||
<div class="tw-flex-shrink-0">
|
||||
<bit-avatar
|
||||
[id]="account.id"
|
||||
[text]="account.name"
|
||||
[color]="account.avatarColor"
|
||||
size="small"
|
||||
aria-hidden="true"
|
||||
></bit-avatar>
|
||||
<span class="tw-sr-only" *ngIf="status.text === 'active'"> {{ "activeAccount" | i18n }}: </span>
|
||||
<span class="tw-sr-only" *ngIf="status.text !== 'active'">
|
||||
{{ "switchToAccount" | i18n }}
|
||||
</span>
|
||||
<div class="tw-max-w-64 tw-truncate">
|
||||
{{ account.email }}
|
||||
</div>
|
||||
<div class="tw-text-left">
|
||||
<span class="tw-sr-only" *ngIf="status.text === 'active'">
|
||||
{{ "activeAccount" | i18n }}:
|
||||
</span>
|
||||
<span class="tw-sr-only" *ngIf="status.text !== 'active'">
|
||||
{{ "switchToAccount" | i18n }}
|
||||
</span>
|
||||
<div class="tw-max-w-64 tw-truncate">
|
||||
{{ account.email }}
|
||||
</div>
|
||||
|
||||
<div class="account-switcher-row-details tw-max-w-64 tw-truncate tw-text-sm">
|
||||
<ng-container slot="secondary">
|
||||
<div class="tw-max-w-64 tw-truncate tw-text-sm">
|
||||
<span class="tw-sr-only">{{ "hostedAt" | i18n }}</span>
|
||||
{{ account.server }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="account-switcher-row-details tw-text-sm tw-italic"
|
||||
[attr.aria-hidden]="status.text === 'active'"
|
||||
>
|
||||
<div class="tw-text-sm tw-italic" [attr.aria-hidden]="status.text === 'active'">
|
||||
<span class="tw-sr-only">(</span>
|
||||
<span [ngClass]="status.text === 'active' ? 'tw-font-bold tw-text-success' : ''">{{
|
||||
status.text
|
||||
}}</span>
|
||||
<span class="tw-sr-only">)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-ml-auto tw-flex-shrink-0">
|
||||
<i class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
|
||||
</div>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<button
|
||||
*ngIf="account.id === specialAccountAddId"
|
||||
type="button"
|
||||
class="account-switcher-row tw-flex tw-w-full tw-items-center tw-gap-3 tw-rounded-md tw-border-none tw-p-3"
|
||||
(click)="selectAccount(account.id)"
|
||||
>
|
||||
<i class="bwi bwi-plus tw-text-2xl" aria-hidden="true"></i>
|
||||
<div>
|
||||
{{ account.name | i18n }}
|
||||
</div>
|
||||
<i slot="end" class="bwi tw-text-2xl" [ngClass]="status.icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
</bit-item>
|
||||
|
||||
<bit-item *ngIf="account.id === specialAccountAddId">
|
||||
<button type="button" bit-item-content (click)="selectAccount(account.id)">
|
||||
<i slot="start" class="bwi bwi-plus tw-text-2xl tw-text-main" aria-hidden="true"></i>
|
||||
{{ account.name | i18n }}
|
||||
</button>
|
||||
</bit-item>
|
||||
|
||||
@@ -19,7 +19,6 @@ import { AccountSwitcherService, AvailableAccount } from "./services/account-swi
|
||||
})
|
||||
export class AccountComponent {
|
||||
@Input() account: AvailableAccount;
|
||||
@Input() extensionRefreshFlag: boolean = false;
|
||||
@Output() loading = new EventEmitter<boolean>();
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[showReadonlyHostname]="showReadonlyHostname"
|
||||
[hideLogo]="true"
|
||||
[maxWidth]="maxWidth"
|
||||
[hideFooter]="hideFooter"
|
||||
>
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet slot="secondary" name="secondary"></router-outlet>
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData {
|
||||
showAcctSwitcher?: boolean;
|
||||
showBackButton?: boolean;
|
||||
showLogo?: boolean;
|
||||
hideFooter?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -54,6 +55,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
protected showReadonlyHostname: boolean;
|
||||
protected maxWidth: "md" | "3xl";
|
||||
protected hasLoggedInAccount: boolean = false;
|
||||
protected hideFooter: boolean;
|
||||
|
||||
protected theme: string;
|
||||
protected logo = ExtensionBitwardenLogo;
|
||||
@@ -112,6 +114,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
this.pageIcon = firstChildRouteData["pageIcon"];
|
||||
}
|
||||
|
||||
this.hideFooter = Boolean(firstChildRouteData["hideFooter"]);
|
||||
this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]);
|
||||
this.maxWidth = firstChildRouteData["maxWidth"];
|
||||
|
||||
@@ -158,6 +161,10 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
this.pageIcon = data.pageIcon !== null ? data.pageIcon : null;
|
||||
}
|
||||
|
||||
if (data.hideFooter !== undefined) {
|
||||
this.hideFooter = data.hideFooter !== null ? data.hideFooter : null;
|
||||
}
|
||||
|
||||
if (data.showReadonlyHostname !== undefined) {
|
||||
this.showReadonlyHostname = data.showReadonlyHostname;
|
||||
}
|
||||
@@ -194,6 +201,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
||||
this.showBackButton = null;
|
||||
this.showLogo = null;
|
||||
this.maxWidth = null;
|
||||
this.hideFooter = null;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
<app-header>
|
||||
<div class="left">
|
||||
<button type="button" routerLink="/tabs/settings">
|
||||
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
|
||||
<span>{{ "back" | i18n }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "accountSecurity" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<app-pop-out></app-pop-out>
|
||||
</div>
|
||||
</app-header>
|
||||
<main tabindex="-1" [formGroup]="form">
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "unlockMethods" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="biometric">{{ "unlockWithBiometrics" | i18n }}</label>
|
||||
<input id="biometric" type="checkbox" formControlName="biometric" />
|
||||
</div>
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
appBoxRow
|
||||
*ngIf="supportsBiometric && this.form.value.biometric"
|
||||
>
|
||||
<label for="autoBiometricsPrompt">{{ "enableAutoBiometricsPrompt" | i18n }}</label>
|
||||
<input
|
||||
id="autoBiometricsPrompt"
|
||||
type="checkbox"
|
||||
(change)="updateAutoBiometricsPrompt()"
|
||||
formControlName="enableAutoBiometricsPrompt"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="pin">{{ "unlockWithPin" | i18n }}</label>
|
||||
<input id="pin" type="checkbox" formControlName="pin" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "sessionTimeoutHeader" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<app-callout type="info" *ngIf="vaultTimeoutPolicyCallout | async as policy">
|
||||
<span *ngIf="policy.timeout && policy.action">
|
||||
{{
|
||||
"vaultTimeoutPolicyWithActionInEffect"
|
||||
| i18n: policy.timeout.hours : policy.timeout.minutes : (policy.action | i18n)
|
||||
}}
|
||||
</span>
|
||||
<span *ngIf="policy.timeout && !policy.action">
|
||||
{{ "vaultTimeoutPolicyInEffect" | i18n: policy.timeout.hours : policy.timeout.minutes }}
|
||||
</span>
|
||||
<span *ngIf="!policy.timeout && policy.action">
|
||||
{{ "vaultTimeoutActionPolicyInEffect" | i18n: (policy.action | i18n) }}
|
||||
</span>
|
||||
</app-callout>
|
||||
<app-vault-timeout-input
|
||||
[vaultTimeoutOptions]="vaultTimeoutOptions"
|
||||
[formControl]="form.controls.vaultTimeout"
|
||||
ngDefaultControl
|
||||
>
|
||||
</app-vault-timeout-input>
|
||||
<div class="box-content-row display-block" appBoxRow>
|
||||
<label for="vaultTimeoutAction">{{ "vaultTimeoutAction" | i18n }}</label>
|
||||
<select
|
||||
id="vaultTimeoutAction"
|
||||
name="VaultTimeoutActions"
|
||||
formControlName="vaultTimeoutAction"
|
||||
>
|
||||
<option *ngFor="let action of availableVaultTimeoutActions" [ngValue]="action">
|
||||
{{ action | i18n }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="!availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)"
|
||||
id="unlockMethodHelp"
|
||||
class="box-footer"
|
||||
>
|
||||
{{ "unlockMethodNeededToChangeTimeoutActionDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box list">
|
||||
<h2 class="box-header">{{ "otherOptions" | i18n }}</h2>
|
||||
<div class="box-content single-line">
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
(click)="fingerprint()"
|
||||
>
|
||||
<div class="row-main">{{ "fingerprintPhrase" | i18n }}</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
(click)="twoStep()"
|
||||
>
|
||||
<div class="row-main">{{ "twoStepLogin" | i18n }}</div>
|
||||
<i class="bwi bwi-external-link bwi-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
(click)="changePassword()"
|
||||
*ngIf="showChangeMasterPass"
|
||||
>
|
||||
<div class="row-main">{{ "changeMasterPassword" | i18n }}</div>
|
||||
<i class="bwi bwi-external-link bwi-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="
|
||||
!accountSwitcherEnabled && availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)
|
||||
"
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
(click)="lock()"
|
||||
>
|
||||
<div class="row-main">{{ "lockNow" | i18n }}</div>
|
||||
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!accountSwitcherEnabled"
|
||||
type="button"
|
||||
class="box-content-row box-content-row-flex text-default"
|
||||
appStopClick
|
||||
(click)="logOut()"
|
||||
>
|
||||
<div class="row-main">{{ "logOut" | i18n }}</div>
|
||||
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -1,499 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
concatMap,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
pairwise,
|
||||
startWith,
|
||||
Subject,
|
||||
switchMap,
|
||||
takeUntil,
|
||||
} from "rxjs";
|
||||
|
||||
import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import {
|
||||
VaultTimeout,
|
||||
VaultTimeoutOption,
|
||||
VaultTimeoutStringType,
|
||||
} from "@bitwarden/common/types/vault-timeout.type";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { KeyService, BiometricStateService, BiometricsService } from "@bitwarden/key-management";
|
||||
|
||||
import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors";
|
||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils";
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
|
||||
import { AwaitDesktopDialogComponent } from "./await-desktop-dialog.component";
|
||||
|
||||
@Component({
|
||||
selector: "auth-account-security",
|
||||
templateUrl: "account-security-v1.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class AccountSecurityComponent implements OnInit, OnDestroy {
|
||||
protected readonly VaultTimeoutAction = VaultTimeoutAction;
|
||||
|
||||
availableVaultTimeoutActions: VaultTimeoutAction[] = [];
|
||||
vaultTimeoutOptions: VaultTimeoutOption[];
|
||||
vaultTimeoutPolicyCallout: Observable<{
|
||||
timeout: { hours: string; minutes: string };
|
||||
action: VaultTimeoutAction;
|
||||
}>;
|
||||
supportsBiometric: boolean;
|
||||
showChangeMasterPass = true;
|
||||
accountSwitcherEnabled = false;
|
||||
|
||||
form = this.formBuilder.group({
|
||||
vaultTimeout: [null as VaultTimeout | null],
|
||||
vaultTimeoutAction: [VaultTimeoutAction.Lock],
|
||||
pin: [null as boolean | null],
|
||||
biometric: false,
|
||||
enableAutoBiometricsPrompt: true,
|
||||
});
|
||||
|
||||
private refreshTimeoutSettings$ = new BehaviorSubject<void>(undefined);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private pinService: PinServiceAbstraction,
|
||||
private policyService: PolicyService,
|
||||
private formBuilder: FormBuilder,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
public messagingService: MessagingService,
|
||||
private environmentService: EnvironmentService,
|
||||
private keyService: KeyService,
|
||||
private stateService: StateService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private dialogService: DialogService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private biometricsService: BiometricsService,
|
||||
) {
|
||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
const maximumVaultTimeoutPolicy = this.policyService.get$(PolicyType.MaximumVaultTimeout);
|
||||
this.vaultTimeoutPolicyCallout = maximumVaultTimeoutPolicy.pipe(
|
||||
filter((policy) => policy != null),
|
||||
map((policy) => {
|
||||
let timeout;
|
||||
if (policy.data?.minutes) {
|
||||
timeout = {
|
||||
hours: Math.floor(policy.data?.minutes / 60).toString(),
|
||||
minutes: (policy.data?.minutes % 60).toString(),
|
||||
};
|
||||
}
|
||||
return { timeout: timeout, action: policy.data?.action };
|
||||
}),
|
||||
);
|
||||
|
||||
const showOnLocked =
|
||||
!this.platformUtilsService.isFirefox() && !this.platformUtilsService.isSafari();
|
||||
|
||||
this.vaultTimeoutOptions = [
|
||||
{ name: this.i18nService.t("immediately"), value: 0 },
|
||||
{ name: this.i18nService.t("oneMinute"), value: 1 },
|
||||
{ name: this.i18nService.t("fiveMinutes"), value: 5 },
|
||||
{ name: this.i18nService.t("fifteenMinutes"), value: 15 },
|
||||
{ name: this.i18nService.t("thirtyMinutes"), value: 30 },
|
||||
{ name: this.i18nService.t("oneHour"), value: 60 },
|
||||
{ name: this.i18nService.t("fourHours"), value: 240 },
|
||||
];
|
||||
|
||||
if (showOnLocked) {
|
||||
this.vaultTimeoutOptions.push({
|
||||
name: this.i18nService.t("onLocked"),
|
||||
value: VaultTimeoutStringType.OnLocked,
|
||||
});
|
||||
}
|
||||
|
||||
this.vaultTimeoutOptions.push({
|
||||
name: this.i18nService.t("onRestart"),
|
||||
value: VaultTimeoutStringType.OnRestart,
|
||||
});
|
||||
this.vaultTimeoutOptions.push({
|
||||
name: this.i18nService.t("never"),
|
||||
value: VaultTimeoutStringType.Never,
|
||||
});
|
||||
|
||||
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||
|
||||
let timeout = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutByUserId$(activeAccount.id),
|
||||
);
|
||||
if (timeout === VaultTimeoutStringType.OnLocked && !showOnLocked) {
|
||||
timeout = VaultTimeoutStringType.OnRestart;
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
vaultTimeout: timeout,
|
||||
vaultTimeoutAction: await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
|
||||
),
|
||||
pin: await this.pinService.isPinSet(activeAccount.id),
|
||||
biometric: await this.vaultTimeoutSettingsService.isBiometricLockSet(),
|
||||
enableAutoBiometricsPrompt: await firstValueFrom(
|
||||
this.biometricStateService.promptAutomatically$,
|
||||
),
|
||||
};
|
||||
this.form.patchValue(initialValues, { emitEvent: false });
|
||||
|
||||
this.supportsBiometric = await this.biometricsService.supportsBiometric();
|
||||
this.showChangeMasterPass = await this.userVerificationService.hasMasterPassword();
|
||||
|
||||
this.form.controls.vaultTimeout.valueChanges
|
||||
.pipe(
|
||||
startWith(initialValues.vaultTimeout), // emit to init pairwise
|
||||
pairwise(),
|
||||
concatMap(async ([previousValue, newValue]) => {
|
||||
await this.saveVaultTimeout(previousValue, newValue);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.form.controls.vaultTimeoutAction.valueChanges
|
||||
.pipe(
|
||||
startWith(initialValues.vaultTimeoutAction), // emit to init pairwise
|
||||
pairwise(),
|
||||
concatMap(async ([previousValue, newValue]) => {
|
||||
await this.saveVaultTimeoutAction(previousValue, newValue);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.form.controls.pin.valueChanges
|
||||
.pipe(
|
||||
concatMap(async (value) => {
|
||||
await this.updatePin(value);
|
||||
this.refreshTimeoutSettings$.next();
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.form.controls.biometric.valueChanges
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
concatMap(async (enabled) => {
|
||||
await this.updateBiometric(enabled);
|
||||
if (enabled) {
|
||||
this.form.controls.enableAutoBiometricsPrompt.enable();
|
||||
} else {
|
||||
this.form.controls.enableAutoBiometricsPrompt.disable();
|
||||
}
|
||||
this.refreshTimeoutSettings$.next();
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.refreshTimeoutSettings$
|
||||
.pipe(
|
||||
switchMap(() =>
|
||||
combineLatest([
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
|
||||
]),
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe(([availableActions, action]) => {
|
||||
this.availableVaultTimeoutActions = availableActions;
|
||||
this.form.controls.vaultTimeoutAction.setValue(action, { emitEvent: false });
|
||||
// NOTE: The UI doesn't properly update without detect changes.
|
||||
// I've even tried using an async pipe, but it still doesn't work. I'm not sure why.
|
||||
// Using an async pipe means that we can't call `detectChanges` AFTER the data has change
|
||||
// meaning that we are forced to use regular class variables instead of observables.
|
||||
this.changeDetectorRef.detectChanges();
|
||||
});
|
||||
|
||||
this.refreshTimeoutSettings$
|
||||
.pipe(
|
||||
switchMap(() =>
|
||||
combineLatest([
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||
maximumVaultTimeoutPolicy,
|
||||
]),
|
||||
),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
.subscribe(([availableActions, policy]) => {
|
||||
if (policy?.data?.action || availableActions.length <= 1) {
|
||||
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
|
||||
} else {
|
||||
this.form.controls.vaultTimeoutAction.enable({ emitEvent: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async saveVaultTimeout(previousValue: VaultTimeout, newValue: VaultTimeout) {
|
||||
if (newValue === VaultTimeoutStringType.Never) {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "warning" },
|
||||
content: { key: "neverLockWarning" },
|
||||
type: "warning",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
this.form.controls.vaultTimeout.setValue(previousValue, { emitEvent: false });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The minTimeoutError does not apply to browser because it supports Immediately
|
||||
// So only check for the policyError
|
||||
if (this.form.controls.vaultTimeout.hasError("policyError")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("vaultTimeoutTooLarge"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||
|
||||
const vaultTimeoutAction = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
|
||||
);
|
||||
|
||||
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
||||
activeAccount.id,
|
||||
newValue,
|
||||
vaultTimeoutAction,
|
||||
);
|
||||
if (newValue === VaultTimeoutStringType.Never) {
|
||||
this.messagingService.send("bgReseedStorage");
|
||||
}
|
||||
}
|
||||
|
||||
async saveVaultTimeoutAction(previousValue: VaultTimeoutAction, newValue: VaultTimeoutAction) {
|
||||
if (newValue === VaultTimeoutAction.LogOut) {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "vaultTimeoutLogOutConfirmationTitle" },
|
||||
content: { key: "vaultTimeoutLogOutConfirmation" },
|
||||
type: "warning",
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
this.form.controls.vaultTimeoutAction.setValue(previousValue, {
|
||||
emitEvent: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.form.controls.vaultTimeout.hasError("policyError")) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("vaultTimeoutTooLarge"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||
|
||||
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
||||
activeAccount.id,
|
||||
this.form.value.vaultTimeout,
|
||||
newValue,
|
||||
);
|
||||
this.refreshTimeoutSettings$.next();
|
||||
}
|
||||
|
||||
async updatePin(value: boolean) {
|
||||
if (value) {
|
||||
const dialogRef = SetPinComponent.open(this.dialogService);
|
||||
|
||||
if (dialogRef == null) {
|
||||
this.form.controls.pin.setValue(false, { emitEvent: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const userHasPinSet = await firstValueFrom(dialogRef.closed);
|
||||
this.form.controls.pin.setValue(userHasPinSet, { emitEvent: false });
|
||||
} else {
|
||||
await this.vaultTimeoutSettingsService.clear();
|
||||
}
|
||||
}
|
||||
|
||||
async updateBiometric(enabled: boolean) {
|
||||
if (enabled && this.supportsBiometric) {
|
||||
let granted;
|
||||
try {
|
||||
granted = await BrowserApi.requestPermission({ permissions: ["nativeMessaging"] });
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line
|
||||
console.error(e);
|
||||
|
||||
if (this.platformUtilsService.isFirefox() && BrowserPopupUtils.inSidebar(window)) {
|
||||
await this.dialogService.openSimpleDialog({
|
||||
title: { key: "nativeMessaginPermissionSidebarTitle" },
|
||||
content: { key: "nativeMessaginPermissionSidebarDesc" },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
type: "info",
|
||||
});
|
||||
|
||||
this.form.controls.biometric.setValue(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!granted) {
|
||||
await this.dialogService.openSimpleDialog({
|
||||
title: { key: "nativeMessaginPermissionErrorTitle" },
|
||||
content: { key: "nativeMessaginPermissionErrorDesc" },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
type: "danger",
|
||||
});
|
||||
|
||||
this.form.controls.biometric.setValue(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const awaitDesktopDialogRef = AwaitDesktopDialogComponent.open(this.dialogService);
|
||||
const awaitDesktopDialogClosed = firstValueFrom(awaitDesktopDialogRef.closed);
|
||||
|
||||
await this.keyService.refreshAdditionalKeys();
|
||||
|
||||
await Promise.race([
|
||||
awaitDesktopDialogClosed.then(async (result) => {
|
||||
if (result !== true) {
|
||||
this.form.controls.biometric.setValue(false);
|
||||
}
|
||||
}),
|
||||
this.biometricsService
|
||||
.authenticateBiometric()
|
||||
.then((result) => {
|
||||
this.form.controls.biometric.setValue(result);
|
||||
if (!result) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
this.i18nService.t("errorEnableBiometricTitle"),
|
||||
this.i18nService.t("errorEnableBiometricDesc"),
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// Handle connection errors
|
||||
this.form.controls.biometric.setValue(false);
|
||||
|
||||
const error = BiometricErrors[e.message as BiometricErrorTypes];
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.dialogService.openSimpleDialog({
|
||||
title: { key: error.title },
|
||||
content: { key: error.description },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
type: "danger",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
awaitDesktopDialogRef.close(true);
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
await this.biometricStateService.setBiometricUnlockEnabled(false);
|
||||
await this.biometricStateService.setFingerprintValidated(false);
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutoBiometricsPrompt() {
|
||||
await this.biometricStateService.setPromptAutomatically(
|
||||
this.form.value.enableAutoBiometricsPrompt,
|
||||
);
|
||||
}
|
||||
|
||||
async changePassword() {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "continueToWebApp" },
|
||||
content: { key: "changeMasterPasswordOnWebConfirmation" },
|
||||
type: "info",
|
||||
acceptButtonText: { key: "continue" },
|
||||
});
|
||||
if (confirmed) {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
await BrowserApi.createNewTab(env.getWebVaultUrl());
|
||||
}
|
||||
}
|
||||
|
||||
async twoStep() {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "twoStepLogin" },
|
||||
content: { key: "twoStepLoginConfirmation" },
|
||||
type: "info",
|
||||
});
|
||||
if (confirmed) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
BrowserApi.createNewTab("https://bitwarden.com/help/setup-two-step-login/");
|
||||
}
|
||||
}
|
||||
|
||||
async fingerprint() {
|
||||
const fingerprint = await this.keyService.getFingerprint(await this.stateService.getUserId());
|
||||
|
||||
const dialogRef = FingerprintDialogComponent.open(this.dialogService, {
|
||||
fingerprint,
|
||||
});
|
||||
|
||||
return firstValueFrom(dialogRef.closed);
|
||||
}
|
||||
|
||||
async lock() {
|
||||
await this.vaultTimeoutService.lock();
|
||||
}
|
||||
|
||||
async logOut() {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "logOut" },
|
||||
content: { key: "logOutConfirmation" },
|
||||
type: "info",
|
||||
});
|
||||
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
if (confirmed) {
|
||||
this.messagingService.send("logout", { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,6 @@ import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||
import { enableAccountSwitching } from "../../../platform/flags";
|
||||
import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils";
|
||||
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
|
||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||
import { SetPinComponent } from "../components/set-pin.component";
|
||||
@@ -82,7 +81,6 @@ import { AwaitDesktopDialogComponent } from "./await-desktop-dialog.component";
|
||||
JslibModule,
|
||||
LinkModule,
|
||||
PopOutComponent,
|
||||
PopupFooterComponent,
|
||||
PopupHeaderComponent,
|
||||
PopupPageComponent,
|
||||
RouterModule,
|
||||
|
||||
@@ -60,10 +60,18 @@ describe("NotificationBackground", () => {
|
||||
const configService = mock<ConfigService>();
|
||||
const accountService = mock<AccountService>();
|
||||
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||
id: "testId" as UserId,
|
||||
email: "test@example.com",
|
||||
emailVerified: true,
|
||||
name: "Test User",
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Locked);
|
||||
authService = mock<AuthService>();
|
||||
authService.activeAccountStatus$ = activeAccountStatusMock$;
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
notificationBackground = new NotificationBackground(
|
||||
autofillService,
|
||||
cipherService,
|
||||
@@ -683,13 +691,6 @@ describe("NotificationBackground", () => {
|
||||
});
|
||||
|
||||
describe("saveOrUpdateCredentials", () => {
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||
id: "testId" as UserId,
|
||||
email: "test@example.com",
|
||||
emailVerified: true,
|
||||
name: "Test User",
|
||||
});
|
||||
|
||||
let getDecryptedCipherByIdSpy: jest.SpyInstance;
|
||||
let getAllDecryptedForUrlSpy: jest.SpyInstance;
|
||||
let updatePasswordSpy: jest.SpyInstance;
|
||||
|
||||
@@ -83,6 +83,8 @@ export default class NotificationBackground {
|
||||
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
||||
};
|
||||
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private autofillService: AutofillService,
|
||||
private cipherService: CipherService,
|
||||
@@ -569,9 +571,7 @@ export default class NotificationBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
|
||||
const cipher = await this.cipherService.encrypt(newCipher, activeUserId);
|
||||
try {
|
||||
@@ -611,10 +611,7 @@ export default class NotificationBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const cipher = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
try {
|
||||
// We've only updated the password, no need to broadcast editedCipher message
|
||||
@@ -647,17 +644,15 @@ export default class NotificationBackground {
|
||||
if (Utils.isNullOrWhitespace(folderId) || folderId === "null") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const folders = await firstValueFrom(this.folderService.folderViews$);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const folders = await firstValueFrom(this.folderService.folderViews$(activeUserId));
|
||||
return folders.some((x) => x.id === folderId);
|
||||
}
|
||||
|
||||
private async getDecryptedCipherById(cipherId: string) {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
if (cipher != null && cipher.type === CipherType.Login) {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
|
||||
return await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
@@ -697,7 +692,8 @@ export default class NotificationBackground {
|
||||
* Returns the first value found from the folder service's folderViews$ observable.
|
||||
*/
|
||||
private async getFolderData() {
|
||||
return await firstValueFrom(this.folderService.folderViews$);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
return await firstValueFrom(this.folderService.folderViews$(activeUserId));
|
||||
}
|
||||
|
||||
private async getWebVaultUrl(): Promise<string> {
|
||||
|
||||
@@ -2275,6 +2275,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
card,
|
||||
identity,
|
||||
sender,
|
||||
addNewCipherType,
|
||||
}: CurrentAddNewItemData) {
|
||||
const cipherView: CipherView = this.buildNewVaultItemCipherView({
|
||||
login,
|
||||
@@ -2294,7 +2295,10 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
||||
collectionIds: cipherView.collectionIds,
|
||||
});
|
||||
|
||||
await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id });
|
||||
await this.openAddEditVaultItemPopout(sender.tab, {
|
||||
cipherId: cipherView.id,
|
||||
cipherType: addNewCipherType,
|
||||
});
|
||||
await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher");
|
||||
} catch (error) {
|
||||
this.logService.error("Error building cipher and opening add/edit vault item popout", error);
|
||||
|
||||
@@ -458,7 +458,7 @@ export class BrowserApi {
|
||||
// and that prompts us to show a new tab, this apparently doesn't happen on sideloaded
|
||||
// extensions and only shows itself production scenarios. See: https://bitwarden.atlassian.net/browse/PM-12298
|
||||
if (this.isSafariApi) {
|
||||
self.location.reload();
|
||||
return self.location.reload();
|
||||
}
|
||||
return chrome.runtime.reload();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Subject } from "rxjs";
|
||||
|
||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
@@ -30,12 +29,12 @@ describe("ForegroundSyncService", () => {
|
||||
const cipherService = mock<CipherService>();
|
||||
const collectionService = mock<CollectionService>();
|
||||
const apiService = mock<ApiService>();
|
||||
const accountService = mock<AccountService>();
|
||||
const accountService = mockAccountServiceWith(userId);
|
||||
const authService = mock<AuthService>();
|
||||
const sendService = mock<InternalSendService>();
|
||||
const sendApiService = mock<SendApiService>();
|
||||
const messageListener = mock<MessageListener>();
|
||||
const stateProvider = new FakeStateProvider(mockAccountServiceWith(userId));
|
||||
const stateProvider = new FakeStateProvider(accountService);
|
||||
|
||||
const sut = new ForegroundSyncService(
|
||||
stateService,
|
||||
|
||||
@@ -65,7 +65,6 @@ import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-req
|
||||
import { RegisterComponent } from "../auth/popup/register.component";
|
||||
import { RemovePasswordComponent } from "../auth/popup/remove-password.component";
|
||||
import { SetPasswordComponent } from "../auth/popup/set-password.component";
|
||||
import { AccountSecurityComponent as AccountSecurityV1Component } from "../auth/popup/settings/account-security-v1.component";
|
||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||
import { SsoComponentV1 } from "../auth/popup/sso-v1.component";
|
||||
import { TwoFactorAuthComponent } from "../auth/popup/two-factor-auth.component";
|
||||
@@ -351,11 +350,12 @@ const routes: Routes = [
|
||||
canActivate: [authGuard],
|
||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||
}),
|
||||
...extensionRefreshSwap(AccountSecurityV1Component, AccountSecurityComponent, {
|
||||
{
|
||||
path: "account-security",
|
||||
component: AccountSecurityComponent,
|
||||
canActivate: [authGuard],
|
||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||
}),
|
||||
},
|
||||
...extensionRefreshSwap(NotificationsSettingsV1Component, NotificationsSettingsComponent, {
|
||||
path: "notifications",
|
||||
canActivate: [authGuard],
|
||||
@@ -713,6 +713,7 @@ const routes: Routes = [
|
||||
pageTitle: {
|
||||
key: "importantNotice",
|
||||
},
|
||||
hideFooter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -29,7 +29,6 @@ import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-req
|
||||
import { RegisterComponent } from "../auth/popup/register.component";
|
||||
import { RemovePasswordComponent } from "../auth/popup/remove-password.component";
|
||||
import { SetPasswordComponent } from "../auth/popup/set-password.component";
|
||||
import { AccountSecurityComponent as AccountSecurityComponentV1 } from "../auth/popup/settings/account-security-v1.component";
|
||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||
import { VaultTimeoutInputComponent } from "../auth/popup/settings/vault-timeout-input.component";
|
||||
import { SsoComponentV1 } from "../auth/popup/sso-v1.component";
|
||||
@@ -165,7 +164,6 @@ import "../platform/popup/locales";
|
||||
TwoFactorOptionsComponent,
|
||||
UpdateTempPasswordComponent,
|
||||
UserVerificationComponent,
|
||||
AccountSecurityComponentV1,
|
||||
VaultTimeoutInputComponent,
|
||||
ViewComponent,
|
||||
ViewCustomFieldsComponent,
|
||||
|
||||
@@ -171,7 +171,7 @@ describe("AddEditFolderDialogComponent", () => {
|
||||
it("deletes the folder", async () => {
|
||||
await component.deleteFolder();
|
||||
|
||||
expect(deleteFolder).toHaveBeenCalledWith(folderView.id);
|
||||
expect(deleteFolder).toHaveBeenCalledWith(folderView.id, "");
|
||||
expect(showToast).toHaveBeenCalledWith({
|
||||
variant: "success",
|
||||
title: null,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
@@ -67,6 +67,7 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
|
||||
name: ["", Validators.required],
|
||||
});
|
||||
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
constructor(
|
||||
@@ -114,10 +115,10 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
|
||||
this.folder.name = this.folderForm.controls.name.value;
|
||||
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
|
||||
const folder = await this.folderService.encrypt(this.folder, userKey);
|
||||
await this.folderApiService.save(folder);
|
||||
await this.folderApiService.save(folder, activeUserId);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
@@ -144,7 +145,8 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.folderApiService.delete(this.folder.id);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
await this.folderApiService.delete(this.folder.id, activeUserId);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<bit-search
|
||||
autocomplete="off"
|
||||
[placeholder]="'search' | i18n"
|
||||
[(ngModel)]="searchText"
|
||||
(ngModelChange)="onSearchTextChanged()"
|
||||
|
||||
@@ -7,9 +7,12 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
@@ -32,7 +35,7 @@ describe("VaultPopupListFiltersService", () => {
|
||||
} as unknown as CollectionService;
|
||||
|
||||
const folderService = {
|
||||
folderViews$,
|
||||
folderViews$: () => folderViews$,
|
||||
} as unknown as FolderService;
|
||||
|
||||
const cipherService = {
|
||||
@@ -60,6 +63,8 @@ describe("VaultPopupListFiltersService", () => {
|
||||
policyAppliesToActiveUser$.next(false);
|
||||
policyService.policyAppliesToActiveUser$.mockClear();
|
||||
|
||||
const accountService = mockAccountServiceWith("userId" as UserId);
|
||||
|
||||
collectionService.getAllNested = () => Promise.resolve([]);
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
@@ -92,6 +97,10 @@ describe("VaultPopupListFiltersService", () => {
|
||||
useValue: { getGlobal: () => ({ state$, update }) },
|
||||
},
|
||||
{ provide: FormBuilder, useClass: FormBuilder },
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@@ -102,6 +103,8 @@ export class VaultPopupListFiltersService {
|
||||
map((ciphers) => Object.values(ciphers)),
|
||||
);
|
||||
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private folderService: FolderService,
|
||||
private cipherService: CipherService,
|
||||
@@ -111,6 +114,7 @@ export class VaultPopupListFiltersService {
|
||||
private formBuilder: FormBuilder,
|
||||
private policyService: PolicyService,
|
||||
private stateProvider: StateProvider,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.filterForm.controls.organization.valueChanges
|
||||
.pipe(takeUntilDestroyed())
|
||||
@@ -264,61 +268,68 @@ export class VaultPopupListFiltersService {
|
||||
/**
|
||||
* Folder array structured to be directly passed to `ChipSelectComponent`
|
||||
*/
|
||||
folders$: Observable<ChipSelectOption<FolderView>[]> = combineLatest([
|
||||
this.filters$.pipe(
|
||||
distinctUntilChanged(
|
||||
(previousFilter, currentFilter) =>
|
||||
// Only update the collections when the organizationId filter changes
|
||||
previousFilter.organization?.id === currentFilter.organization?.id,
|
||||
folders$: Observable<ChipSelectOption<FolderView>[]> = this.activeUserId$.pipe(
|
||||
switchMap((userId) =>
|
||||
combineLatest([
|
||||
this.filters$.pipe(
|
||||
distinctUntilChanged(
|
||||
(previousFilter, currentFilter) =>
|
||||
// Only update the collections when the organizationId filter changes
|
||||
previousFilter.organization?.id === currentFilter.organization?.id,
|
||||
),
|
||||
),
|
||||
this.folderService.folderViews$(userId),
|
||||
this.cipherViews$,
|
||||
]).pipe(
|
||||
map(([filters, folders, cipherViews]): [PopupListFilter, FolderView[], CipherView[]] => {
|
||||
if (folders.length === 1 && folders[0].id === null) {
|
||||
// Do not display folder selections when only the "no folder" option is available.
|
||||
return [filters, [], cipherViews];
|
||||
}
|
||||
|
||||
// Sort folders by alphabetic name
|
||||
folders.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
let arrangedFolders = folders;
|
||||
|
||||
const noFolder = folders.find((f) => f.id === null);
|
||||
|
||||
if (noFolder) {
|
||||
// Update `name` of the "no folder" option to "Items with no folder"
|
||||
const updatedNoFolder = {
|
||||
...noFolder,
|
||||
name: this.i18nService.t("itemsWithNoFolder"),
|
||||
};
|
||||
|
||||
// Move the "no folder" option to the end of the list
|
||||
arrangedFolders = [...folders.filter((f) => f.id !== null), updatedNoFolder];
|
||||
}
|
||||
return [filters, arrangedFolders, cipherViews];
|
||||
}),
|
||||
map(([filters, folders, cipherViews]) => {
|
||||
const organizationId = filters.organization?.id ?? null;
|
||||
|
||||
// When no org or "My vault" is selected, return all folders
|
||||
if (organizationId === null || organizationId === MY_VAULT_ID) {
|
||||
return folders;
|
||||
}
|
||||
|
||||
const orgCiphers = cipherViews.filter((c) => c.organizationId === organizationId);
|
||||
|
||||
// Return only the folders that have ciphers within the filtered organization
|
||||
return folders.filter((f) => orgCiphers.some((oc) => oc.folderId === f.id));
|
||||
}),
|
||||
map((folders) => {
|
||||
const nestedFolders = this.getAllFoldersNested(folders);
|
||||
return new DynamicTreeNode<FolderView>({
|
||||
fullList: folders,
|
||||
nestedList: nestedFolders,
|
||||
});
|
||||
}),
|
||||
map((folders) =>
|
||||
folders.nestedList.map((f) => this.convertToChipSelectOption(f, "bwi-folder")),
|
||||
),
|
||||
),
|
||||
),
|
||||
this.folderService.folderViews$,
|
||||
this.cipherViews$,
|
||||
]).pipe(
|
||||
map(([filters, folders, cipherViews]): [PopupListFilter, FolderView[], CipherView[]] => {
|
||||
if (folders.length === 1 && folders[0].id === null) {
|
||||
// Do not display folder selections when only the "no folder" option is available.
|
||||
return [filters, [], cipherViews];
|
||||
}
|
||||
|
||||
// Sort folders by alphabetic name
|
||||
folders.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
let arrangedFolders = folders;
|
||||
|
||||
const noFolder = folders.find((f) => f.id === null);
|
||||
|
||||
if (noFolder) {
|
||||
// Update `name` of the "no folder" option to "Items with no folder"
|
||||
noFolder.name = this.i18nService.t("itemsWithNoFolder");
|
||||
|
||||
// Move the "no folder" option to the end of the list
|
||||
arrangedFolders = [...folders.filter((f) => f.id !== null), noFolder];
|
||||
}
|
||||
return [filters, arrangedFolders, cipherViews];
|
||||
}),
|
||||
map(([filters, folders, cipherViews]) => {
|
||||
const organizationId = filters.organization?.id ?? null;
|
||||
|
||||
// When no org or "My vault" is selected, return all folders
|
||||
if (organizationId === null || organizationId === MY_VAULT_ID) {
|
||||
return folders;
|
||||
}
|
||||
|
||||
const orgCiphers = cipherViews.filter((c) => c.organizationId === organizationId);
|
||||
|
||||
// Return only the folders that have ciphers within the filtered organization
|
||||
return folders.filter((f) => orgCiphers.some((oc) => oc.folderId === f.id));
|
||||
}),
|
||||
map((folders) => {
|
||||
const nestedFolders = this.getAllFoldersNested(folders);
|
||||
return new DynamicTreeNode<FolderView>({
|
||||
fullList: folders,
|
||||
nestedList: nestedFolders,
|
||||
});
|
||||
}),
|
||||
map((folders) =>
|
||||
folders.nestedList.map((f) => this.convertToChipSelectOption(f, "bwi-folder")),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,13 @@ import { By } from "@angular/platform-browser";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
@@ -52,8 +55,9 @@ describe("FoldersV2Component", () => {
|
||||
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
|
||||
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
||||
{ provide: LogService, useValue: mock<LogService>() },
|
||||
{ provide: FolderService, useValue: { folderViews$ } },
|
||||
{ provide: FolderService, useValue: { folderViews$: () => folderViews$ } },
|
||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||
{ provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) },
|
||||
],
|
||||
})
|
||||
.overrideComponent(FoldersV2Component, {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { map, Observable } from "rxjs";
|
||||
import { filter, map, Observable, switchMap } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import {
|
||||
@@ -45,18 +47,21 @@ export class FoldersV2Component {
|
||||
folders$: Observable<FolderView[]>;
|
||||
|
||||
NoFoldersIcon = VaultIcons.NoFolders;
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private folderService: FolderService,
|
||||
private dialogService: DialogService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.folders$ = this.folderService.folderViews$.pipe(
|
||||
this.folders$ = this.activeUserId$.pipe(
|
||||
filter((userId): userId is UserId => userId !== null),
|
||||
switchMap((userId) => this.folderService.folderViews$(userId)),
|
||||
map((folders) => {
|
||||
// Remove the last folder, which is the "no folder" option folder
|
||||
if (folders.length > 0) {
|
||||
return folders.slice(0, folders.length - 1);
|
||||
}
|
||||
|
||||
return folders;
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { map, Observable } from "rxjs";
|
||||
import { filter, map, Observable, switchMap } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
|
||||
@@ -12,16 +14,21 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
export class FoldersComponent {
|
||||
folders$: Observable<FolderView[]>;
|
||||
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private folderService: FolderService,
|
||||
private router: Router,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.folders$ = this.folderService.folderViews$.pipe(
|
||||
this.folders$ = this.activeUserId$.pipe(
|
||||
filter((userId): userId is UserId => userId != null),
|
||||
switchMap((userId) => this.folderService.folderViews$(userId)),
|
||||
map((folders) => {
|
||||
// Remove the last folder, which is the "no folder" option folder
|
||||
if (folders.length > 0) {
|
||||
folders = folders.slice(0, folders.length - 1);
|
||||
return folders.slice(0, folders.length - 1);
|
||||
}
|
||||
|
||||
return folders;
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ export class VaultFilterService extends BaseVaultFilterService {
|
||||
collectionService: CollectionService,
|
||||
policyService: PolicyService,
|
||||
stateProvider: StateProvider,
|
||||
private accountService: AccountService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
organizationService,
|
||||
@@ -33,6 +33,7 @@ export class VaultFilterService extends BaseVaultFilterService {
|
||||
collectionService,
|
||||
policyService,
|
||||
stateProvider,
|
||||
accountService,
|
||||
);
|
||||
this.vaultFilter.myVaultOnly = false;
|
||||
this.vaultFilter.selectedOrganizationId = null;
|
||||
|
||||
@@ -118,58 +118,58 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Bitwarden - wachtwoordbeheerder</value>
|
||||
<value>Bitwarden Wachtwoordbeheerder</value>
|
||||
</data>
|
||||
<data name="Summary" xml:space="preserve">
|
||||
<value>At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.</value>
|
||||
<value>Thuis, op het werk of onderweg, Bitwarden beveiligt eenvoudig al je wachtwoorden, sleutels en gevoelige informatie.</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more!
|
||||
<value>Erkend als de beste wachtwoordmanager door PCMag, WIRED, The Verge, CNET, G2 en meer!
|
||||
|
||||
SECURE YOUR DIGITAL LIFE
|
||||
Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access.
|
||||
BEVEILIG JE DIGITALE LEVEN
|
||||
Beveilig je digitale leven en bescherm je tegen datalekken door unieke, sterke wachtwoorden te genereren en op te slaan voor elke account. Bewaar alles in een end-to-end versleutelde wachtwoordkluis waar alleen jij toegang toe hebt.
|
||||
|
||||
ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE
|
||||
Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions.
|
||||
OVERAL EN ALTIJD TOEGANG TOT JE GEGEVENS, OP ELK APPARAAT
|
||||
Beheer, bewaar, beveilig en deel een onbeperkt aantal wachtwoorden op een onbeperkt aantal apparaten zonder beperkingen.
|
||||
|
||||
EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE
|
||||
Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features.
|
||||
IEDEREEN ZOU DE MIDDELEN MOETEN HEBBEN OM VEILIG ONLINE TE BLIJVEN
|
||||
Gebruik Bitwarden gratis, zonder advertenties of verkoop van gegevens. Bitwarden vindt dat iedereen de mogelijkheid moet hebben om veilig online te zijn. Premium abonnementen bieden toegang tot geavanceerde functies.
|
||||
|
||||
EMPOWER YOUR TEAMS WITH BITWARDEN
|
||||
Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more.
|
||||
VERSTERK JE TEAMS MET BITWARDEN
|
||||
Abonnementen voor Teams en Enterprise worden geleverd met professionele zakelijke functies. Enkele voorbeelden zijn SSO-integratie, zelf hosten, directory-integratie en SCIM provisioning, globaal beleid, API-toegang, gebeurtenislogboeken en meer.
|
||||
|
||||
Use Bitwarden to secure your workforce and share sensitive information with colleagues.
|
||||
Gebruik Bitwarden om je medewerkers te beveiligen en gevoelige informatie te delen met collega's.
|
||||
|
||||
|
||||
More reasons to choose Bitwarden:
|
||||
Meer redenen om voor Bitwarden te kiezen:
|
||||
|
||||
World-Class Encryption
|
||||
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashtag, and PBKDF2 SHA-256) so your data stays secure and private.
|
||||
Encryptie van wereldklasse
|
||||
Wachtwoorden worden beschermd met geavanceerde end-to-end versleuteling (AES-256 bit, salted hashtag en PBKDF2 SHA-256) zodat je gegevens veilig en privé blijven.
|
||||
|
||||
3rd-party Audits
|
||||
Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications.
|
||||
Audits door derde partijen
|
||||
Bitwarden voert regelmatig uitgebreide beveiligingsaudits uit bij gerenommeerde beveiligingsbedrijven. Deze jaarlijkse audits omvatten broncodebeoordelingen en penetratietests voor Bitwarden IP's, servers en webapplicaties.
|
||||
|
||||
Advanced 2FA
|
||||
Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey.
|
||||
Geavanceerde 2FA
|
||||
Beveilig je login met een authenticator van derden, codes per e-mail of FIDO2 WebAuthn referenties zoals een hardware beveiligingssleutel of passkey.
|
||||
|
||||
Bitwarden Send
|
||||
Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure.
|
||||
Verstuur gegevens rechtstreeks naar anderen met behoud van end-to-end versleutelde beveiliging en beperking van blootstelling.
|
||||
|
||||
Built-in Generator
|
||||
Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy.
|
||||
Ingebouwde generator
|
||||
Maak lange, complexe en duidelijke wachtwoorden en unieke gebruikersnamen voor elke site die je bezoekt. Integreer met e-mail alias providers voor extra privacy.
|
||||
|
||||
Global Translations
|
||||
Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin.
|
||||
Wereldwijde vertalingen
|
||||
Bitwarden vertalingen bestaan voor meer dan 60 talen, vertaald door de wereldwijde gemeenschap via Crowdin.
|
||||
|
||||
Cross-Platform Applications
|
||||
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
|
||||
Platformoverkoepelende applicaties
|
||||
Beveilig en deel gevoelige gegevens in je Bitwarden Vault vanuit elke browser, mobiel apparaat of desktop OS, en meer.
|
||||
|
||||
Bitwarden secures more than just passwords
|
||||
End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev!
|
||||
Bitwarden beveiligt meer dan alleen wachtwoorden
|
||||
Met de end-to-end versleutelde oplossingen voor referentiebeheer van Bitwarden kunnen organisaties alles beveiligen, inclusief ontwikkelaarsgeheimen en ervaringen met wachtwoorden. Bezoek Bitwarden.com voor meer informatie over Bitwarden Secrets Manager en Bitwarden Passwordless.dev!
|
||||
</value>
|
||||
</data>
|
||||
<data name="AssetTitle" xml:space="preserve">
|
||||
<value>At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.</value>
|
||||
<value>Thuis, op het werk of onderweg, Bitwarden beveiligt eenvoudig al je wachtwoorden, sleutels en gevoelige informatie.</value>
|
||||
</data>
|
||||
<data name="ScreenshotSync" xml:space="preserve">
|
||||
<value>Synchroniseer en gebruik je kluis op meerdere apparaten</value>
|
||||
|
||||
@@ -24,6 +24,8 @@ import { CipherResponse } from "../vault/models/cipher.response";
|
||||
import { FolderResponse } from "../vault/models/folder.response";
|
||||
|
||||
export class EditCommand {
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
@@ -121,12 +123,12 @@ export class EditCommand {
|
||||
|
||||
cipher.collectionIds = req;
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const updatedCipher = await this.cipherService.saveCollectionsWithServer(cipher);
|
||||
const decCipher = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(
|
||||
updatedCipher,
|
||||
await firstValueFrom(this.activeUserId$),
|
||||
),
|
||||
);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
@@ -136,7 +138,8 @@ export class EditCommand {
|
||||
}
|
||||
|
||||
private async editFolder(id: string, req: FolderExport) {
|
||||
const folder = await this.folderService.getFromState(id);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const folder = await this.folderService.getFromState(id, activeUserId);
|
||||
if (folder == null) {
|
||||
return Response.notFound();
|
||||
}
|
||||
@@ -144,12 +147,11 @@ export class EditCommand {
|
||||
let folderView = await folder.decrypt();
|
||||
folderView = FolderExport.toView(req, folderView);
|
||||
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
|
||||
const encFolder = await this.folderService.encrypt(folderView, userKey);
|
||||
try {
|
||||
await this.folderApiService.save(encFolder);
|
||||
const updatedFolder = await this.folderService.get(folder.id);
|
||||
await this.folderApiService.save(encFolder, activeUserId);
|
||||
const updatedFolder = await this.folderService.get(folder.id, activeUserId);
|
||||
const decFolder = await updatedFolder.decrypt();
|
||||
const res = new FolderResponse(decFolder);
|
||||
return Response.success(res);
|
||||
|
||||
@@ -51,6 +51,8 @@ import { FolderResponse } from "../vault/models/folder.response";
|
||||
import { DownloadCommand } from "./download.command";
|
||||
|
||||
export class GetCommand extends DownloadCommand {
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
@@ -113,10 +115,8 @@ export class GetCommand extends DownloadCommand {
|
||||
let decCipher: CipherView = null;
|
||||
if (Utils.isGuid(id)) {
|
||||
const cipher = await this.cipherService.get(id);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
if (cipher != null) {
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
decCipher = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
@@ -383,13 +383,14 @@ export class GetCommand extends DownloadCommand {
|
||||
|
||||
private async getFolder(id: string) {
|
||||
let decFolder: FolderView = null;
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
if (Utils.isGuid(id)) {
|
||||
const folder = await this.folderService.getFromState(id);
|
||||
const folder = await this.folderService.getFromState(id, activeUserId);
|
||||
if (folder != null) {
|
||||
decFolder = await folder.decrypt();
|
||||
}
|
||||
} else if (id.trim() !== "") {
|
||||
let folders = await this.folderService.getAllDecryptedFromState();
|
||||
let folders = await this.folderService.getAllDecryptedFromState(activeUserId);
|
||||
folders = CliUtils.searchFolders(folders, id);
|
||||
if (folders.length > 1) {
|
||||
return Response.multipleResults(folders.map((f) => f.id));
|
||||
@@ -551,9 +552,7 @@ export class GetCommand extends DownloadCommand {
|
||||
private async getFingerprint(id: string) {
|
||||
let fingerprint: string[] = null;
|
||||
if (id === "me") {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const publicKey = await firstValueFrom(this.keyService.userPublicKey$(activeUserId));
|
||||
fingerprint = await this.keyService.getFingerprint(activeUserId, publicKey);
|
||||
} else if (Utils.isGuid(id)) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import {
|
||||
OrganizationUserApiService,
|
||||
@@ -12,6 +12,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { ListResponse as ApiListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@@ -38,6 +39,7 @@ export class ListCommand {
|
||||
private organizationUserApiService: OrganizationUserApiService,
|
||||
private apiService: ApiService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async run(object: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
@@ -135,7 +137,10 @@ export class ListCommand {
|
||||
}
|
||||
|
||||
private async listFolders(options: Options) {
|
||||
let folders = await this.folderService.getAllDecryptedFromState();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
let folders = await this.folderService.getAllDecryptedFromState(activeUserId);
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
folders = CliUtils.searchFolders(folders, options.search);
|
||||
|
||||
@@ -76,6 +76,7 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.organizationUserApiService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.createCommand = new CreateCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
@@ -115,6 +116,7 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.cipherAuthorizationService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.confirmCommand = new ConfirmCommand(
|
||||
this.serviceContainer.apiService,
|
||||
|
||||
@@ -113,6 +113,7 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.organizationUserApiService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(object, cmd);
|
||||
|
||||
@@ -321,6 +322,7 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.cipherAuthorizationService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(object, id, cmd);
|
||||
this.processResponse(response);
|
||||
|
||||
@@ -30,6 +30,8 @@ import { CipherResponse } from "./models/cipher.response";
|
||||
import { FolderResponse } from "./models/folder.response";
|
||||
|
||||
export class CreateCommand {
|
||||
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
@@ -86,9 +88,7 @@ export class CreateCommand {
|
||||
}
|
||||
|
||||
private async createCipher(req: CipherExport) {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId);
|
||||
try {
|
||||
const newCipher = await this.cipherService.createWithServer(cipher);
|
||||
@@ -152,9 +152,7 @@ export class CreateCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const updatedCipher = await this.cipherService.saveAttachmentRawWithServer(
|
||||
cipher,
|
||||
fileName,
|
||||
@@ -171,12 +169,12 @@ export class CreateCommand {
|
||||
}
|
||||
|
||||
private async createFolder(req: FolderExport) {
|
||||
const activeAccountId = await firstValueFrom(this.accountService.activeAccount$);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId.id);
|
||||
const activeUserId = await firstValueFrom(this.activeUserId$);
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
|
||||
const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey);
|
||||
try {
|
||||
await this.folderApiService.save(folder);
|
||||
const newFolder = await this.folderService.get(folder.id);
|
||||
await this.folderApiService.save(folder, activeUserId);
|
||||
const newFolder = await this.folderService.get(folder.id, activeUserId);
|
||||
const decFolder = await newFolder.decrypt();
|
||||
const res = new FolderResponse(decFolder);
|
||||
return Response.success(res);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -19,6 +20,7 @@ export class DeleteCommand {
|
||||
private folderApiService: FolderApiServiceAbstraction,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
private cipherAuthorizationService: CipherAuthorizationService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
@@ -103,13 +105,16 @@ export class DeleteCommand {
|
||||
}
|
||||
|
||||
private async deleteFolder(id: string) {
|
||||
const folder = await this.folderService.getFromState(id);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const folder = await this.folderService.getFromState(id, activeUserId);
|
||||
if (folder == null) {
|
||||
return Response.notFound();
|
||||
}
|
||||
|
||||
try {
|
||||
await this.folderApiService.delete(id);
|
||||
await this.folderApiService.delete(id, activeUserId);
|
||||
return Response.success();
|
||||
} catch (e) {
|
||||
return Response.error(e);
|
||||
|
||||
114
apps/desktop/desktop_native/Cargo.lock
generated
114
apps/desktop/desktop_native/Cargo.lock
generated
@@ -567,9 +567,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.2"
|
||||
version = "1.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
|
||||
checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -616,9 +616,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.22"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69371e34337c4c984bbe322360c2547210bf632eb2814bbe78a6e87a2935bd2b"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -626,9 +626,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.22"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e24c1b4099818523236a8ca881d2b45db98dadfb4625cf6608c12069fcbbde1"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -650,9 +650,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
@@ -703,16 +703,6 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.0"
|
||||
@@ -740,9 +730,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
@@ -759,9 +749,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
@@ -821,9 +811,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e1ec88093d2abd9cf1b09ffd979136b8e922bf31cad966a8fe0d73233112ef"
|
||||
checksum = "4d44ff199ff93242c3afe480ab588d544dd08d72e92885e152ffebc670f076ad"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-cmd",
|
||||
@@ -835,9 +825,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afa390d956ee7ccb41aeed7ed7856ab3ffb4fc587e7216be7e0f83e949b4e6c"
|
||||
checksum = "66fd8f17ad454fc1e4f4ab83abffcc88a532e90350d3ffddcb73030220fcbd52"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -849,9 +839,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-cmd"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c23bfff654d6227cbc83de8e059d2f8678ede5fc3a6c5a35d5c379983cc61e6"
|
||||
checksum = "4717c9c806a9e07fdcb34c84965a414ea40fafe57667187052cf1eb7f5e8a8a9"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
@@ -862,15 +852,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c01b36e22051bc6928a78583f1621abaaf7621561c2ada1b00f7878fbe2caa"
|
||||
checksum = "2f6515329bf3d98f4073101c7866ff2bec4e635a13acb82e3f3753fff0bf43cb"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.133"
|
||||
version = "1.0.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6e14013136fac689345d17b9a6df55977251f11d333c0a571e8d963b55e1f95"
|
||||
checksum = "fb93e6a7ce8ec985c02bbb758237a31598b340acbbc3c19c5a4fa6adaaac92ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -935,7 +925,7 @@ dependencies = [
|
||||
"bitwarden-russh",
|
||||
"byteorder",
|
||||
"cbc",
|
||||
"core-foundation 0.10.0",
|
||||
"core-foundation",
|
||||
"desktop_objc",
|
||||
"dirs",
|
||||
"ed25519",
|
||||
@@ -996,7 +986,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
"core-foundation 0.9.4",
|
||||
"core-foundation",
|
||||
"glob",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -1144,7 +1134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1176,9 +1166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
@@ -1518,9 +1508,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.162"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@@ -1529,7 +1519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1643,9 +1633,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -1677,9 +1667,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi-build"
|
||||
version = "2.1.3"
|
||||
version = "2.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
|
||||
checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19"
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
@@ -2375,9 +2365,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -2479,15 +2469,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.41"
|
||||
version = "0.38.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
|
||||
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2562,12 +2552,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d0283c0a4a22a0f1b0e4edca251aa20b92fc96eaa09b84bec052f9415e9d71"
|
||||
checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
@@ -2575,9 +2565,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.12.0"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||
checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -2585,27 +2575,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.215"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2846,7 +2836,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3350,7 +3340,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -31,7 +31,7 @@ base64 = "=0.22.1"
|
||||
byteorder = "=1.5.0"
|
||||
cbc = { version = "=0.1.2", features = ["alloc"] }
|
||||
homedir = "=0.3.4"
|
||||
libc = "=0.2.162"
|
||||
libc = "=0.2.169"
|
||||
pin-project = "=1.1.7"
|
||||
dirs = "=5.0.1"
|
||||
futures = "=0.3.31"
|
||||
@@ -81,8 +81,8 @@ keytar = "=0.1.6"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = { version = "=0.10.0", optional = true }
|
||||
security-framework = { version = "=3.0.0", optional = true }
|
||||
security-framework-sys = { version = "=2.12.0", optional = true }
|
||||
security-framework = { version = "=3.1.0", optional = true }
|
||||
security-framework-sys = { version = "=2.13.0", optional = true }
|
||||
desktop_objc = { path = "../objc" }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
||||
@@ -21,10 +21,10 @@ serde = { version = "1.0.205", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
tokio = { version = "1.39.2", features = ["sync"] }
|
||||
tokio-util = "0.7.11"
|
||||
uniffi = { version = "0.28.0", features = ["cli"] }
|
||||
uniffi = { version = "0.28.3", features = ["cli"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
oslog = "0.2.0"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.28.0", features = ["build"] }
|
||||
uniffi = { version = "0.28.3", features = ["build"] }
|
||||
|
||||
@@ -30,4 +30,4 @@ tokio-stream = "=0.1.15"
|
||||
windows-registry = "=0.3.0"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "=2.1.3"
|
||||
napi-build = "=2.1.4"
|
||||
|
||||
@@ -14,8 +14,8 @@ thiserror = "=1.0.69"
|
||||
tokio = "1.39.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "=0.9.4"
|
||||
core-foundation = "=0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.104"
|
||||
cc = "1.2.4"
|
||||
glob = "0.3.1"
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
"entitlements": "resources/entitlements.mas.plist",
|
||||
"entitlementsInherit": "resources/entitlements.mas.inherit.plist",
|
||||
"entitlementsLoginHelper": "resources/entitlements.mas.loginhelper.plist",
|
||||
"hardenedRuntime": false,
|
||||
"hardenedRuntime": true,
|
||||
"extendInfo": {
|
||||
"LSMinimumSystemVersion": "12",
|
||||
"ElectronTeamID": "LTZ2PFU5D6"
|
||||
|
||||
@@ -6,5 +6,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -8,5 +8,7 @@
|
||||
<array>
|
||||
<string>LTZ2PFU5D6.com.bitwarden.desktop</string>
|
||||
</array>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<!--
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
<true/>
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.inherit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<!--
|
||||
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
|
||||
|
||||
@@ -34,5 +34,7 @@
|
||||
<string>/Library/Application Support/Microsoft Edge Canary/NativeMessagingHosts/</string>
|
||||
<string>/Library/Application Support/Vivaldi/NativeMessagingHosts/</string>
|
||||
</array>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -3399,10 +3399,10 @@
|
||||
"message": "ملاحظة هامة"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "إعداد المصادقة الثنائية"
|
||||
"message": "إعداد تسجيل الدخول بخطوتين"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "سيقوم Bitwarden بإرسال رمز إلى البريد الإلكتروني الخاص بحسابك للتحقق من تسجيلات الدخول من الأجهزة الجديدة ابتداء من فبراير 2025."
|
||||
"message": "سيقوم Bitwarden بإرسال رمز إلى البريد الإلكتروني الخاص بحسابك للتحقق من تسجيلات الدخول من الأجهزة الجديدة ابتداءً من فبراير 2025."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "يمكنك إعداد المصادقة الثنائية كطريقة بديلة لحماية حسابك أو تغيير بريدك الإلكتروني إلى بريد يمكنك الوصول إليه."
|
||||
@@ -3426,7 +3426,7 @@
|
||||
"message": "نعم، يمكنني الوصول بشكل موثوق إلى بريدي الإلكتروني"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "تفعيل المصادقة الثنائية"
|
||||
"message": "تشغيل تسجيل الدخول بخطوتين"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "تغيير البريد الإلكتروني الخاص بالحساب"
|
||||
|
||||
@@ -2729,7 +2729,7 @@
|
||||
"message": "Laitteeseesi lähetettiin ilmoitus"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "Varmista, että vahvistavan laitteen holvi on avattu ja että se näyttää saman tunnistelausekkeen"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Tarvitsetko toisen vaihtoehdon?"
|
||||
@@ -3402,7 +3402,7 @@
|
||||
"message": "Määritä kaksivaiheinen kirjautuminen"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden lähettää tilisi sähköpostiosoitteeseen koodin, jolla voit vahvistaa kirjautumiset uusista laitteista helmikuusta 2025 alkaen."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
|
||||
@@ -323,7 +323,7 @@
|
||||
"message": "Générer un mot de passe"
|
||||
},
|
||||
"generatePassphrase": {
|
||||
"message": "Generate passphrase"
|
||||
"message": "Générer une phrase de passe"
|
||||
},
|
||||
"type": {
|
||||
"message": "Type"
|
||||
@@ -467,7 +467,7 @@
|
||||
"message": "Copier la clé privée SSH"
|
||||
},
|
||||
"copyPassphrase": {
|
||||
"message": "Copy passphrase",
|
||||
"message": "Copier la phrase de passe",
|
||||
"description": "Copy passphrase to clipboard"
|
||||
},
|
||||
"copyUri": {
|
||||
@@ -926,7 +926,7 @@
|
||||
"message": "La session d'authentification a expiré. Veuillez redémarrer le processus de connexion."
|
||||
},
|
||||
"selfHostBaseUrl": {
|
||||
"message": "Self-host server URL",
|
||||
"message": "URL du serveur auto-hébergé",
|
||||
"description": "Label for field requesting a self-hosted integration service URL"
|
||||
},
|
||||
"apiUrl": {
|
||||
@@ -1951,7 +1951,7 @@
|
||||
"message": "Votre nouveau mot de passe principal ne répond pas aux exigences de politique de sécurité."
|
||||
},
|
||||
"receiveMarketingEmailsV2": {
|
||||
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
|
||||
"message": "Obtenez des conseils, des annonces et des opportunités de recherche de la part de Bitwarden dans votre boîte de réception."
|
||||
},
|
||||
"unsubscribe": {
|
||||
"message": "Se désabonner"
|
||||
@@ -2478,10 +2478,10 @@
|
||||
"message": "Générer le nom d'utilisateur"
|
||||
},
|
||||
"generateEmail": {
|
||||
"message": "Generate email"
|
||||
"message": "Générer un courriel"
|
||||
},
|
||||
"spinboxBoundariesHint": {
|
||||
"message": "Value must be between $MIN$ and $MAX$.",
|
||||
"message": "La valeur doit être comprise entre $MIN$ et $MAX$.",
|
||||
"description": "Explains spin box minimum and maximum values to the user",
|
||||
"placeholders": {
|
||||
"min": {
|
||||
@@ -2495,7 +2495,7 @@
|
||||
}
|
||||
},
|
||||
"passwordLengthRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
|
||||
"message": " Utilisez $RECOMMENDED$ caractères ou plus pour générer un mot de passe fort.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2505,7 +2505,7 @@
|
||||
}
|
||||
},
|
||||
"passphraseNumWordsRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
|
||||
"message": " Utilisez $RECOMMENDED$ mots ou plus pour générer une phrase de passe forte.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2729,10 +2729,10 @@
|
||||
"message": "A notification was sent to your device"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "Assurez-vous que votre compte est déverrouillé et que la phrase d'empreinte digitale correspond à celle de l'autre appareil"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Need another option?"
|
||||
"message": "Besoin d'une autre option ?"
|
||||
},
|
||||
"fingerprintMatchInfo": {
|
||||
"message": "Veuillez vous assurer que votre coffre est déverrouillé et que la phrase d'empreinte correspond à celle de l'autre appareil."
|
||||
@@ -2741,13 +2741,13 @@
|
||||
"message": "Phrase d'empreinte"
|
||||
},
|
||||
"youWillBeNotifiedOnceTheRequestIsApproved": {
|
||||
"message": "You will be notified once the request is approved"
|
||||
"message": "Vous serez notifié une fois que la demande sera approuvée"
|
||||
},
|
||||
"needAnotherOption": {
|
||||
"message": "La connexion avec l'appareil doit être configurée dans les paramètres de l'application Bitwarden. Besoin d'une autre option ?"
|
||||
},
|
||||
"viewAllLogInOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "Afficher toutes les options de connexion"
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "Afficher toutes les options de connexion"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"message": "Nota sicura"
|
||||
},
|
||||
"typeSshKey": {
|
||||
"message": "SSH key"
|
||||
"message": "Chiave SSH"
|
||||
},
|
||||
"folders": {
|
||||
"message": "Cartelle"
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "Welcome back"
|
||||
"message": "Bentornato"
|
||||
},
|
||||
"moveToOrgDesc": {
|
||||
"message": "Scegli un'organizzazione in cui vuoi spostare questo elemento. Spostarlo in un'organizzazione trasferisce la proprietà dell'elemento all'organizzazione. Una volta spostato, non sarai più il proprietario diretto di questo elemento."
|
||||
@@ -181,61 +181,61 @@
|
||||
"message": "Indirizzo"
|
||||
},
|
||||
"sshPrivateKey": {
|
||||
"message": "Private key"
|
||||
"message": "Chiave privata"
|
||||
},
|
||||
"sshPublicKey": {
|
||||
"message": "Public key"
|
||||
"message": "Chiave pubblica"
|
||||
},
|
||||
"sshFingerprint": {
|
||||
"message": "Fingerprint"
|
||||
"message": "Impronta digitale"
|
||||
},
|
||||
"sshKeyAlgorithm": {
|
||||
"message": "Key type"
|
||||
"message": "Tipo di chiave"
|
||||
},
|
||||
"sshKeyAlgorithmED25519": {
|
||||
"message": "ED25519"
|
||||
},
|
||||
"sshKeyAlgorithmRSA2048": {
|
||||
"message": "RSA 2048-Bit"
|
||||
"message": "RSA a 2048 bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA3072": {
|
||||
"message": "RSA 3072-Bit"
|
||||
"message": "RSA a 3072 bit"
|
||||
},
|
||||
"sshKeyAlgorithmRSA4096": {
|
||||
"message": "RSA 4096-Bit"
|
||||
"message": "RSA a 4096 bit"
|
||||
},
|
||||
"sshKeyGenerated": {
|
||||
"message": "A new SSH key was generated"
|
||||
"message": "È stata generata una nuova chiave SSH"
|
||||
},
|
||||
"sshKeyWrongPassword": {
|
||||
"message": "The password you entered is incorrect."
|
||||
"message": "La password inserita non è corretta."
|
||||
},
|
||||
"importSshKey": {
|
||||
"message": "Import"
|
||||
"message": "Importa"
|
||||
},
|
||||
"confirmSshKeyPassword": {
|
||||
"message": "Confirm password"
|
||||
"message": "Conferma password"
|
||||
},
|
||||
"enterSshKeyPasswordDesc": {
|
||||
"message": "Enter the password for the SSH key."
|
||||
"message": "Inserisci la password per la chiave SSH."
|
||||
},
|
||||
"enterSshKeyPassword": {
|
||||
"message": "Enter password"
|
||||
"message": "Inserisci password"
|
||||
},
|
||||
"sshAgentUnlockRequired": {
|
||||
"message": "Please unlock your vault to approve the SSH key request."
|
||||
"message": "Sbloccare la cassaforte per approvare la richiesta di chiave SSH."
|
||||
},
|
||||
"sshAgentUnlockTimeout": {
|
||||
"message": "SSH key request timed out."
|
||||
"message": "Richiesta chiave SSH scaduta."
|
||||
},
|
||||
"enableSshAgent": {
|
||||
"message": "Enable SSH agent"
|
||||
"message": "Abilita agente SSH"
|
||||
},
|
||||
"enableSshAgentDesc": {
|
||||
"message": "Enable the SSH agent to sign SSH requests right from your Bitwarden vault."
|
||||
"message": "Abilita l'agente SSH per firmare le richieste SSH direttamente dalla tua cassaforte Bitwarden."
|
||||
},
|
||||
"enableSshAgentHelp": {
|
||||
"message": "The SSH agent is a service targeted at developers that allows you to sign SSH requests directly from your Bitwarden vault."
|
||||
"message": "L'agente SSH è un servizio rivolto agli sviluppatori che consente di firmare le richieste SSH direttamente dalla tua cassaforte Bitwarden."
|
||||
},
|
||||
"premiumRequired": {
|
||||
"message": "Premium necessario"
|
||||
@@ -461,10 +461,10 @@
|
||||
"message": "Copia password"
|
||||
},
|
||||
"regenerateSshKey": {
|
||||
"message": "Regenerate SSH key"
|
||||
"message": "Rigenera la chiave SSH"
|
||||
},
|
||||
"copySshPrivateKey": {
|
||||
"message": "Copy SSH private key"
|
||||
"message": "Copia chiave privata SSH"
|
||||
},
|
||||
"copyPassphrase": {
|
||||
"message": "Copia passphrase",
|
||||
@@ -624,7 +624,7 @@
|
||||
"message": "Crea account"
|
||||
},
|
||||
"newToBitwarden": {
|
||||
"message": "New to Bitwarden?"
|
||||
"message": "Nuovo in Bitwarden?"
|
||||
},
|
||||
"setAStrongPassword": {
|
||||
"message": "Imposta una password robusta"
|
||||
@@ -636,16 +636,16 @@
|
||||
"message": "Accedi"
|
||||
},
|
||||
"logInToBitwarden": {
|
||||
"message": "Log in to Bitwarden"
|
||||
"message": "Accedi a Bitwarden"
|
||||
},
|
||||
"logInWithPasskey": {
|
||||
"message": "Log in with passkey"
|
||||
"message": "Accedi con passkey"
|
||||
},
|
||||
"loginWithDevice": {
|
||||
"message": "Log in with device"
|
||||
"message": "Accedi con dispositivo"
|
||||
},
|
||||
"useSingleSignOn": {
|
||||
"message": "Use single sign-on"
|
||||
"message": "Usa il Single Sign-On"
|
||||
},
|
||||
"submit": {
|
||||
"message": "Invia"
|
||||
@@ -920,10 +920,10 @@
|
||||
"message": "URL del server"
|
||||
},
|
||||
"authenticationTimeout": {
|
||||
"message": "Authentication timeout"
|
||||
"message": "Timeout autenticazione"
|
||||
},
|
||||
"authenticationSessionTimedOut": {
|
||||
"message": "The authentication session timed out. Please restart the login process."
|
||||
"message": "La sessione di autenticazione è scaduta. Accedi di nuovo."
|
||||
},
|
||||
"selfHostBaseUrl": {
|
||||
"message": "URL server autogestito",
|
||||
@@ -1393,13 +1393,13 @@
|
||||
"message": "Cronologia delle password"
|
||||
},
|
||||
"generatorHistory": {
|
||||
"message": "Generator history"
|
||||
"message": "Cronologia generatore"
|
||||
},
|
||||
"clearGeneratorHistoryTitle": {
|
||||
"message": "Clear generator history"
|
||||
"message": "Cancella cronologia generatore"
|
||||
},
|
||||
"cleargGeneratorHistoryDescription": {
|
||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||
"message": "Se continui, tutte le voci verranno eliminate definitivamente dalla cronologia del generatore. Vuoi continuare?"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Cancella",
|
||||
@@ -1409,13 +1409,13 @@
|
||||
"message": "Non ci sono password da mostrare."
|
||||
},
|
||||
"clearHistory": {
|
||||
"message": "Clear history"
|
||||
"message": "Cancella cronologia"
|
||||
},
|
||||
"nothingToShow": {
|
||||
"message": "Nothing to show"
|
||||
"message": "Niente da mostrare"
|
||||
},
|
||||
"nothingGeneratedRecently": {
|
||||
"message": "You haven't generated anything recently"
|
||||
"message": "Non hai generato niente di recente"
|
||||
},
|
||||
"undo": {
|
||||
"message": "Annulla"
|
||||
@@ -1771,10 +1771,10 @@
|
||||
"message": "L'eliminazione del tuo account è permanente. Non può essere annullata."
|
||||
},
|
||||
"cannotDeleteAccount": {
|
||||
"message": "Cannot delete account"
|
||||
"message": "Impossibile eliminare account"
|
||||
},
|
||||
"cannotDeleteAccountDesc": {
|
||||
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
|
||||
"message": "Questa azione non può essere completata perché il tuo account è di proprietà di un'organizzazione. Contatta l'amministratore della tua organizzazione per dettagli."
|
||||
},
|
||||
"accountDeleted": {
|
||||
"message": "Account eliminato"
|
||||
@@ -2481,7 +2481,7 @@
|
||||
"message": "Genera e-mail"
|
||||
},
|
||||
"spinboxBoundariesHint": {
|
||||
"message": "Value must be between $MIN$ and $MAX$.",
|
||||
"message": "Il valore deve essere compreso tra $MIN$ e $MAX$.",
|
||||
"description": "Explains spin box minimum and maximum values to the user",
|
||||
"placeholders": {
|
||||
"min": {
|
||||
@@ -2495,7 +2495,7 @@
|
||||
}
|
||||
},
|
||||
"passwordLengthRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
|
||||
"message": " Usa $RECOMMENDED$ caratteri o più per generare una password forte.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2505,7 +2505,7 @@
|
||||
}
|
||||
},
|
||||
"passphraseNumWordsRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
|
||||
"message": " Usa $RECOMMENDED$ parole o più per generare una passphrase forte.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2726,13 +2726,13 @@
|
||||
"message": "Una notifica è stata inviata al tuo dispositivo."
|
||||
},
|
||||
"aNotificationWasSentToYourDevice": {
|
||||
"message": "A notification was sent to your device"
|
||||
"message": "Una notifica è stata inviata al tuo dispositivo"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "Assicurati che il tuo account sia sbloccato e che la frase dell'impronta digitale corrisponda nell'altro dispositivo"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Need another option?"
|
||||
"message": "Bisogno di un'altra opzione?"
|
||||
},
|
||||
"fingerprintMatchInfo": {
|
||||
"message": "Assicurati che la tua cassaforte sia sbloccata e che la frase impronta corrisponda sull'altro dispositivo."
|
||||
@@ -2741,13 +2741,13 @@
|
||||
"message": "Frase impronta"
|
||||
},
|
||||
"youWillBeNotifiedOnceTheRequestIsApproved": {
|
||||
"message": "You will be notified once the request is approved"
|
||||
"message": "Sarai notificato una volta che la richiesta sarà approvata"
|
||||
},
|
||||
"needAnotherOption": {
|
||||
"message": "L'accesso con dispositivo deve essere abilitato nelle impostazioni dell'app Bitwarden. Ti serve un'altra opzione?"
|
||||
},
|
||||
"viewAllLogInOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "Visualizza tutte le opzioni di accesso"
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "Visualizza tutte le opzioni di accesso"
|
||||
@@ -2869,7 +2869,7 @@
|
||||
"message": "Controlla se la tua password è presente in una violazione dei dati"
|
||||
},
|
||||
"loggedInExclamation": {
|
||||
"message": "Logged in!"
|
||||
"message": "Accesso effettuato!"
|
||||
},
|
||||
"important": {
|
||||
"message": "Importante:"
|
||||
@@ -2902,16 +2902,16 @@
|
||||
"message": "Aggiornamento delle impostazioni consigliato"
|
||||
},
|
||||
"rememberThisDeviceToMakeFutureLoginsSeamless": {
|
||||
"message": "Remember this device to make future logins seamless"
|
||||
"message": "Ricorda questo dispositivo per rendere immediati i futuri accessi"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Approvazione del dispositivo obbligatoria. Seleziona un'opzione di approvazione:"
|
||||
},
|
||||
"deviceApprovalRequiredV2": {
|
||||
"message": "Device approval required"
|
||||
"message": "Approvazione dispositivo richiesta"
|
||||
},
|
||||
"selectAnApprovalOptionBelow": {
|
||||
"message": "Select an approval option below"
|
||||
"message": "Seleziona un'opzione di approvazione sotto"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Ricorda questo dispositivo"
|
||||
@@ -2966,7 +2966,7 @@
|
||||
"message": "Email utente mancante"
|
||||
},
|
||||
"activeUserEmailNotFoundLoggingYouOut": {
|
||||
"message": "Active user email not found. Logging you out."
|
||||
"message": "Email utente attiva non trovata. Logout in corso."
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "Dispositivo fidato"
|
||||
@@ -3363,55 +3363,55 @@
|
||||
"message": "Non è stato possibile trovare nessuna porta libera per il login Sso."
|
||||
},
|
||||
"authorize": {
|
||||
"message": "Authorize"
|
||||
"message": "Autorizza"
|
||||
},
|
||||
"deny": {
|
||||
"message": "Deny"
|
||||
"message": "Nega"
|
||||
},
|
||||
"sshkeyApprovalTitle": {
|
||||
"message": "Confirm SSH key usage"
|
||||
"message": "Conferma l'uso della chiave SSH"
|
||||
},
|
||||
"sshkeyApprovalMessageInfix": {
|
||||
"message": "is requesting access to"
|
||||
"message": "richiede l'accesso a"
|
||||
},
|
||||
"unknownApplication": {
|
||||
"message": "An application"
|
||||
"message": "Un'applicazione"
|
||||
},
|
||||
"sshKeyPasswordUnsupported": {
|
||||
"message": "Importing password protected SSH keys is not yet supported"
|
||||
"message": "L'importazione di chiavi SSH protette da password non è ancora supportata"
|
||||
},
|
||||
"invalidSshKey": {
|
||||
"message": "The SSH key is invalid"
|
||||
"message": "La chiave SSH non è valida"
|
||||
},
|
||||
"sshKeyTypeUnsupported": {
|
||||
"message": "The SSH key type is not supported"
|
||||
"message": "Il tipo di chiave SSH non è supportato"
|
||||
},
|
||||
"importSshKeyFromClipboard": {
|
||||
"message": "Import key from clipboard"
|
||||
"message": "Importa chiave dagli Appunti"
|
||||
},
|
||||
"sshKeyPasted": {
|
||||
"message": "SSH key imported successfully"
|
||||
"message": "Chiave SSH importata correttamente"
|
||||
},
|
||||
"fileSavedToDevice": {
|
||||
"message": "File salvato sul dispositivo. Gestisci dai download del dispositivo."
|
||||
},
|
||||
"importantNotice": {
|
||||
"message": "Important notice"
|
||||
"message": "Notifica importante"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Set up two-step login"
|
||||
"message": "Imposta accesso in due passaggi"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden invierà un codice all'e-mail del tuo account per verificare gli accessi da nuovi dispositivi a partire da febbraio 2025."
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
"message": "Puoi impostare l'accesso in due passaggi come modo alternativo per proteggere il tuo account, o cambiare la tua e-mail in una alla quale puoi accedere."
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
"message": "Ricordamelo più tardi"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "Do you have reliable access to your email, $EMAIL$?",
|
||||
"message": "Hai accesso affidabile alla tua e-mail, $EMAIL$?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -3420,15 +3420,15 @@
|
||||
}
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessNo": {
|
||||
"message": "No, I do not"
|
||||
"message": "No, non ce l'ho"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "Yes, I can reliably access my email"
|
||||
"message": "Sì, posso accedere in modo affidabile alla mia e-mail"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "Turn on two-step login"
|
||||
"message": "Attiva accesso in due passaggi"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
"message": "Cambia l'e-mail dell'account"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"message": "セキュアメモ"
|
||||
},
|
||||
"typeSshKey": {
|
||||
"message": "SSH key"
|
||||
"message": "SSH キー"
|
||||
},
|
||||
"folders": {
|
||||
"message": "フォルダー"
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
"welcomeBack": {
|
||||
"message": "Welcome back"
|
||||
"message": "ようこそ"
|
||||
},
|
||||
"moveToOrgDesc": {
|
||||
"message": "このアイテムを移動する組織を選択してください。組織に移動すると、アイテムの所有権がその組織に移行します。 このアイテムが移動された後、あなたはこのアイテムの直接の所有者にはなりません。"
|
||||
@@ -181,16 +181,16 @@
|
||||
"message": "住所"
|
||||
},
|
||||
"sshPrivateKey": {
|
||||
"message": "Private key"
|
||||
"message": "秘密鍵"
|
||||
},
|
||||
"sshPublicKey": {
|
||||
"message": "Public key"
|
||||
"message": "公開鍵"
|
||||
},
|
||||
"sshFingerprint": {
|
||||
"message": "Fingerprint"
|
||||
"message": "フィンガープリント"
|
||||
},
|
||||
"sshKeyAlgorithm": {
|
||||
"message": "Key type"
|
||||
"message": "キーの種類"
|
||||
},
|
||||
"sshKeyAlgorithmED25519": {
|
||||
"message": "ED25519"
|
||||
@@ -205,37 +205,37 @@
|
||||
"message": "RSA 4096-Bit"
|
||||
},
|
||||
"sshKeyGenerated": {
|
||||
"message": "A new SSH key was generated"
|
||||
"message": "新しい SSH 鍵が生成されました"
|
||||
},
|
||||
"sshKeyWrongPassword": {
|
||||
"message": "The password you entered is incorrect."
|
||||
"message": "入力されたパスワードが間違っています。"
|
||||
},
|
||||
"importSshKey": {
|
||||
"message": "Import"
|
||||
"message": "インポート"
|
||||
},
|
||||
"confirmSshKeyPassword": {
|
||||
"message": "Confirm password"
|
||||
"message": "パスワードを確認"
|
||||
},
|
||||
"enterSshKeyPasswordDesc": {
|
||||
"message": "Enter the password for the SSH key."
|
||||
"message": "SSH キーのパスワードを入力します。"
|
||||
},
|
||||
"enterSshKeyPassword": {
|
||||
"message": "Enter password"
|
||||
"message": "パスワードを入力"
|
||||
},
|
||||
"sshAgentUnlockRequired": {
|
||||
"message": "Please unlock your vault to approve the SSH key request."
|
||||
"message": "SSH キーリクエストを承認するには、保管庫のロックを解除してください。"
|
||||
},
|
||||
"sshAgentUnlockTimeout": {
|
||||
"message": "SSH key request timed out."
|
||||
"message": "SSH キーの要求がタイムアウトしました。"
|
||||
},
|
||||
"enableSshAgent": {
|
||||
"message": "Enable SSH agent"
|
||||
"message": "SSH エージェントを有効にする"
|
||||
},
|
||||
"enableSshAgentDesc": {
|
||||
"message": "Enable the SSH agent to sign SSH requests right from your Bitwarden vault."
|
||||
"message": "Bitwarden 保管庫から直接 SSH 要求に署名するために SSH エージェントを有効にします。"
|
||||
},
|
||||
"enableSshAgentHelp": {
|
||||
"message": "The SSH agent is a service targeted at developers that allows you to sign SSH requests directly from your Bitwarden vault."
|
||||
"message": "SSH エージェントとは、Bitwarden 保管庫から直接 SSH リクエストに署名できる、開発者を対象としたサービスです。"
|
||||
},
|
||||
"premiumRequired": {
|
||||
"message": "プレミアム会員専用"
|
||||
@@ -323,7 +323,7 @@
|
||||
"message": "パスワードの自動生成"
|
||||
},
|
||||
"generatePassphrase": {
|
||||
"message": "Generate passphrase"
|
||||
"message": "パスフレーズを生成"
|
||||
},
|
||||
"type": {
|
||||
"message": "タイプ"
|
||||
@@ -461,13 +461,13 @@
|
||||
"message": "パスワードのコピー"
|
||||
},
|
||||
"regenerateSshKey": {
|
||||
"message": "Regenerate SSH key"
|
||||
"message": "SSH キーを再生成"
|
||||
},
|
||||
"copySshPrivateKey": {
|
||||
"message": "Copy SSH private key"
|
||||
"message": "SSH 秘密鍵をコピー"
|
||||
},
|
||||
"copyPassphrase": {
|
||||
"message": "Copy passphrase",
|
||||
"message": "パスフレーズをコピー",
|
||||
"description": "Copy passphrase to clipboard"
|
||||
},
|
||||
"copyUri": {
|
||||
@@ -624,7 +624,7 @@
|
||||
"message": "アカウントの作成"
|
||||
},
|
||||
"newToBitwarden": {
|
||||
"message": "New to Bitwarden?"
|
||||
"message": "Bitwarden は初めてですか?"
|
||||
},
|
||||
"setAStrongPassword": {
|
||||
"message": "強力なパスワードを設定する"
|
||||
@@ -636,16 +636,16 @@
|
||||
"message": "ログイン"
|
||||
},
|
||||
"logInToBitwarden": {
|
||||
"message": "Log in to Bitwarden"
|
||||
"message": "Bitwarden にログイン"
|
||||
},
|
||||
"logInWithPasskey": {
|
||||
"message": "Log in with passkey"
|
||||
"message": "パスキーでログイン"
|
||||
},
|
||||
"loginWithDevice": {
|
||||
"message": "Log in with device"
|
||||
"message": "デバイスでログイン"
|
||||
},
|
||||
"useSingleSignOn": {
|
||||
"message": "Use single sign-on"
|
||||
"message": "シングルサインオンを使用する"
|
||||
},
|
||||
"submit": {
|
||||
"message": "送信"
|
||||
@@ -920,13 +920,13 @@
|
||||
"message": "サーバー URL"
|
||||
},
|
||||
"authenticationTimeout": {
|
||||
"message": "Authentication timeout"
|
||||
"message": "認証のタイムアウト"
|
||||
},
|
||||
"authenticationSessionTimedOut": {
|
||||
"message": "The authentication session timed out. Please restart the login process."
|
||||
"message": "認証セッションの有効期限が切れました。ログイン操作を最初からやり直してください。"
|
||||
},
|
||||
"selfHostBaseUrl": {
|
||||
"message": "Self-host server URL",
|
||||
"message": "自己ホスト型サーバーの URL",
|
||||
"description": "Label for field requesting a self-hosted integration service URL"
|
||||
},
|
||||
"apiUrl": {
|
||||
@@ -1320,7 +1320,7 @@
|
||||
"description": "Copy credit card number"
|
||||
},
|
||||
"copyEmail": {
|
||||
"message": "Copy email"
|
||||
"message": "メールアドレスをコピー"
|
||||
},
|
||||
"copySecurityCode": {
|
||||
"message": "セキュリティコードのコピー",
|
||||
@@ -1393,13 +1393,13 @@
|
||||
"message": "パスワードの履歴"
|
||||
},
|
||||
"generatorHistory": {
|
||||
"message": "Generator history"
|
||||
"message": "生成履歴"
|
||||
},
|
||||
"clearGeneratorHistoryTitle": {
|
||||
"message": "Clear generator history"
|
||||
"message": "生成履歴を消去"
|
||||
},
|
||||
"cleargGeneratorHistoryDescription": {
|
||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||
"message": "続行すると、すべてのエントリは生成履歴から完全に削除されます。続行してもよろしいですか?"
|
||||
},
|
||||
"clear": {
|
||||
"message": "消去する",
|
||||
@@ -1409,13 +1409,13 @@
|
||||
"message": "表示するパスワードがありません"
|
||||
},
|
||||
"clearHistory": {
|
||||
"message": "Clear history"
|
||||
"message": "履歴を消去"
|
||||
},
|
||||
"nothingToShow": {
|
||||
"message": "Nothing to show"
|
||||
"message": "表示するものがありません"
|
||||
},
|
||||
"nothingGeneratedRecently": {
|
||||
"message": "You haven't generated anything recently"
|
||||
"message": "最近生成したものはありません"
|
||||
},
|
||||
"undo": {
|
||||
"message": "元に戻す"
|
||||
@@ -1771,10 +1771,10 @@
|
||||
"message": "アカウントを恒久的に削除します。元に戻すことはできません。"
|
||||
},
|
||||
"cannotDeleteAccount": {
|
||||
"message": "Cannot delete account"
|
||||
"message": "アカウントを削除できません"
|
||||
},
|
||||
"cannotDeleteAccountDesc": {
|
||||
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
|
||||
"message": "このアカウントは組織が所有しているため、操作を完了できません。詳しくは組織の管理者へご確認ください。"
|
||||
},
|
||||
"accountDeleted": {
|
||||
"message": "アカウントが削除されました"
|
||||
@@ -2478,10 +2478,10 @@
|
||||
"message": "ユーザー名を生成"
|
||||
},
|
||||
"generateEmail": {
|
||||
"message": "Generate email"
|
||||
"message": "メールアドレスを生成"
|
||||
},
|
||||
"spinboxBoundariesHint": {
|
||||
"message": "Value must be between $MIN$ and $MAX$.",
|
||||
"message": "値は $MIN$ から $MAX$ の間でなければなりません。",
|
||||
"description": "Explains spin box minimum and maximum values to the user",
|
||||
"placeholders": {
|
||||
"min": {
|
||||
@@ -2495,7 +2495,7 @@
|
||||
}
|
||||
},
|
||||
"passwordLengthRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
|
||||
"message": " 強力なパスワードを生成するには、 $RECOMMENDED$ 文字以上を使用してください。",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2505,7 +2505,7 @@
|
||||
}
|
||||
},
|
||||
"passphraseNumWordsRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
|
||||
"message": " 強力なパスフレーズを生成するには、 $RECOMMENDED$ 単語以上を使用してください。",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2558,11 +2558,11 @@
|
||||
"message": "外部転送サービスを使用してメールエイリアスを生成します。"
|
||||
},
|
||||
"forwarderDomainName": {
|
||||
"message": "Email domain",
|
||||
"message": "メールアドレスのドメイン",
|
||||
"description": "Labels the domain name email forwarder service option"
|
||||
},
|
||||
"forwarderDomainNameHint": {
|
||||
"message": "Choose a domain that is supported by the selected service",
|
||||
"message": "選択したサービスでサポートされているドメインを選択してください",
|
||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||
},
|
||||
"forwarderError": {
|
||||
@@ -2726,13 +2726,13 @@
|
||||
"message": "デバイスに通知を送信しました。"
|
||||
},
|
||||
"aNotificationWasSentToYourDevice": {
|
||||
"message": "A notification was sent to your device"
|
||||
"message": "お使いのデバイスに通知が送信されました"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "アカウントがロック解除されていることと、フィンガープリントフレーズが他の端末と一致していることを確認してください"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Need another option?"
|
||||
"message": "別の選択肢が必要ですか?"
|
||||
},
|
||||
"fingerprintMatchInfo": {
|
||||
"message": "保管庫がロックされていることと、パスフレーズが他のデバイスと一致していることを確認してください。"
|
||||
@@ -2741,13 +2741,13 @@
|
||||
"message": "パスフレーズ"
|
||||
},
|
||||
"youWillBeNotifiedOnceTheRequestIsApproved": {
|
||||
"message": "You will be notified once the request is approved"
|
||||
"message": "リクエストが承認されると通知されます"
|
||||
},
|
||||
"needAnotherOption": {
|
||||
"message": "Bitwarden アプリの設定でデバイスでログインする必要があります。別のオプションが必要ですか?"
|
||||
},
|
||||
"viewAllLogInOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "すべてのログインオプションを表示"
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "すべてのログインオプションを表示"
|
||||
@@ -2869,7 +2869,7 @@
|
||||
"message": "このパスワードの既知のデータ流出を確認"
|
||||
},
|
||||
"loggedInExclamation": {
|
||||
"message": "Logged in!"
|
||||
"message": "ログインしました!"
|
||||
},
|
||||
"important": {
|
||||
"message": "重要"
|
||||
@@ -2902,16 +2902,16 @@
|
||||
"message": "設定の更新を推奨"
|
||||
},
|
||||
"rememberThisDeviceToMakeFutureLoginsSeamless": {
|
||||
"message": "Remember this device to make future logins seamless"
|
||||
"message": "このデバイスを記憶して今後のログインをシームレスにする"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "デバイスの承認が必要です。以下から承認オプションを選択してください:"
|
||||
},
|
||||
"deviceApprovalRequiredV2": {
|
||||
"message": "Device approval required"
|
||||
"message": "デバイスの承認が必要です"
|
||||
},
|
||||
"selectAnApprovalOptionBelow": {
|
||||
"message": "Select an approval option below"
|
||||
"message": "以下の承認オプションを選択してください"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "このデバイスを記憶する"
|
||||
@@ -2966,7 +2966,7 @@
|
||||
"message": "ユーザーのメールアドレスがありません"
|
||||
},
|
||||
"activeUserEmailNotFoundLoggingYouOut": {
|
||||
"message": "Active user email not found. Logging you out."
|
||||
"message": "アクティブなユーザーメールアドレスが見つかりません。ログアウトします。"
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "信頼されたデバイス"
|
||||
@@ -3363,55 +3363,55 @@
|
||||
"message": "SSO ログインのための空きポートが見つかりませんでした。"
|
||||
},
|
||||
"authorize": {
|
||||
"message": "Authorize"
|
||||
"message": "認可"
|
||||
},
|
||||
"deny": {
|
||||
"message": "Deny"
|
||||
"message": "拒否"
|
||||
},
|
||||
"sshkeyApprovalTitle": {
|
||||
"message": "Confirm SSH key usage"
|
||||
"message": "SSH 鍵の使用を確認します"
|
||||
},
|
||||
"sshkeyApprovalMessageInfix": {
|
||||
"message": "is requesting access to"
|
||||
"message": "がアクセスを要求しています: "
|
||||
},
|
||||
"unknownApplication": {
|
||||
"message": "An application"
|
||||
"message": "アプリ"
|
||||
},
|
||||
"sshKeyPasswordUnsupported": {
|
||||
"message": "Importing password protected SSH keys is not yet supported"
|
||||
"message": "パスワードで保護された SSH キーのインポートはまだサポートされていません"
|
||||
},
|
||||
"invalidSshKey": {
|
||||
"message": "The SSH key is invalid"
|
||||
"message": "SSH キーが無効です"
|
||||
},
|
||||
"sshKeyTypeUnsupported": {
|
||||
"message": "The SSH key type is not supported"
|
||||
"message": "サポートされていない種類の SSH キーです"
|
||||
},
|
||||
"importSshKeyFromClipboard": {
|
||||
"message": "Import key from clipboard"
|
||||
"message": "クリップボードからキーをインポート"
|
||||
},
|
||||
"sshKeyPasted": {
|
||||
"message": "SSH key imported successfully"
|
||||
"message": "SSH キーのインポートに成功しました"
|
||||
},
|
||||
"fileSavedToDevice": {
|
||||
"message": "ファイルをデバイスに保存しました。デバイスのダウンロードで管理できます。"
|
||||
},
|
||||
"importantNotice": {
|
||||
"message": "Important notice"
|
||||
"message": "重要なお知らせ"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Set up two-step login"
|
||||
"message": "2段階認証によるログインを設定する"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
|
||||
"message": "Bitwarden は2025年2月以降、新しいデバイスからのログイン時にアカウントのメールアドレスに確認コードを送信します。"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
|
||||
"message": "代わりに2段階認証によるログインでアカウントを保護するか、メールアドレスをあなたがアクセスできるものに変更できます。"
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
"message": "後で再通知"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "Do you have reliable access to your email, $EMAIL$?",
|
||||
"message": "新しいメールアドレス $EMAIL$ はあなたが管理しているものですか?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -3420,15 +3420,15 @@
|
||||
}
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessNo": {
|
||||
"message": "No, I do not"
|
||||
"message": "いいえ、違います"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "Yes, I can reliably access my email"
|
||||
"message": "はい、メールアドレスには私が確実にアクセスできます"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "Turn on two-step login"
|
||||
"message": "2段階認証によるログインを有効にする"
|
||||
},
|
||||
"changeAcctEmail": {
|
||||
"message": "Change account email"
|
||||
"message": "アカウントのメールアドレスを変更する"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,7 +945,7 @@
|
||||
"message": "Pictogrammenserver-URL"
|
||||
},
|
||||
"environmentSaved": {
|
||||
"message": "De omgevings-URL's zijn opgeslagen."
|
||||
"message": "Omgevings-URL's opgeslagen"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
@@ -1002,7 +1002,7 @@
|
||||
"message": "Nieuwe map toevoegen"
|
||||
},
|
||||
"view": {
|
||||
"message": "Beeld"
|
||||
"message": "Weergeven"
|
||||
},
|
||||
"account": {
|
||||
"message": "Account"
|
||||
@@ -1268,7 +1268,7 @@
|
||||
"description": "Copy to clipboard"
|
||||
},
|
||||
"checkForUpdates": {
|
||||
"message": "Controleren op updates"
|
||||
"message": "Controleren op updates…"
|
||||
},
|
||||
"version": {
|
||||
"message": "Versie $VERSION_NUM$",
|
||||
@@ -3026,7 +3026,7 @@
|
||||
}
|
||||
},
|
||||
"multipleInputEmails": {
|
||||
"message": "Een of meer e-mailadressen zijn ongeldig"
|
||||
"message": "Eén of meer e-mailadressen zijn ongeldig"
|
||||
},
|
||||
"inputTrimValidator": {
|
||||
"message": "Invoer mag niet alleen witruimte bevatten.",
|
||||
@@ -3051,7 +3051,7 @@
|
||||
"message": "-- Type om te filteren --"
|
||||
},
|
||||
"multiSelectLoading": {
|
||||
"message": "Opties ophalen..."
|
||||
"message": "Opties ophalen…"
|
||||
},
|
||||
"multiSelectNotFound": {
|
||||
"message": "Geen items gevonden"
|
||||
@@ -3243,7 +3243,7 @@
|
||||
"message": "LastPass Email"
|
||||
},
|
||||
"importingYourAccount": {
|
||||
"message": "Account impoteren..."
|
||||
"message": "Account impoteren…"
|
||||
},
|
||||
"lastPassMFARequired": {
|
||||
"message": "LastPass multifactor-authenticatie vereist"
|
||||
@@ -3396,7 +3396,7 @@
|
||||
"message": "Bestand op apparaat opgeslagen. Beheer vanaf de downloads op je apparaat."
|
||||
},
|
||||
"importantNotice": {
|
||||
"message": "Belangrijke mededeling"
|
||||
"message": "Belangrijke melding"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Tweestapsaanmelding instellen"
|
||||
|
||||
@@ -3399,7 +3399,7 @@
|
||||
"message": "Dôležité upozornenie"
|
||||
},
|
||||
"setupTwoStepLogin": {
|
||||
"message": "Nastavenie dvojstupňového prihlásenia"
|
||||
"message": "Nastaviť dvojstupňové prihlásenie"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "Bitwarden vám od februára 2025 pošle na e-mail vášho účtu kód na overenie prihlásenia z nových zariadení."
|
||||
|
||||
@@ -208,19 +208,19 @@
|
||||
"message": "Генерисан је нови SSH кључ"
|
||||
},
|
||||
"sshKeyWrongPassword": {
|
||||
"message": "The password you entered is incorrect."
|
||||
"message": "Лозинка коју сте унели није тачна."
|
||||
},
|
||||
"importSshKey": {
|
||||
"message": "Import"
|
||||
"message": "Увоз"
|
||||
},
|
||||
"confirmSshKeyPassword": {
|
||||
"message": "Confirm password"
|
||||
"message": "Потврда лозинке"
|
||||
},
|
||||
"enterSshKeyPasswordDesc": {
|
||||
"message": "Enter the password for the SSH key."
|
||||
"message": "Унети лозинку за SSH кључ."
|
||||
},
|
||||
"enterSshKeyPassword": {
|
||||
"message": "Enter password"
|
||||
"message": "Унесите лозинку"
|
||||
},
|
||||
"sshAgentUnlockRequired": {
|
||||
"message": "Откључајте свој сеф да бисте одобрили захтев за SSH кључ."
|
||||
@@ -920,10 +920,10 @@
|
||||
"message": "УРЛ Сервера"
|
||||
},
|
||||
"authenticationTimeout": {
|
||||
"message": "Authentication timeout"
|
||||
"message": "Истекло је време аутентификације"
|
||||
},
|
||||
"authenticationSessionTimedOut": {
|
||||
"message": "The authentication session timed out. Please restart the login process."
|
||||
"message": "Истекло је време сесије за аутентификацију. Молим вас покрените процес пријаве поново."
|
||||
},
|
||||
"selfHostBaseUrl": {
|
||||
"message": "УРЛ сервера који се самостално хостује",
|
||||
@@ -1393,13 +1393,13 @@
|
||||
"message": "Историја Лозинке"
|
||||
},
|
||||
"generatorHistory": {
|
||||
"message": "Generator history"
|
||||
"message": "Генератор историје"
|
||||
},
|
||||
"clearGeneratorHistoryTitle": {
|
||||
"message": "Clear generator history"
|
||||
"message": "Испразнити генератор историје"
|
||||
},
|
||||
"cleargGeneratorHistoryDescription": {
|
||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||
"message": "Ако наставите, сви уноси ће бити трајно избрисани из генератора историје. Да ли сте сигурни да желите да наставите?"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Очисти",
|
||||
@@ -1409,13 +1409,13 @@
|
||||
"message": "Нема лозинки за приказивање."
|
||||
},
|
||||
"clearHistory": {
|
||||
"message": "Clear history"
|
||||
"message": "Обриши историју"
|
||||
},
|
||||
"nothingToShow": {
|
||||
"message": "Nothing to show"
|
||||
"message": "Ништа за приказ"
|
||||
},
|
||||
"nothingGeneratedRecently": {
|
||||
"message": "You haven't generated anything recently"
|
||||
"message": "Недавно нисте ништа генерисали"
|
||||
},
|
||||
"undo": {
|
||||
"message": "Опозови"
|
||||
@@ -2481,7 +2481,7 @@
|
||||
"message": "Генеришите имејл"
|
||||
},
|
||||
"spinboxBoundariesHint": {
|
||||
"message": "Value must be between $MIN$ and $MAX$.",
|
||||
"message": "Вредност мора бити између $MIN$ и $MAX$.",
|
||||
"description": "Explains spin box minimum and maximum values to the user",
|
||||
"placeholders": {
|
||||
"min": {
|
||||
@@ -2495,7 +2495,7 @@
|
||||
}
|
||||
},
|
||||
"passwordLengthRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ characters or more to generate a strong password.",
|
||||
"message": " Употребити $RECOMMENDED$ знакова или више да бисте генерисали јаку лозинку.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2505,7 +2505,7 @@
|
||||
}
|
||||
},
|
||||
"passphraseNumWordsRecommendationHint": {
|
||||
"message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.",
|
||||
"message": " Употребити $RECOMMENDED$ речи или више да бисте генерисали јаку приступну фразу.",
|
||||
"description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).",
|
||||
"placeholders": {
|
||||
"recommended": {
|
||||
@@ -2726,13 +2726,13 @@
|
||||
"message": "Обавештење је послато на ваш уређај."
|
||||
},
|
||||
"aNotificationWasSentToYourDevice": {
|
||||
"message": "A notification was sent to your device"
|
||||
"message": "Обавештење је послато на ваш уређај"
|
||||
},
|
||||
"makeSureYourAccountIsUnlockedAndTheFingerprintEtc": {
|
||||
"message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device"
|
||||
"message": "Уверите се да је ваш налог откључан и да се фраза отиска подудара на другом уређају"
|
||||
},
|
||||
"needAnotherOptionV1": {
|
||||
"message": "Need another option?"
|
||||
"message": "Треба Вам друга опција?"
|
||||
},
|
||||
"fingerprintMatchInfo": {
|
||||
"message": "Уверите се да је ваш сеф откључан и да се фраза отиска прста подудара на другом уређају."
|
||||
@@ -2741,13 +2741,13 @@
|
||||
"message": "Сигурносна фраза сефа"
|
||||
},
|
||||
"youWillBeNotifiedOnceTheRequestIsApproved": {
|
||||
"message": "You will be notified once the request is approved"
|
||||
"message": "Бићете обавештени када захтев буде одобрен"
|
||||
},
|
||||
"needAnotherOption": {
|
||||
"message": "Пријава помоћу уређаја мора бити подешена у подешавањима Bitwarden апликације. Потребна је друга опција?"
|
||||
},
|
||||
"viewAllLogInOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "Погледајте сав извештај у опције"
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "Погредајте све опције пријављивања"
|
||||
@@ -2869,7 +2869,7 @@
|
||||
"message": "Проверите познате упада података за ову лозинку"
|
||||
},
|
||||
"loggedInExclamation": {
|
||||
"message": "Logged in!"
|
||||
"message": "Пријављено!"
|
||||
},
|
||||
"important": {
|
||||
"message": "Важно:"
|
||||
@@ -2902,16 +2902,16 @@
|
||||
"message": "Препоручено ажурирање поставки"
|
||||
},
|
||||
"rememberThisDeviceToMakeFutureLoginsSeamless": {
|
||||
"message": "Remember this device to make future logins seamless"
|
||||
"message": "Запамтити овај уређај да би будуће пријаве биле беспрекорне"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Потребно је одобрење уређаја. Изаберите опцију одобрења испод:"
|
||||
},
|
||||
"deviceApprovalRequiredV2": {
|
||||
"message": "Device approval required"
|
||||
"message": "Потребно је одобрење уређаја"
|
||||
},
|
||||
"selectAnApprovalOptionBelow": {
|
||||
"message": "Select an approval option below"
|
||||
"message": "Изаберите опцију одобрења у наставку"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Запамти овај уређај"
|
||||
@@ -2966,7 +2966,7 @@
|
||||
"message": "Недостаје имејл корисника"
|
||||
},
|
||||
"activeUserEmailNotFoundLoggingYouOut": {
|
||||
"message": "Active user email not found. Logging you out."
|
||||
"message": "Имејл активног корисника није пронађен. Одјављивање."
|
||||
},
|
||||
"deviceTrusted": {
|
||||
"message": "Уређај поуздан"
|
||||
|
||||
@@ -3363,10 +3363,10 @@
|
||||
"message": "SSO girişi için açık port bulunamadı."
|
||||
},
|
||||
"authorize": {
|
||||
"message": "Authorize"
|
||||
"message": "Yetkilendir"
|
||||
},
|
||||
"deny": {
|
||||
"message": "Deny"
|
||||
"message": "Reddet"
|
||||
},
|
||||
"sshkeyApprovalTitle": {
|
||||
"message": "Confirm SSH key usage"
|
||||
|
||||
@@ -742,7 +742,7 @@
|
||||
"message": "必须填写确认主密码。"
|
||||
},
|
||||
"masterPasswordMinlength": {
|
||||
"message": "主密码必须至少 $VALUE$ 个字符长度。",
|
||||
"message": "主密码长度必须至少为 $VALUE$ 个字符。",
|
||||
"description": "The Master Password must be at least a specific number of characters long.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
@@ -1044,7 +1044,7 @@
|
||||
"message": "前往网页 App 吗?"
|
||||
},
|
||||
"changeMasterPasswordOnWebConfirmation": {
|
||||
"message": "您可以在 Bitwarden 网页应用上更改您的主密码。"
|
||||
"message": "您可以在 Bitwarden 网页 App 上更改您的主密码。"
|
||||
},
|
||||
"fingerprintPhrase": {
|
||||
"message": "指纹短语",
|
||||
@@ -1058,7 +1058,7 @@
|
||||
"message": "转到网页版密码库"
|
||||
},
|
||||
"getMobileApp": {
|
||||
"message": "获取移动应用程序"
|
||||
"message": "获取移动 App"
|
||||
},
|
||||
"getBrowserExtension": {
|
||||
"message": "获取浏览器扩展"
|
||||
@@ -1247,13 +1247,13 @@
|
||||
"message": "语言"
|
||||
},
|
||||
"languageDesc": {
|
||||
"message": "更改应用程序所使用的语言。重新启动后生效。"
|
||||
"message": "更改应用程序所使用的语言。重启后生效。"
|
||||
},
|
||||
"theme": {
|
||||
"message": "主题"
|
||||
},
|
||||
"themeDesc": {
|
||||
"message": "更改本应用程序的颜色主题。"
|
||||
"message": "更改应用程序的颜色主题。"
|
||||
},
|
||||
"dark": {
|
||||
"message": "深色",
|
||||
@@ -1665,7 +1665,7 @@
|
||||
"message": "确认密码库导出"
|
||||
},
|
||||
"exportWarningDesc": {
|
||||
"message": "导出的密码库数据包含未加密格式。您不应该通过不安全的渠道(例如电子邮件)来存储或发送导出的文件。用完后请立即将其删除。"
|
||||
"message": "此导出包含未加密格式的密码库数据。您不应该通过不安全的渠道(例如电子邮件)来存储或发送此导出文件。使用完后请立即将其删除。"
|
||||
},
|
||||
"encExportKeyWarningDesc": {
|
||||
"message": "此导出将使用您账户的加密密钥来加密您的数据。如果您曾经轮换过账户的加密密钥,您应将其重新导出,否则您将无法解密导出的文件。"
|
||||
@@ -1711,7 +1711,7 @@
|
||||
"message": "使用 PIN 码解锁"
|
||||
},
|
||||
"setYourPinCode": {
|
||||
"message": "设定您用来解锁 Bitwarden 的 PIN 码。您的 PIN 设置将在您完全注销本应用程序时被重置。"
|
||||
"message": "设置用于解锁 Bitwarden 的 PIN 码。您的 PIN 设置将在您完全注销应用程序时被重置。"
|
||||
},
|
||||
"pinRequired": {
|
||||
"message": "需要 PIN 码。"
|
||||
@@ -1753,7 +1753,7 @@
|
||||
"message": "应用程序启动时要求使用触控 ID"
|
||||
},
|
||||
"requirePasswordOnStart": {
|
||||
"message": "应用程序启动时要求输入密码或 PIN 码"
|
||||
"message": "App 启动时要求输入密码或 PIN 码"
|
||||
},
|
||||
"recommendedForSecurity": {
|
||||
"message": "安全起见,推荐设置。"
|
||||
@@ -2402,7 +2402,7 @@
|
||||
"message": "偏好设置"
|
||||
},
|
||||
"appPreferences": {
|
||||
"message": "应用设置(所有账户)"
|
||||
"message": "应用程序设置(所有账户)"
|
||||
},
|
||||
"accountSwitcherLimitReached": {
|
||||
"message": "已达到账户上限。请注销一个账户后再添加其他账户。"
|
||||
@@ -3369,7 +3369,7 @@
|
||||
"message": "拒绝"
|
||||
},
|
||||
"sshkeyApprovalTitle": {
|
||||
"message": "确认 SSH 密钥的使用方式"
|
||||
"message": "确认 SSH 密钥的使用"
|
||||
},
|
||||
"sshkeyApprovalMessageInfix": {
|
||||
"message": "正在请求访问"
|
||||
@@ -3402,7 +3402,7 @@
|
||||
"message": "设置两步登录"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage1": {
|
||||
"message": "从 2025 年 02 月开始,Bitwarden 将向您的账户电子邮箱发送一个代码,以验证来自新设备的登录。"
|
||||
"message": "从 2025 年 02 月起,当有来自新设备的登录时,Bitwarden 将向您的账户电子邮箱发送验证码。"
|
||||
},
|
||||
"newDeviceVerificationNoticeContentPage2": {
|
||||
"message": "您可以设置两步登录作为保护账户的替代方法,或将您的电子邮箱更改为您可以访问的电子邮箱。"
|
||||
@@ -3411,7 +3411,7 @@
|
||||
"message": "稍后提醒我"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneFormContent": {
|
||||
"message": "您能可靠地访问您的电子邮箱 $EMAIL$ 吗?",
|
||||
"message": "您能可正常访问您的电子邮箱 $EMAIL$ 吗?",
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"content": "$1",
|
||||
@@ -3423,7 +3423,7 @@
|
||||
"message": "不,我不能"
|
||||
},
|
||||
"newDeviceVerificationNoticePageOneEmailAccessYes": {
|
||||
"message": "是的,我可以可靠地访问我的电子邮箱"
|
||||
"message": "是的,我可以正常访问我的电子邮箱"
|
||||
},
|
||||
"turnOnTwoStepLogin": {
|
||||
"message": "开启两步登录"
|
||||
|
||||
44
apps/desktop/src/package-lock.json
generated
44
apps/desktop/src/package-lock.json
generated
@@ -23,50 +23,6 @@
|
||||
"node_modules/@bitwarden/desktop-napi": {
|
||||
"resolved": "../desktop_native/napi",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@phc/format": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
||||
"integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/argon2": {
|
||||
"version": "0.41.1",
|
||||
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz",
|
||||
"integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@phc/format": "^1.0.0",
|
||||
"node-addon-api": "^8.1.0",
|
||||
"node-gyp-build": "^4.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.1.tgz",
|
||||
"integrity": "sha512-vmEOvxwiH8tlOcv4SyE8RH34rI5/nWVaigUeAUPawC6f0+HoDthwI0vkMu4tbtsZrXq6QXFfrkhjofzKEs5tpA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18 || ^20 || >= 21"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz",
|
||||
"integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +208,9 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
});
|
||||
} else {
|
||||
password = await this.getSshKeyPassword();
|
||||
if (password === "") {
|
||||
return;
|
||||
}
|
||||
await this.importSshKeyFromClipboard(password);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -131,7 +131,9 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
|
||||
|
||||
protected getUserName(r: EventResponse, userId: string) {
|
||||
if (r.installationId != null) {
|
||||
return `Installation: ${r.installationId}`;
|
||||
return {
|
||||
name: `Installation: ${r.installationId}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (userId != null) {
|
||||
|
||||
@@ -6,7 +6,11 @@ import { mock } from "jest-mock-extended";
|
||||
|
||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@@ -29,6 +33,8 @@ describe("EmergencyViewDialogComponent", () => {
|
||||
card: {},
|
||||
} as CipherView;
|
||||
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(Utils.newGuid() as UserId);
|
||||
|
||||
beforeEach(async () => {
|
||||
open.mockClear();
|
||||
close.mockClear();
|
||||
@@ -43,6 +49,7 @@ describe("EmergencyViewDialogComponent", () => {
|
||||
{ provide: DialogService, useValue: { open } },
|
||||
{ provide: DialogRef, useValue: { close } },
|
||||
{ provide: DIALOG_DATA, useValue: { cipher: mockCipher } },
|
||||
{ provide: AccountService, useValue: accountService },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
*ngIf="deprecateStripeSourcesAPI"
|
||||
[showAccountCredit]="false"
|
||||
></app-payment-v2>
|
||||
<app-tax-info [trialFlow]="true" (onCountryChanged)="changedCountry()"></app-tax-info>
|
||||
<app-tax-info [trialFlow]="true" (countryChanged)="changedCountry()"></app-tax-info>
|
||||
</div>
|
||||
<div class="tw-flex tw-space-x-2">
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
|
||||
@@ -131,11 +131,10 @@
|
||||
<bit-section>
|
||||
<h3 bitTypography="h2">{{ "paymentInformation" | i18n }}</h3>
|
||||
<app-payment-v2 [showBankAccount]="false"></app-payment-v2>
|
||||
<app-tax-info></app-tax-info>
|
||||
<app-tax-info (taxInformationChanged)="onTaxInformationChanged()"></app-tax-info>
|
||||
<div class="tw-mb-4">
|
||||
<div class="tw-text-muted tw-text-sm tw-flex tw-flex-col">
|
||||
<span>{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}</span>
|
||||
<!-- TODO: Currently incorrect - https://bitwarden.atlassian.net/browse/PM-11525 -->
|
||||
<span>{{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,13 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { combineLatest, concatMap, from, Observable, of } from "rxjs";
|
||||
import { debounceTime } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||
import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
@@ -44,6 +47,7 @@ export class PremiumV2Component {
|
||||
FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader,
|
||||
);
|
||||
|
||||
protected estimatedTax: number = 0;
|
||||
protected readonly familyPlanMaxUserCount = 6;
|
||||
protected readonly premiumPrice = 10;
|
||||
protected readonly storageGBPrice = 4;
|
||||
@@ -60,6 +64,7 @@ export class PremiumV2Component {
|
||||
private syncService: SyncService,
|
||||
private toastService: ToastService,
|
||||
private tokenService: TokenService,
|
||||
private taxService: TaxServiceAbstraction,
|
||||
) {
|
||||
this.isSelfHost = this.platformUtilsService.isSelfHost();
|
||||
|
||||
@@ -82,6 +87,12 @@ export class PremiumV2Component {
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.addOnFormGroup.controls.additionalStorage.valueChanges
|
||||
.pipe(debounceTime(1000), takeUntilDestroyed())
|
||||
.subscribe(() => {
|
||||
this.refreshSalesTax();
|
||||
});
|
||||
}
|
||||
|
||||
finalizeUpgrade = async () => {
|
||||
@@ -158,12 +169,6 @@ export class PremiumV2Component {
|
||||
return this.storageGBPrice * this.addOnFormGroup.value.additionalStorage;
|
||||
}
|
||||
|
||||
protected get estimatedTax(): number {
|
||||
return this.taxInfoComponent?.taxRate != null
|
||||
? (this.taxInfoComponent.taxRate / 100) * this.subtotal
|
||||
: 0;
|
||||
}
|
||||
|
||||
protected get premiumURL(): string {
|
||||
return `${this.cloudWebVaultURL}/#/settings/subscription/premium`;
|
||||
}
|
||||
@@ -179,4 +184,36 @@ export class PremiumV2Component {
|
||||
protected async onLicenseFileSelectedChanged(): Promise<void> {
|
||||
await this.postFinalizeUpgrade();
|
||||
}
|
||||
|
||||
private refreshSalesTax(): void {
|
||||
if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) {
|
||||
return;
|
||||
}
|
||||
const request: PreviewIndividualInvoiceRequest = {
|
||||
passwordManager: {
|
||||
additionalStorage: this.addOnFormGroup.value.additionalStorage,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: this.taxInfoComponent.postalCode,
|
||||
country: this.taxInfoComponent.country,
|
||||
},
|
||||
};
|
||||
|
||||
this.taxService
|
||||
.previewIndividualInvoice(request)
|
||||
.then((invoice) => {
|
||||
this.estimatedTax = invoice.taxAmount;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastService.showToast({
|
||||
title: "",
|
||||
variant: "error",
|
||||
message: this.i18nService.t(error.message),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected onTaxInformationChanged(): void {
|
||||
this.refreshSalesTax();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<bit-section>
|
||||
<h3 bitTypography="h2">{{ "paymentInformation" | i18n }}</h3>
|
||||
<app-payment [hideBank]="true"></app-payment>
|
||||
<app-tax-info></app-tax-info>
|
||||
<app-tax-info (taxInformationChanged)="onTaxInformationChanged()" />
|
||||
<div id="price" class="tw-my-4">
|
||||
<div class="tw-text-muted tw-text-sm">
|
||||
{{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom, Observable } from "rxjs";
|
||||
import { debounceTime } from "rxjs/operators";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||
import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
@@ -39,6 +43,9 @@ export class PremiumComponent implements OnInit {
|
||||
protected addonForm = new FormGroup({
|
||||
additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]),
|
||||
});
|
||||
|
||||
private estimatedTax: number = 0;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private i18nService: I18nService,
|
||||
@@ -50,9 +57,16 @@ export class PremiumComponent implements OnInit {
|
||||
private environmentService: EnvironmentService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
private toastService: ToastService,
|
||||
private taxService: TaxServiceAbstraction,
|
||||
) {
|
||||
this.selfHosted = platformUtilsService.isSelfHost();
|
||||
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
|
||||
|
||||
this.addonForm.controls.additionalStorage.valueChanges
|
||||
.pipe(debounceTime(1000), takeUntilDestroyed())
|
||||
.subscribe(() => {
|
||||
this.refreshSalesTax();
|
||||
});
|
||||
}
|
||||
protected setSelectedFile(event: Event) {
|
||||
const fileInputEl = <HTMLInputElement>event.target;
|
||||
@@ -154,12 +168,42 @@ export class PremiumComponent implements OnInit {
|
||||
}
|
||||
|
||||
get taxCharges(): number {
|
||||
return this.taxInfoComponent != null && this.taxInfoComponent.taxRate != null
|
||||
? (this.taxInfoComponent.taxRate / 100) * this.subtotal
|
||||
: 0;
|
||||
return this.estimatedTax;
|
||||
}
|
||||
|
||||
get total(): number {
|
||||
return this.subtotal + this.taxCharges || 0;
|
||||
}
|
||||
|
||||
private refreshSalesTax(): void {
|
||||
if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) {
|
||||
return;
|
||||
}
|
||||
const request: PreviewIndividualInvoiceRequest = {
|
||||
passwordManager: {
|
||||
additionalStorage: this.addonForm.value.additionalStorage,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: this.taxInfoComponent.postalCode,
|
||||
country: this.taxInfoComponent.country,
|
||||
},
|
||||
};
|
||||
|
||||
this.taxService
|
||||
.previewIndividualInvoice(request)
|
||||
.then((invoice) => {
|
||||
this.estimatedTax = invoice.taxAmount;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastService.showToast({
|
||||
title: "",
|
||||
variant: "error",
|
||||
message: this.i18nService.t(error.message),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected onTaxInformationChanged(): void {
|
||||
this.refreshSalesTax();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data";
|
||||
import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
@@ -34,6 +36,7 @@ export class AdjustSubscription implements OnInit, OnDestroy {
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private formBuilder: FormBuilder,
|
||||
private toastService: ToastService,
|
||||
private internalOrganizationService: InternalOrganizationServiceAbstraction,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -64,7 +67,20 @@ export class AdjustSubscription implements OnInit, OnDestroy {
|
||||
this.additionalSeatCount,
|
||||
this.adjustSubscriptionForm.value.newMaxSeats,
|
||||
);
|
||||
await this.organizationApiService.updatePasswordManagerSeats(this.organizationId, request);
|
||||
|
||||
const response = await this.organizationApiService.updatePasswordManagerSeats(
|
||||
this.organizationId,
|
||||
request,
|
||||
);
|
||||
|
||||
const organization = await this.internalOrganizationService.get(this.organizationId);
|
||||
|
||||
const organizationData = new OrganizationData(response, {
|
||||
isMember: organization.isMember,
|
||||
isProviderUser: organization.isProviderUser,
|
||||
});
|
||||
|
||||
await this.internalOrganizationService.upsert(organizationData);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
|
||||
@@ -344,25 +344,14 @@
|
||||
</span>
|
||||
<a></a>
|
||||
</p>
|
||||
<app-payment
|
||||
*ngIf="
|
||||
(upgradeRequiresPaymentMethod || showPayment || isPaymentSourceEmpty()) &&
|
||||
!deprecateStripeSourcesAPI
|
||||
"
|
||||
[hideCredit]="true"
|
||||
></app-payment>
|
||||
<app-payment-v2
|
||||
*ngIf="
|
||||
(upgradeRequiresPaymentMethod || showPayment || isPaymentSourceEmpty()) &&
|
||||
deprecateStripeSourcesAPI
|
||||
"
|
||||
[showAccountCredit]="false"
|
||||
>
|
||||
</app-payment-v2>
|
||||
<app-tax-info
|
||||
*ngIf="showPayment || upgradeRequiresPaymentMethod || isPaymentSourceEmpty()"
|
||||
(onCountryChanged)="changedCountry()"
|
||||
></app-tax-info>
|
||||
<ng-container *ngIf="canUpdatePaymentInformation()">
|
||||
<app-payment *ngIf="!deprecateStripeSourcesAPI" [hideCredit]="true" />
|
||||
<app-payment-v2 *ngIf="deprecateStripeSourcesAPI" [showAccountCredit]="false" />
|
||||
<app-manage-tax-information
|
||||
[startWith]="taxInformation"
|
||||
(taxInformationChanged)="taxInformationChanged($event)"
|
||||
></app-manage-tax-information>
|
||||
</ng-container>
|
||||
<div id="price" class="tw-mt-4">
|
||||
<p class="tw-text-lg tw-mb-1">
|
||||
<span class="tw-font-semibold"
|
||||
@@ -947,6 +936,20 @@
|
||||
</p>
|
||||
</bit-hint>
|
||||
</div>
|
||||
<div *ngIf="totalOpened" class="row tw-mt-4">
|
||||
<bit-hint class="col-6">
|
||||
<p
|
||||
class="tw-flex tw-justify-between tw-border-0 tw-border-solid tw-border-t tw-border-secondary-300 tw-pt-2 tw-mb-0"
|
||||
>
|
||||
<span class="tw-font-semibold">
|
||||
{{ "estimatedTax" | i18n }}
|
||||
</span>
|
||||
<span>
|
||||
{{ estimatedTax | currency: "USD" : "$" }}
|
||||
</span>
|
||||
</p>
|
||||
</bit-hint>
|
||||
</div>
|
||||
<div *ngIf="totalOpened" id="price" class="row tw-mt-4">
|
||||
<bit-hint class="col-6">
|
||||
<p
|
||||
|
||||
@@ -15,6 +15,7 @@ import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.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";
|
||||
@@ -24,13 +25,17 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||
import {
|
||||
PaymentMethodType,
|
||||
PlanInterval,
|
||||
PlanType,
|
||||
ProductTierType,
|
||||
} from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
|
||||
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
|
||||
import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
@@ -46,7 +51,6 @@ import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { PaymentV2Component } from "../shared/payment/payment-v2.component";
|
||||
import { PaymentComponent } from "../shared/payment/payment.component";
|
||||
import { TaxInfoComponent } from "../shared/tax-info.component";
|
||||
|
||||
type ChangePlanDialogParams = {
|
||||
organizationId: string;
|
||||
@@ -89,13 +93,12 @@ interface OnSuccessArgs {
|
||||
export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
|
||||
@ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component;
|
||||
@ViewChild(TaxInfoComponent) taxComponent: TaxInfoComponent;
|
||||
@ViewChild(ManageTaxInformationComponent) taxComponent: ManageTaxInformationComponent;
|
||||
|
||||
@Input() acceptingSponsorship = false;
|
||||
@Input() organizationId: string;
|
||||
@Input() showFree = false;
|
||||
@Input() showCancel = false;
|
||||
selectedFile: File;
|
||||
|
||||
@Input()
|
||||
get productTier(): ProductTierType {
|
||||
@@ -107,6 +110,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
this.formGroup?.controls?.productTier?.setValue(product);
|
||||
}
|
||||
|
||||
protected estimatedTax: number = 0;
|
||||
private _productTier = ProductTierType.Free;
|
||||
|
||||
@Input()
|
||||
@@ -173,6 +177,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
protected taxInformation: TaxInformation;
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) private dialogParams: ChangePlanDialogParams,
|
||||
private dialogRef: DialogRef<ChangePlanDialogResultType>,
|
||||
@@ -189,6 +195,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private taxService: TaxServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
@@ -267,6 +274,11 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.setInitialPlanSelection();
|
||||
this.loading = false;
|
||||
|
||||
const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId);
|
||||
this.taxInformation = TaxInformation.from(taxInfo);
|
||||
|
||||
this.refreshSalesTax();
|
||||
}
|
||||
|
||||
setInitialPlanSelection() {
|
||||
@@ -402,6 +414,12 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
this.selectedPlan = plan;
|
||||
this.formGroup.patchValue({ productTier: plan.productTier });
|
||||
|
||||
try {
|
||||
this.refreshSalesTax();
|
||||
} catch {
|
||||
this.estimatedTax = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -567,12 +585,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
get taxCharges() {
|
||||
return this.taxComponent != null && this.taxComponent.taxRate != null
|
||||
? (this.taxComponent.taxRate / 100) * this.passwordManagerSubtotal
|
||||
: 0;
|
||||
}
|
||||
|
||||
get passwordManagerSeats() {
|
||||
if (this.selectedPlan.productTier === ProductTierType.Families) {
|
||||
return this.selectedPlan.PasswordManager.baseSeats;
|
||||
@@ -584,15 +596,15 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
if (this.organization.useSecretsManager) {
|
||||
return (
|
||||
this.passwordManagerSubtotal +
|
||||
this.additionalStorageTotal(this.selectedPlan) +
|
||||
this.secretsManagerSubtotal +
|
||||
this.taxCharges || 0
|
||||
this.additionalStorageTotal(this.selectedPlan) +
|
||||
this.secretsManagerSubtotal +
|
||||
this.estimatedTax
|
||||
);
|
||||
}
|
||||
return (
|
||||
this.passwordManagerSubtotal +
|
||||
this.additionalStorageTotal(this.selectedPlan) +
|
||||
this.taxCharges || 0
|
||||
this.additionalStorageTotal(this.selectedPlan) +
|
||||
this.estimatedTax
|
||||
);
|
||||
}
|
||||
|
||||
@@ -645,8 +657,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
changedCountry() {
|
||||
if (this.deprecateStripeSourcesAPI && this.paymentV2Component && this.taxComponent) {
|
||||
this.paymentV2Component.showBankAccount = this.taxComponent.country === "US";
|
||||
if (this.deprecateStripeSourcesAPI && this.paymentV2Component) {
|
||||
this.paymentV2Component.showBankAccount = this.taxInformation.country === "US";
|
||||
|
||||
if (
|
||||
!this.paymentV2Component.showBankAccount &&
|
||||
@@ -654,8 +666,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
) {
|
||||
this.paymentV2Component.select(PaymentMethodType.Card);
|
||||
}
|
||||
} else if (this.paymentComponent && this.taxComponent) {
|
||||
this.paymentComponent!.hideBank = this.taxComponent?.taxFormGroup?.value.country !== "US";
|
||||
} else if (this.paymentComponent && this.taxInformation) {
|
||||
this.paymentComponent!.hideBank = this.taxInformation.country !== "US";
|
||||
// Bank Account payments are only available for US customers
|
||||
if (
|
||||
this.paymentComponent.hideBank &&
|
||||
@@ -667,9 +679,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
protected taxInformationChanged(event: TaxInformation): void {
|
||||
this.taxInformation = event;
|
||||
this.changedCountry();
|
||||
this.refreshSalesTax();
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
if (!this.taxComponent?.taxFormGroup.valid && this.taxComponent?.taxFormGroup.touched) {
|
||||
this.taxComponent?.taxFormGroup.markAllAsTouched();
|
||||
if (this.taxComponent !== undefined && !this.taxComponent.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -723,8 +740,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
this.formGroup.controls.premiumAccessAddon.value;
|
||||
request.planType = this.selectedPlan.type;
|
||||
if (this.showPayment) {
|
||||
request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country;
|
||||
request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode;
|
||||
request.billingAddressCountry = this.taxInformation.country;
|
||||
request.billingAddressPostalCode = this.taxInformation.postalCode;
|
||||
}
|
||||
|
||||
// Secrets Manager
|
||||
@@ -735,15 +752,9 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
const tokenizedPaymentSource = await this.paymentV2Component.tokenize();
|
||||
const updatePaymentMethodRequest = new UpdatePaymentMethodRequest();
|
||||
updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource;
|
||||
updatePaymentMethodRequest.taxInformation = {
|
||||
country: this.taxComponent.country,
|
||||
postalCode: this.taxComponent.postalCode,
|
||||
taxId: this.taxComponent.taxId,
|
||||
line1: this.taxComponent.line1,
|
||||
line2: this.taxComponent.line2,
|
||||
city: this.taxComponent.city,
|
||||
state: this.taxComponent.state,
|
||||
};
|
||||
updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From(
|
||||
this.taxInformation,
|
||||
);
|
||||
|
||||
await this.billingApiService.updateOrganizationPaymentMethod(
|
||||
this.organizationId,
|
||||
@@ -754,8 +765,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
const paymentRequest = new PaymentRequest();
|
||||
paymentRequest.paymentToken = tokenResult[0];
|
||||
paymentRequest.paymentMethodType = tokenResult[1];
|
||||
paymentRequest.country = this.taxComponent.taxFormGroup?.value.country;
|
||||
paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode;
|
||||
paymentRequest.country = this.taxInformation.country;
|
||||
paymentRequest.postalCode = this.taxInformation.postalCode;
|
||||
await this.organizationApiService.updatePayment(this.organizationId, paymentRequest);
|
||||
}
|
||||
}
|
||||
@@ -944,4 +955,48 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
|
||||
manageSelectableProduct(index: number) {
|
||||
return index;
|
||||
}
|
||||
|
||||
private refreshSalesTax(): void {
|
||||
if (!this.taxInformation.country || !this.taxInformation.postalCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request: PreviewOrganizationInvoiceRequest = {
|
||||
organizationId: this.organizationId,
|
||||
passwordManager: {
|
||||
additionalStorage: 0,
|
||||
plan: this.selectedPlan?.type,
|
||||
seats: this.sub.seats,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: this.taxInformation.postalCode,
|
||||
country: this.taxInformation.country,
|
||||
taxId: this.taxInformation.taxId,
|
||||
},
|
||||
};
|
||||
|
||||
if (this.organization.useSecretsManager) {
|
||||
request.secretsManager = {
|
||||
seats: this.sub.smSeats,
|
||||
additionalMachineAccounts: this.sub.smServiceAccounts,
|
||||
};
|
||||
}
|
||||
|
||||
this.taxService
|
||||
.previewOrganizationInvoice(request)
|
||||
.then((invoice) => {
|
||||
this.estimatedTax = invoice.taxAmount;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastService.showToast({
|
||||
title: "",
|
||||
variant: "error",
|
||||
message: this.i18nService.t(error.message),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected canUpdatePaymentInformation(): boolean {
|
||||
return this.upgradeRequiresPaymentMethod || this.showPayment || this.isPaymentSourceEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@
|
||||
>{{ "additionalUsers" | i18n }}:</span
|
||||
>
|
||||
<span *ngIf="!selectablePlan.PasswordManager.baseSeats">{{ "users" | i18n }}:</span>
|
||||
{{ formGroup.controls["additionalSeats"].value || 0 }} ×
|
||||
{{ formGroup.controls.additionalSeats.value || 0 }} ×
|
||||
{{
|
||||
(selectablePlan.isAnnual
|
||||
? selectablePlan.PasswordManager.seatPrice / 12
|
||||
@@ -355,7 +355,7 @@
|
||||
*ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption"
|
||||
>
|
||||
{{ "additionalStorageGb" | i18n }}:
|
||||
{{ formGroup.controls["additionalStorage"].value || 0 }} ×
|
||||
{{ formGroup.controls.additionalStorage.value || 0 }} ×
|
||||
{{
|
||||
(selectablePlan.isAnnual
|
||||
? selectablePlan.PasswordManager.additionalStoragePricePerGb / 12
|
||||
@@ -388,7 +388,7 @@
|
||||
>{{ "additionalUsers" | i18n }}:</span
|
||||
>
|
||||
<span *ngIf="!selectablePlan.PasswordManager.baseSeats">{{ "users" | i18n }}:</span>
|
||||
{{ formGroup.controls["additionalSeats"].value || 0 }} ×
|
||||
{{ formGroup.controls.additionalSeats.value || 0 }} ×
|
||||
{{ selectablePlan.PasswordManager.seatPrice | currency: "$" }}
|
||||
{{ "monthAbbr" | i18n }} =
|
||||
{{
|
||||
@@ -403,7 +403,7 @@
|
||||
*ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption"
|
||||
>
|
||||
{{ "additionalStorageGb" | i18n }}:
|
||||
{{ formGroup.controls["additionalStorage"].value || 0 }} ×
|
||||
{{ formGroup.controls.additionalStorage.value || 0 }} ×
|
||||
{{ selectablePlan.PasswordManager.additionalStoragePricePerGb | currency: "$" }}
|
||||
{{ "monthAbbr" | i18n }} =
|
||||
{{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }}
|
||||
@@ -440,7 +440,12 @@
|
||||
<app-payment-v2
|
||||
*ngIf="deprecateStripeSourcesAPI && (createOrganization || upgradeRequiresPaymentMethod)"
|
||||
></app-payment-v2>
|
||||
<app-tax-info (onCountryChanged)="changedCountry()"></app-tax-info>
|
||||
<app-manage-tax-information
|
||||
class="tw-my-4"
|
||||
[showTaxIdField]="showTaxIdField"
|
||||
[startWith]="taxInformation"
|
||||
(taxInformationChanged)="onTaxInformationChanged($event)"
|
||||
/>
|
||||
<div id="price" class="tw-my-4">
|
||||
<div class="tw-text-muted tw-text-base">
|
||||
{{ "passwordManagerPlanPrice" | i18n }}: {{ passwordManagerSubtotal | currency: "USD $" }}
|
||||
@@ -450,7 +455,7 @@
|
||||
<br />
|
||||
</span>
|
||||
<ng-container>
|
||||
{{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }}
|
||||
{{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<hr class="tw-my-1 tw-grid tw-grid-cols-3 tw-ml-0" />
|
||||
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { debounceTime } from "rxjs/operators";
|
||||
|
||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.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";
|
||||
@@ -26,9 +28,12 @@ import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/mode
|
||||
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
|
||||
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction";
|
||||
import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request";
|
||||
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
|
||||
import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
@@ -50,7 +55,6 @@ import { OrganizationCreateModule } from "../../admin-console/organizations/crea
|
||||
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
|
||||
import { PaymentV2Component } from "../shared/payment/payment-v2.component";
|
||||
import { PaymentComponent } from "../shared/payment/payment.component";
|
||||
import { TaxInfoComponent } from "../shared/tax-info.component";
|
||||
|
||||
interface OnSuccessArgs {
|
||||
organizationId: string;
|
||||
@@ -72,13 +76,14 @@ const Allowed2020PlansForLegacyProviders = [
|
||||
export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
|
||||
@ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component;
|
||||
@ViewChild(TaxInfoComponent) taxComponent: TaxInfoComponent;
|
||||
@ViewChild(ManageTaxInformationComponent) taxComponent: ManageTaxInformationComponent;
|
||||
|
||||
@Input() organizationId: string;
|
||||
@Input() organizationId?: string;
|
||||
@Input() showFree = true;
|
||||
@Input() showCancel = false;
|
||||
@Input() acceptingSponsorship = false;
|
||||
@Input() currentPlan: PlanResponse;
|
||||
|
||||
selectedFile: File;
|
||||
|
||||
@Input()
|
||||
@@ -93,6 +98,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
|
||||
private _productTier = ProductTierType.Free;
|
||||
|
||||
protected taxInformation: TaxInformation;
|
||||
|
||||
@Input()
|
||||
get plan(): PlanType {
|
||||
return this._plan;
|
||||
@@ -149,7 +156,10 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
billing: BillingResponse;
|
||||
provider: ProviderResponse;
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
protected estimatedTax: number = 0;
|
||||
protected total: number = 0;
|
||||
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
@@ -168,6 +178,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
private toastService: ToastService,
|
||||
private configService: ConfigService,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private taxService: TaxServiceAbstraction,
|
||||
) {
|
||||
this.selfHosted = this.platformUtilsService.isSelfHost();
|
||||
}
|
||||
@@ -181,6 +192,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.organization = await this.organizationService.get(this.organizationId);
|
||||
this.billing = await this.organizationApiService.getBilling(this.organizationId);
|
||||
this.sub = await this.organizationApiService.getSubscription(this.organizationId);
|
||||
this.taxInformation = await this.organizationApiService.getTaxInfo(this.organizationId);
|
||||
} else {
|
||||
this.taxInformation = await this.apiService.getTaxInfo();
|
||||
}
|
||||
|
||||
if (!this.selfHosted) {
|
||||
@@ -241,6 +255,16 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
|
||||
this.formGroup.valueChanges.pipe(debounceTime(1000), takeUntil(this.destroy$)).subscribe(() => {
|
||||
this.refreshSalesTax();
|
||||
});
|
||||
|
||||
this.secretsManagerForm.valueChanges
|
||||
.pipe(debounceTime(1000), takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this.refreshSalesTax();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -438,17 +462,6 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
return this.selectedPlan.trialPeriodDays != null;
|
||||
}
|
||||
|
||||
get taxCharges() {
|
||||
return this.taxComponent != null && this.taxComponent.taxRate != null
|
||||
? (this.taxComponent.taxRate / 100) *
|
||||
(this.passwordManagerSubtotal + this.secretsManagerSubtotal)
|
||||
: 0;
|
||||
}
|
||||
|
||||
get total() {
|
||||
return this.passwordManagerSubtotal + this.secretsManagerSubtotal + this.taxCharges || 0;
|
||||
}
|
||||
|
||||
get paymentDesc() {
|
||||
if (this.acceptingSponsorship) {
|
||||
return this.i18nService.t("paymentSponsored");
|
||||
@@ -554,9 +567,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.changedProduct();
|
||||
}
|
||||
|
||||
changedCountry() {
|
||||
protected changedCountry(): void {
|
||||
if (this.deprecateStripeSourcesAPI) {
|
||||
this.paymentV2Component.showBankAccount = this.taxComponent.country === "US";
|
||||
this.paymentV2Component.showBankAccount = this.taxInformation?.country === "US";
|
||||
if (
|
||||
!this.paymentV2Component.showBankAccount &&
|
||||
this.paymentV2Component.selected === PaymentMethodType.BankAccount
|
||||
@@ -564,7 +577,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.paymentV2Component.select(PaymentMethodType.Card);
|
||||
}
|
||||
} else {
|
||||
this.paymentComponent.hideBank = this.taxComponent.taxFormGroup?.value.country !== "US";
|
||||
this.paymentComponent.hideBank = this.taxInformation?.country !== "US";
|
||||
if (
|
||||
this.paymentComponent.hideBank &&
|
||||
this.paymentComponent.method === PaymentMethodType.BankAccount
|
||||
@@ -575,28 +588,31 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
cancel() {
|
||||
protected onTaxInformationChanged(event: TaxInformation): void {
|
||||
this.taxInformation = event;
|
||||
this.changedCountry();
|
||||
this.refreshSalesTax();
|
||||
}
|
||||
|
||||
protected cancel(): void {
|
||||
this.onCanceled.emit();
|
||||
}
|
||||
|
||||
setSelectedFile(event: Event) {
|
||||
protected setSelectedFile(event: Event): void {
|
||||
const fileInputEl = <HTMLInputElement>event.target;
|
||||
this.selectedFile = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null;
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
if (this.taxComponent) {
|
||||
if (!this.taxComponent?.taxFormGroup.valid) {
|
||||
this.taxComponent?.taxFormGroup.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
if (this.taxComponent && !this.taxComponent.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.singleOrgPolicyBlock) {
|
||||
return;
|
||||
}
|
||||
const doSubmit = async (): Promise<string> => {
|
||||
let orgId: string = null;
|
||||
let orgId: string;
|
||||
if (this.createOrganization) {
|
||||
const orgKey = await this.keyService.makeOrgKey<OrgKey>();
|
||||
const key = orgKey[0].encryptedString;
|
||||
@@ -607,11 +623,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
const collectionCt = collection.encryptedString;
|
||||
const orgKeys = await this.keyService.makeKeyPair(orgKey[1]);
|
||||
|
||||
if (this.selfHosted) {
|
||||
orgId = await this.createSelfHosted(key, collectionCt, orgKeys);
|
||||
} else {
|
||||
orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]);
|
||||
}
|
||||
orgId = this.selfHosted
|
||||
? await this.createSelfHosted(key, collectionCt, orgKeys)
|
||||
: await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
@@ -619,7 +633,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
message: this.i18nService.t("organizationReadyToGo"),
|
||||
});
|
||||
} else {
|
||||
orgId = await this.updateOrganization(orgId);
|
||||
orgId = await this.updateOrganization();
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
@@ -653,7 +667,63 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.messagingService.send("organizationCreated", { organizationId });
|
||||
};
|
||||
|
||||
private async updateOrganization(orgId: string) {
|
||||
protected get showTaxIdField(): boolean {
|
||||
switch (this.formGroup.controls.productTier.value) {
|
||||
case ProductTierType.Free:
|
||||
case ProductTierType.Families:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private refreshSalesTax(): void {
|
||||
if (this.formGroup.controls.plan.value == PlanType.Free) {
|
||||
this.estimatedTax = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.taxComponent.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request: PreviewOrganizationInvoiceRequest = {
|
||||
organizationId: this.organizationId,
|
||||
passwordManager: {
|
||||
additionalStorage: this.formGroup.controls.additionalStorage.value,
|
||||
plan: this.formGroup.controls.plan.value,
|
||||
seats: this.formGroup.controls.additionalSeats.value,
|
||||
},
|
||||
taxInformation: {
|
||||
postalCode: this.taxInformation.postalCode,
|
||||
country: this.taxInformation.country,
|
||||
taxId: this.taxInformation.taxId,
|
||||
},
|
||||
};
|
||||
|
||||
if (this.secretsManagerForm.controls.enabled.value === true) {
|
||||
request.secretsManager = {
|
||||
seats: this.secretsManagerForm.controls.userSeats.value,
|
||||
additionalMachineAccounts: this.secretsManagerForm.controls.additionalServiceAccounts.value,
|
||||
};
|
||||
}
|
||||
|
||||
this.taxService
|
||||
.previewOrganizationInvoice(request)
|
||||
.then((invoice) => {
|
||||
this.estimatedTax = invoice.taxAmount;
|
||||
this.total = invoice.totalAmount;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastService.showToast({
|
||||
title: "",
|
||||
variant: "error",
|
||||
message: this.i18nService.t(error.message),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async updateOrganization() {
|
||||
const request = new OrganizationUpgradeRequest();
|
||||
request.additionalSeats = this.formGroup.controls.additionalSeats.value;
|
||||
request.additionalStorageGb = this.formGroup.controls.additionalStorage.value;
|
||||
@@ -661,8 +731,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.selectedPlan.PasswordManager.hasPremiumAccessOption &&
|
||||
this.formGroup.controls.premiumAccessAddon.value;
|
||||
request.planType = this.selectedPlan.type;
|
||||
request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country;
|
||||
request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode;
|
||||
request.billingAddressCountry = this.taxInformation?.country;
|
||||
request.billingAddressPostalCode = this.taxInformation?.postalCode;
|
||||
|
||||
// Secrets Manager
|
||||
this.buildSecretsManagerRequest(request);
|
||||
@@ -671,10 +741,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
if (this.deprecateStripeSourcesAPI) {
|
||||
const updatePaymentMethodRequest = new UpdatePaymentMethodRequest();
|
||||
updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize();
|
||||
const expandedTaxInfoUpdateRequest = new ExpandedTaxInfoUpdateRequest();
|
||||
expandedTaxInfoUpdateRequest.country = this.taxComponent.country;
|
||||
expandedTaxInfoUpdateRequest.postalCode = this.taxComponent.postalCode;
|
||||
updatePaymentMethodRequest.taxInformation = expandedTaxInfoUpdateRequest;
|
||||
updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From(
|
||||
this.taxInformation,
|
||||
);
|
||||
await this.billingApiService.updateOrganizationPaymentMethod(
|
||||
this.organizationId,
|
||||
updatePaymentMethodRequest,
|
||||
@@ -684,8 +753,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
const paymentRequest = new PaymentRequest();
|
||||
paymentRequest.paymentToken = paymentToken;
|
||||
paymentRequest.paymentMethodType = paymentMethodType;
|
||||
paymentRequest.country = this.taxComponent.taxFormGroup?.value.country;
|
||||
paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode;
|
||||
paymentRequest.country = this.taxInformation?.country;
|
||||
paymentRequest.postalCode = this.taxInformation?.postalCode;
|
||||
await this.organizationApiService.updatePayment(this.organizationId, paymentRequest);
|
||||
}
|
||||
}
|
||||
@@ -709,7 +778,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
collectionCt: string,
|
||||
orgKeys: [string, EncString],
|
||||
orgKey: SymmetricCryptoKey,
|
||||
) {
|
||||
): Promise<string> {
|
||||
const request = new OrganizationCreateRequest();
|
||||
request.key = key;
|
||||
request.collectionName = collectionCt;
|
||||
@@ -738,15 +807,13 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
this.selectedPlan.PasswordManager.hasPremiumAccessOption &&
|
||||
this.formGroup.controls.premiumAccessAddon.value;
|
||||
request.planType = this.selectedPlan.type;
|
||||
request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode;
|
||||
request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country;
|
||||
if (this.taxComponent.taxFormGroup?.value.includeTaxId) {
|
||||
request.taxIdNumber = this.taxComponent.taxFormGroup?.value.taxId;
|
||||
request.billingAddressLine1 = this.taxComponent.taxFormGroup?.value.line1;
|
||||
request.billingAddressLine2 = this.taxComponent.taxFormGroup?.value.line2;
|
||||
request.billingAddressCity = this.taxComponent.taxFormGroup?.value.city;
|
||||
request.billingAddressState = this.taxComponent.taxFormGroup?.value.state;
|
||||
}
|
||||
request.billingAddressPostalCode = this.taxInformation?.postalCode;
|
||||
request.billingAddressCountry = this.taxInformation?.country;
|
||||
request.taxIdNumber = this.taxInformation?.taxId;
|
||||
request.billingAddressLine1 = this.taxInformation?.line1;
|
||||
request.billingAddressLine2 = this.taxInformation?.line2;
|
||||
request.billingAddressCity = this.taxInformation?.city;
|
||||
request.billingAddressState = this.taxInformation?.state;
|
||||
}
|
||||
|
||||
// Secrets Manager
|
||||
|
||||
@@ -63,20 +63,5 @@
|
||||
{{ "paymentChargedWithUnpaidSubscription" | i18n }}
|
||||
</p>
|
||||
</bit-section>
|
||||
<!-- Tax Information -->
|
||||
<bit-section>
|
||||
<h2 bitTypography="h2">{{ "taxInformation" | i18n }}</h2>
|
||||
<p bitTypography="body1">{{ "taxInformationDesc" | i18n }}</p>
|
||||
<app-tax-info></app-tax-info>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
bitFormButton
|
||||
buttonType="primary"
|
||||
[bitAction]="updateTaxInformation"
|
||||
>
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
</bit-section>
|
||||
</ng-container>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Location } from "@angular/common";
|
||||
import { Component, OnDestroy, ViewChild } from "@angular/core";
|
||||
import { Component, OnDestroy } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { from, lastValueFrom, switchMap } from "rxjs";
|
||||
@@ -11,7 +11,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
|
||||
@@ -22,7 +21,6 @@ import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { FreeTrial } from "../../../core/types/free-trial";
|
||||
import { TrialFlowService } from "../../services/trial-flow.service";
|
||||
import { TaxInfoComponent } from "../../shared";
|
||||
import {
|
||||
AddCreditDialogResult,
|
||||
openAddCreditDialog,
|
||||
@@ -36,8 +34,6 @@ import {
|
||||
templateUrl: "./organization-payment-method.component.html",
|
||||
})
|
||||
export class OrganizationPaymentMethodComponent implements OnDestroy {
|
||||
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
|
||||
|
||||
organizationId: string;
|
||||
isUnpaid = false;
|
||||
accountCredit: number;
|
||||
@@ -155,6 +151,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
|
||||
data: {
|
||||
initialPaymentMethod: this.paymentSource?.type,
|
||||
organizationId: this.organizationId,
|
||||
productTier: this.organization?.productTierType,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -170,6 +167,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
|
||||
data: {
|
||||
initialPaymentMethod: this.paymentSource?.type,
|
||||
organizationId: this.organizationId,
|
||||
productTier: this.organization?.productTierType,
|
||||
},
|
||||
});
|
||||
const result = await lastValueFrom(dialogRef.closed);
|
||||
@@ -183,32 +181,6 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
|
||||
}
|
||||
};
|
||||
|
||||
protected updateTaxInformation = async (): Promise<void> => {
|
||||
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
|
||||
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
|
||||
|
||||
if (this.taxInfoComponent.taxFormGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = new ExpandedTaxInfoUpdateRequest();
|
||||
request.country = this.taxInfoComponent.country;
|
||||
request.postalCode = this.taxInfoComponent.postalCode;
|
||||
request.taxId = this.taxInfoComponent.taxId;
|
||||
request.line1 = this.taxInfoComponent.line1;
|
||||
request.line2 = this.taxInfoComponent.line2;
|
||||
request.city = this.taxInfoComponent.city;
|
||||
request.state = this.taxInfoComponent.state;
|
||||
|
||||
await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("taxInfoUpdated"),
|
||||
});
|
||||
};
|
||||
|
||||
protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise<void> => {
|
||||
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data";
|
||||
import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -104,6 +106,7 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private toastService: ToastService,
|
||||
private internalOrganizationService: InternalOrganizationServiceAbstraction,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -157,11 +160,20 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest
|
||||
? this.formGroup.value.maxAutoscaleServiceAccounts
|
||||
: null;
|
||||
|
||||
await this.organizationApiService.updateSecretsManagerSubscription(
|
||||
const response = await this.organizationApiService.updateSecretsManagerSubscription(
|
||||
this.organizationId,
|
||||
request,
|
||||
);
|
||||
|
||||
const organization = await this.internalOrganizationService.get(this.organizationId);
|
||||
|
||||
const organizationData = new OrganizationData(response, {
|
||||
isMember: organization.isMember,
|
||||
isProviderUser: organization.isProviderUser,
|
||||
});
|
||||
|
||||
await this.internalOrganizationService.upsert(organizationData);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
|
||||
142
apps/web/src/app/billing/services/reseller-warning.service.ts
Normal file
142
apps/web/src/app/billing/services/reseller-warning.service.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
export interface ResellerWarning {
|
||||
type: "info" | "warning";
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class ResellerWarningService {
|
||||
private readonly RENEWAL_WARNING_DAYS = 14;
|
||||
private readonly GRACE_PERIOD_DAYS = 30;
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
getWarning(
|
||||
organization: Organization,
|
||||
organizationBillingMetadata: OrganizationBillingMetadataResponse,
|
||||
): ResellerWarning | null {
|
||||
if (!organization.hasReseller) {
|
||||
return null; // If no reseller, return null immediately
|
||||
}
|
||||
|
||||
// Check for past due warning first (highest priority)
|
||||
if (this.shouldShowPastDueWarning(organizationBillingMetadata)) {
|
||||
const gracePeriodEnd = this.getGracePeriodEndDate(organizationBillingMetadata.invoiceDueDate);
|
||||
if (!gracePeriodEnd) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "warning",
|
||||
message: this.i18nService.t(
|
||||
"resellerPastDueWarning",
|
||||
organization.providerName,
|
||||
this.formatDate(gracePeriodEnd),
|
||||
),
|
||||
} as ResellerWarning;
|
||||
}
|
||||
|
||||
// Check for open invoice warning
|
||||
if (this.shouldShowInvoiceWarning(organizationBillingMetadata)) {
|
||||
const invoiceCreatedDate = organizationBillingMetadata.invoiceCreatedDate;
|
||||
const invoiceDueDate = organizationBillingMetadata.invoiceDueDate;
|
||||
if (!invoiceCreatedDate || !invoiceDueDate) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "info",
|
||||
message: this.i18nService.t(
|
||||
"resellerOpenInvoiceWarning",
|
||||
organization.providerName,
|
||||
this.formatDate(organizationBillingMetadata.invoiceCreatedDate),
|
||||
this.formatDate(organizationBillingMetadata.invoiceDueDate),
|
||||
),
|
||||
} as ResellerWarning;
|
||||
}
|
||||
|
||||
// Check for renewal warning
|
||||
if (this.shouldShowRenewalWarning(organizationBillingMetadata)) {
|
||||
const subPeriodEndDate = organizationBillingMetadata.subPeriodEndDate;
|
||||
if (!subPeriodEndDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "info",
|
||||
message: this.i18nService.t(
|
||||
"resellerRenewalWarning",
|
||||
organization.providerName,
|
||||
this.formatDate(organizationBillingMetadata.subPeriodEndDate),
|
||||
),
|
||||
} as ResellerWarning;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private shouldShowRenewalWarning(
|
||||
organizationBillingMetadata: OrganizationBillingMetadataResponse,
|
||||
): boolean {
|
||||
if (
|
||||
!organizationBillingMetadata.hasSubscription ||
|
||||
!organizationBillingMetadata.subPeriodEndDate
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const renewalDate = new Date(organizationBillingMetadata.subPeriodEndDate);
|
||||
const daysUntilRenewal = Math.ceil(
|
||||
(renewalDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24),
|
||||
);
|
||||
return daysUntilRenewal <= this.RENEWAL_WARNING_DAYS;
|
||||
}
|
||||
|
||||
private shouldShowInvoiceWarning(
|
||||
organizationBillingMetadata: OrganizationBillingMetadataResponse,
|
||||
): boolean {
|
||||
if (
|
||||
!organizationBillingMetadata.hasOpenInvoice ||
|
||||
!organizationBillingMetadata.invoiceDueDate
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate);
|
||||
return invoiceDueDate > new Date();
|
||||
}
|
||||
|
||||
private shouldShowPastDueWarning(
|
||||
organizationBillingMetadata: OrganizationBillingMetadataResponse,
|
||||
): boolean {
|
||||
if (
|
||||
!organizationBillingMetadata.hasOpenInvoice ||
|
||||
!organizationBillingMetadata.invoiceDueDate
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate);
|
||||
return invoiceDueDate <= new Date() && !organizationBillingMetadata.isSubscriptionUnpaid;
|
||||
}
|
||||
|
||||
private getGracePeriodEndDate(dueDate: Date | null): Date | null {
|
||||
if (!dueDate) {
|
||||
return null;
|
||||
}
|
||||
const gracePeriodEnd = new Date(dueDate);
|
||||
gracePeriodEnd.setDate(gracePeriodEnd.getDate() + this.GRACE_PERIOD_DAYS);
|
||||
return gracePeriodEnd;
|
||||
}
|
||||
|
||||
private formatDate(date: Date | null): string {
|
||||
if (!date) {
|
||||
return "N/A";
|
||||
}
|
||||
return new Date(date).toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,12 @@
|
||||
[showBankAccount]="!!organizationId"
|
||||
[initialPaymentMethod]="initialPaymentMethod"
|
||||
></app-payment-v2>
|
||||
<app-tax-info (onCountryChanged)="onCountryChanged()"></app-tax-info>
|
||||
<app-manage-tax-information
|
||||
*ngIf="taxInformation"
|
||||
[showTaxIdField]="showTaxIdField"
|
||||
[startWith]="taxInformation"
|
||||
(taxInformationChanged)="taxInformationChanged($event)"
|
||||
/>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary" [bitAction]="submit">
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { forwardRef, Component, Inject, ViewChild } from "@angular/core";
|
||||
import { Component, forwardRef, Inject, OnInit, ViewChild } from "@angular/core";
|
||||
|
||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { PaymentMethodType, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
|
||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { TaxInfoComponent } from "../";
|
||||
import { PaymentV2Component } from "../payment/payment-v2.component";
|
||||
|
||||
export interface AdjustPaymentDialogV2Params {
|
||||
initialPaymentMethod?: PaymentMethodType;
|
||||
organizationId?: string;
|
||||
productTier?: ProductTierType;
|
||||
}
|
||||
|
||||
export enum AdjustPaymentDialogV2ResultType {
|
||||
@@ -27,9 +32,10 @@ export enum AdjustPaymentDialogV2ResultType {
|
||||
@Component({
|
||||
templateUrl: "./adjust-payment-dialog-v2.component.html",
|
||||
})
|
||||
export class AdjustPaymentDialogV2Component {
|
||||
export class AdjustPaymentDialogV2Component implements OnInit {
|
||||
@ViewChild(PaymentV2Component) paymentComponent: PaymentV2Component;
|
||||
@ViewChild(forwardRef(() => TaxInfoComponent)) taxInfoComponent: TaxInfoComponent;
|
||||
@ViewChild(forwardRef(() => ManageTaxInformationComponent))
|
||||
taxInfoComponent: ManageTaxInformationComponent;
|
||||
|
||||
protected readonly PaymentMethodType = PaymentMethodType;
|
||||
protected readonly ResultType = AdjustPaymentDialogV2ResultType;
|
||||
@@ -37,10 +43,14 @@ export class AdjustPaymentDialogV2Component {
|
||||
protected dialogHeader: string;
|
||||
protected initialPaymentMethod: PaymentMethodType;
|
||||
protected organizationId?: string;
|
||||
protected productTier?: ProductTierType;
|
||||
|
||||
protected taxInformation: TaxInformation;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
@Inject(DIALOG_DATA) protected dialogParams: AdjustPaymentDialogV2Params,
|
||||
private dialogRef: DialogRef<AdjustPaymentDialogV2ResultType>,
|
||||
private i18nService: I18nService,
|
||||
@@ -50,10 +60,34 @@ export class AdjustPaymentDialogV2Component {
|
||||
this.dialogHeader = this.i18nService.t(key);
|
||||
this.initialPaymentMethod = this.dialogParams.initialPaymentMethod ?? PaymentMethodType.Card;
|
||||
this.organizationId = this.dialogParams.organizationId;
|
||||
this.productTier = this.dialogParams.productTier;
|
||||
}
|
||||
|
||||
onCountryChanged = () => {
|
||||
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||
ngOnInit(): void {
|
||||
if (this.organizationId) {
|
||||
this.organizationApiService
|
||||
.getTaxInfo(this.organizationId)
|
||||
.then((response: TaxInfoResponse) => {
|
||||
this.taxInformation = TaxInformation.from(response);
|
||||
})
|
||||
.catch(() => {
|
||||
this.taxInformation = new TaxInformation();
|
||||
});
|
||||
} else {
|
||||
this.apiService
|
||||
.getTaxInfo()
|
||||
.then((response: TaxInfoResponse) => {
|
||||
this.taxInformation = TaxInformation.from(response);
|
||||
})
|
||||
.catch(() => {
|
||||
this.taxInformation = new TaxInformation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
taxInformationChanged(event: TaxInformation) {
|
||||
this.taxInformation = event;
|
||||
if (event.country === "US") {
|
||||
this.paymentComponent.showBankAccount = !!this.organizationId;
|
||||
} else {
|
||||
this.paymentComponent.showBankAccount = false;
|
||||
@@ -61,28 +95,34 @@ export class AdjustPaymentDialogV2Component {
|
||||
this.paymentComponent.select(PaymentMethodType.Card);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
submit = async (): Promise<void> => {
|
||||
this.taxInfoComponent.taxFormGroup.updateValueAndValidity();
|
||||
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
|
||||
if (this.taxInfoComponent.taxFormGroup.invalid) {
|
||||
if (!this.taxInfoComponent.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.organizationId) {
|
||||
await this.updatePremiumUserPaymentMethod();
|
||||
} else {
|
||||
await this.updateOrganizationPaymentMethod();
|
||||
try {
|
||||
if (!this.organizationId) {
|
||||
await this.updatePremiumUserPaymentMethod();
|
||||
} else {
|
||||
await this.updateOrganizationPaymentMethod();
|
||||
}
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("updatedPaymentMethod"),
|
||||
});
|
||||
|
||||
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
|
||||
} catch (error) {
|
||||
this.toastService.showToast({
|
||||
variant: "error",
|
||||
title: null,
|
||||
message: this.i18nService.t(error.message) || error.message,
|
||||
});
|
||||
}
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("updatedPaymentMethod"),
|
||||
});
|
||||
|
||||
this.dialogRef.close(AdjustPaymentDialogV2ResultType.Submitted);
|
||||
};
|
||||
|
||||
private updateOrganizationPaymentMethod = async () => {
|
||||
@@ -90,27 +130,39 @@ export class AdjustPaymentDialogV2Component {
|
||||
|
||||
const request = new UpdatePaymentMethodRequest();
|
||||
request.paymentSource = paymentSource;
|
||||
request.taxInformation = {
|
||||
country: this.taxInfoComponent.country,
|
||||
postalCode: this.taxInfoComponent.postalCode,
|
||||
taxId: this.taxInfoComponent.taxId,
|
||||
line1: this.taxInfoComponent.line1,
|
||||
line2: this.taxInfoComponent.line2,
|
||||
city: this.taxInfoComponent.city,
|
||||
state: this.taxInfoComponent.state,
|
||||
};
|
||||
request.taxInformation = ExpandedTaxInfoUpdateRequest.From(this.taxInformation);
|
||||
|
||||
await this.billingApiService.updateOrganizationPaymentMethod(this.organizationId, request);
|
||||
};
|
||||
|
||||
protected get showTaxIdField(): boolean {
|
||||
if (!this.organizationId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.productTier) {
|
||||
case ProductTierType.Free:
|
||||
case ProductTierType.Families:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private updatePremiumUserPaymentMethod = async () => {
|
||||
const { type, token } = await this.paymentComponent.tokenize();
|
||||
|
||||
const request = new PaymentRequest();
|
||||
request.paymentMethodType = type;
|
||||
request.paymentToken = token;
|
||||
request.country = this.taxInfoComponent.country;
|
||||
request.postalCode = this.taxInfoComponent.postalCode;
|
||||
request.country = this.taxInformation.country;
|
||||
request.postalCode = this.taxInformation.postalCode;
|
||||
request.taxId = this.taxInformation.taxId;
|
||||
request.state = this.taxInformation.state;
|
||||
request.line1 = this.taxInformation.line1;
|
||||
request.line2 = this.taxInformation.line2;
|
||||
request.city = this.taxInformation.city;
|
||||
request.state = this.taxInformation.state;
|
||||
await this.apiService.postAccountPayment(request);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
>
|
||||
<ng-container bitDialogContent>
|
||||
<app-payment [hideBank]="!organizationId" [hideCredit]="true"></app-payment>
|
||||
<app-tax-info (onCountryChanged)="changeCountry()"></app-tax-info>
|
||||
<app-manage-tax-information
|
||||
*ngIf="taxInformation"
|
||||
[showTaxIdField]="showTaxIdField"
|
||||
[startWith]="taxInformation"
|
||||
(taxInformationChanged)="taxInformationChanged($event)"
|
||||
/>
|
||||
</ng-container>
|
||||
<ng-container bitDialogFooter>
|
||||
<button type="submit" bitButton bitFormButton buttonType="primary">
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Component, Inject, ViewChild } from "@angular/core";
|
||||
import { Component, Inject, OnInit, ViewChild } from "@angular/core";
|
||||
import { FormGroup } from "@angular/forms";
|
||||
|
||||
import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { TaxInformation } from "@bitwarden/common/billing/models/domain";
|
||||
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
|
||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
|
||||
import { PaymentComponent } from "../payment/payment.component";
|
||||
import { TaxInfoComponent } from "../tax-info.component";
|
||||
|
||||
export interface AdjustPaymentDialogData {
|
||||
organizationId: string;
|
||||
@@ -28,9 +30,9 @@ export enum AdjustPaymentDialogResult {
|
||||
@Component({
|
||||
templateUrl: "adjust-payment-dialog.component.html",
|
||||
})
|
||||
export class AdjustPaymentDialogComponent {
|
||||
export class AdjustPaymentDialogComponent implements OnInit {
|
||||
@ViewChild(PaymentComponent, { static: true }) paymentComponent: PaymentComponent;
|
||||
@ViewChild(TaxInfoComponent, { static: true }) taxInfoComponent: TaxInfoComponent;
|
||||
@ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent;
|
||||
|
||||
organizationId: string;
|
||||
currentType: PaymentMethodType;
|
||||
@@ -39,6 +41,8 @@ export class AdjustPaymentDialogComponent {
|
||||
protected DialogResult = AdjustPaymentDialogResult;
|
||||
protected formGroup = new FormGroup({});
|
||||
|
||||
protected taxInformation: TaxInformation;
|
||||
|
||||
constructor(
|
||||
private dialogRef: DialogRef,
|
||||
@Inject(DIALOG_DATA) protected data: AdjustPaymentDialogData,
|
||||
@@ -52,26 +56,49 @@ export class AdjustPaymentDialogComponent {
|
||||
this.currentType = data.currentType;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.organizationId) {
|
||||
this.organizationApiService
|
||||
.getTaxInfo(this.organizationId)
|
||||
.then((response: TaxInfoResponse) => {
|
||||
this.taxInformation = TaxInformation.from(response);
|
||||
})
|
||||
.catch(() => {
|
||||
this.taxInformation = new TaxInformation();
|
||||
});
|
||||
} else {
|
||||
this.apiService
|
||||
.getTaxInfo()
|
||||
.then((response: TaxInfoResponse) => {
|
||||
this.taxInformation = TaxInformation.from(response);
|
||||
})
|
||||
.catch(() => {
|
||||
this.taxInformation = new TaxInformation();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
if (!this.taxInfoComponent?.taxFormGroup.valid && this.taxInfoComponent?.taxFormGroup.touched) {
|
||||
this.taxInfoComponent.taxFormGroup.markAllAsTouched();
|
||||
if (!this.taxInfoComponent?.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = new PaymentRequest();
|
||||
const response = this.paymentComponent.createPaymentToken().then((result) => {
|
||||
request.paymentToken = result[0];
|
||||
request.paymentMethodType = result[1];
|
||||
request.postalCode = this.taxInfoComponent.taxFormGroup?.value.postalCode;
|
||||
request.country = this.taxInfoComponent.taxFormGroup?.value.country;
|
||||
request.postalCode = this.taxInformation?.postalCode;
|
||||
request.country = this.taxInformation?.country;
|
||||
request.taxId = this.taxInformation?.taxId;
|
||||
if (this.organizationId == null) {
|
||||
return this.apiService.postAccountPayment(request);
|
||||
} else {
|
||||
request.taxId = this.taxInfoComponent.taxFormGroup?.value.taxId;
|
||||
request.state = this.taxInfoComponent.taxFormGroup?.value.state;
|
||||
request.line1 = this.taxInfoComponent.taxFormGroup?.value.line1;
|
||||
request.line2 = this.taxInfoComponent.taxFormGroup?.value.line2;
|
||||
request.city = this.taxInfoComponent.taxFormGroup?.value.city;
|
||||
request.state = this.taxInfoComponent.taxFormGroup?.value.state;
|
||||
request.taxId = this.taxInformation?.taxId;
|
||||
request.state = this.taxInformation?.state;
|
||||
request.line1 = this.taxInformation?.line1;
|
||||
request.line2 = this.taxInformation?.line2;
|
||||
request.city = this.taxInformation?.city;
|
||||
request.state = this.taxInformation?.state;
|
||||
return this.organizationApiService.updatePayment(this.organizationId, request);
|
||||
}
|
||||
});
|
||||
@@ -84,8 +111,9 @@ export class AdjustPaymentDialogComponent {
|
||||
this.dialogRef.close(AdjustPaymentDialogResult.Adjusted);
|
||||
};
|
||||
|
||||
changeCountry() {
|
||||
if (this.taxInfoComponent.taxInfo.country === "US") {
|
||||
taxInformationChanged(event: TaxInformation) {
|
||||
this.taxInformation = event;
|
||||
if (event.country === "US") {
|
||||
this.paymentComponent.hideBank = !this.organizationId;
|
||||
} else {
|
||||
this.paymentComponent.hideBank = true;
|
||||
@@ -95,6 +123,10 @@ export class AdjustPaymentDialogComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected get showTaxIdField(): boolean {
|
||||
return !!this.organizationId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -110,23 +110,5 @@
|
||||
{{ "paymentChargedWithUnpaidSubscription" | i18n }}
|
||||
</p>
|
||||
</bit-section>
|
||||
<bit-section *ngIf="forOrganization">
|
||||
<h2 bitTypography="h2">{{ "taxInformation" | i18n }}</h2>
|
||||
<p bitTypography="body1">{{ "taxInformationDesc" | i18n }}</p>
|
||||
<div *ngIf="!org || loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</div>
|
||||
<form *ngIf="org && !loading" [formGroup]="taxForm" [bitSubmit]="submitTaxInfo">
|
||||
<app-tax-info></app-tax-info>
|
||||
<button bitButton bitFormButton buttonType="primary" type="submit">
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
</form>
|
||||
</bit-section>
|
||||
</ng-container>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Location } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
@@ -16,7 +16,6 @@ import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/mode
|
||||
import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response";
|
||||
import { VerifyBankRequest } from "@bitwarden/common/models/request/verify-bank.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { DialogService, ToastService } from "@bitwarden/components";
|
||||
@@ -29,15 +28,12 @@ import {
|
||||
AdjustPaymentDialogResult,
|
||||
openAdjustPaymentDialog,
|
||||
} from "./adjust-payment-dialog/adjust-payment-dialog.component";
|
||||
import { TaxInfoComponent } from "./tax-info.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "payment-method.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class PaymentMethodComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(TaxInfoComponent) taxInfo: TaxInfoComponent;
|
||||
|
||||
loading = false;
|
||||
firstLoaded = false;
|
||||
billing: BillingPaymentResponse;
|
||||
@@ -61,7 +57,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
|
||||
]),
|
||||
});
|
||||
|
||||
taxForm = this.formBuilder.group({});
|
||||
launchPaymentModalAutomatically = false;
|
||||
protected freeTrialData: FreeTrial;
|
||||
|
||||
@@ -72,7 +67,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
private logService: LogService,
|
||||
private route: ActivatedRoute,
|
||||
private formBuilder: FormBuilder,
|
||||
private dialogService: DialogService,
|
||||
@@ -198,15 +192,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
|
||||
await this.load();
|
||||
};
|
||||
|
||||
submitTaxInfo = async () => {
|
||||
await this.taxInfo.submitTaxInfo();
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("taxInfoUpdated"),
|
||||
});
|
||||
};
|
||||
|
||||
determineOrgsWithUpcomingPaymentIssues() {
|
||||
this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(
|
||||
this.organization,
|
||||
@@ -231,10 +216,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
|
||||
return this.organizationId != null;
|
||||
}
|
||||
|
||||
get headerClass() {
|
||||
return this.forOrganization ? ["page-header"] : ["tabbed-header"];
|
||||
}
|
||||
|
||||
get paymentSourceClasses() {
|
||||
if (this.paymentSource == null) {
|
||||
return [];
|
||||
|
||||
@@ -13,51 +13,41 @@
|
||||
</bit-select>
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div [ngClass]="trialFlow ? 'tw-col-span-6' : 'tw-col-span-4'">
|
||||
<div class="tw-col-span-6">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "zipPostalCode" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="postalCode" autocomplete="postal-code" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6" *ngIf="showTaxIdCheckbox">
|
||||
<bit-form-control>
|
||||
<input bitCheckbox type="checkbox" formControlName="includeTaxId" />
|
||||
<bit-label>{{ "includeVAT" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
<div class="tw-col-span-6" *ngIf="isTaxSupported">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "address1" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="line1" autocomplete="address-line1" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4" *ngIf="showTaxIdFields">
|
||||
<div class="tw-col-span-6">
|
||||
<div class="tw-col-span-6" *ngIf="isTaxSupported">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "address2" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="line2" autocomplete="address-line2" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6" *ngIf="isTaxSupported">
|
||||
<bit-form-field>
|
||||
<bit-label for="addressCity">{{ "cityTown" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="city" autocomplete="address-level2" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6" *ngIf="isTaxSupported">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "stateProvince" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="state" autocomplete="address-level1" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6" *ngIf="isTaxSupported && showTaxIdField">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "taxIdNumber" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="taxId" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4" *ngIf="showTaxIdFields">
|
||||
<div class="tw-col-span-6">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "address1" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="line1" autocomplete="address-line1" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "address2" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="line2" autocomplete="address-line2" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6">
|
||||
<bit-form-field>
|
||||
<bit-label for="addressCity">{{ "cityTown" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="city" autocomplete="address-level2" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-col-span-6">
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "stateProvince" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="state" autocomplete="address-level1" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user