mirror of
https://github.com/bitwarden/browser
synced 2026-02-13 06:54:07 +00:00
Merge branch 'main' into ps/extension-refresh
This commit is contained in:
2
.github/workflows/auto-branch-updater.yml
vendored
2
.github/workflows/auto-branch-updater.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: 'eu-web-${{ steps.setup.outputs.branch }}'
|
||||
fetch-depth: 0
|
||||
|
||||
38
.github/workflows/build-browser.yml
vendored
38
.github/workflows/build-browser.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get Package Version
|
||||
id: gen_vars
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
working-directory: apps/browser
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Testing locales - extName length
|
||||
run: |
|
||||
@@ -111,10 +111,10 @@ jobs:
|
||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -173,63 +173,63 @@ jobs:
|
||||
working-directory: browser-source/apps/browser
|
||||
|
||||
- name: Upload Opera artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: dist-opera-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-opera.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Opera MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-opera-mv3.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Chrome MV3 artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Chrome MV3 Beta artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-chrome-MV3-beta-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-chrome-mv3-beta.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Firefox artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: dist-firefox-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-firefox.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Firefox MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-firefox-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-firefox-mv3.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Edge artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: dist-edge-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-edge.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Edge MV3 artifact (DO NOT USE FOR PROD)
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: DO-NOT-USE-FOR-PROD-dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/dist/dist-edge-mv3.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload browser source
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: browser-source-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source.zip
|
||||
@@ -237,7 +237,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage artifact
|
||||
if: false
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: coverage-${{ env._BUILD_NUMBER }}.zip
|
||||
path: browser-source/apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip
|
||||
@@ -254,10 +254,10 @@ jobs:
|
||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -352,7 +352,7 @@ jobs:
|
||||
ls -la
|
||||
|
||||
- name: Upload Safari artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: dist-safari-${{ env._BUILD_NUMBER }}.zip
|
||||
path: apps/browser/dist/dist-safari.zip
|
||||
@@ -367,7 +367,7 @@ jobs:
|
||||
- build-safari
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -423,7 +423,7 @@ jobs:
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
|
||||
39
.github/workflows/build-cli.yml
vendored
39
.github/workflows/build-cli.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get Package Version
|
||||
id: retrieve-package-version
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
_WIN_PKG_VERSION: 3.5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Setup Unix Vars
|
||||
run: |
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
awk '{print tolower($0)}')" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -130,14 +130,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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
|
||||
@@ -162,7 +162,7 @@ jobs:
|
||||
_WIN_PKG_VERSION: 3.5
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Setup Windows builder
|
||||
run: |
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
choco install nasm --no-progress
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -269,14 +269,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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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
|
||||
@@ -284,18 +284,21 @@ jobs:
|
||||
|
||||
- name: Upload Chocolatey asset
|
||||
if: matrix.license_type.build_prefix == 'bit'
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
|
||||
path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Zip NPM Build Artifact
|
||||
run: Get-ChildItem -Path .\build | Compress-Archive -DestinationPath .\bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
|
||||
|
||||
- name: Upload NPM Build Directory asset
|
||||
if: matrix.license_type.build_prefix == 'bit'
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
|
||||
path: apps/cli/build
|
||||
path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
|
||||
if-no-files-found: error
|
||||
|
||||
snap:
|
||||
@@ -309,7 +312,7 @@ jobs:
|
||||
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
@@ -319,7 +322,7 @@ jobs:
|
||||
echo "BW Package Version: $_PACKAGE_VERSION"
|
||||
|
||||
- name: Get bw linux cli
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: bw-linux-${{ env._PACKAGE_VERSION }}.zip
|
||||
path: apps/cli/dist/snap
|
||||
@@ -332,7 +335,7 @@ jobs:
|
||||
ls -alth
|
||||
|
||||
- name: Build snap
|
||||
uses: snapcore/action-build@2096990827aa966f773676c8a53793c723b6b40f # v1.2.0
|
||||
uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
|
||||
with:
|
||||
path: apps/cli/dist/snap
|
||||
|
||||
@@ -361,14 +364,14 @@ jobs:
|
||||
run: sudo snap remove bw
|
||||
|
||||
- name: Upload snap asset
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
|
||||
@@ -405,7 +408,7 @@ jobs:
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
|
||||
116
.github/workflows/build-desktop.yml
vendored
116
.github/workflows/build-desktop.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Verify
|
||||
run: |
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get Package Version
|
||||
id: retrieve-version
|
||||
@@ -140,10 +140,10 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -169,7 +169,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
@@ -193,42 +193,42 @@ jobs:
|
||||
run: npm run dist:lin
|
||||
|
||||
- name: Upload .deb artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}-linux.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml
|
||||
@@ -249,10 +249,10 @@ jobs:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -298,7 +298,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: apps/desktop/desktop_native/napi/*.node
|
||||
@@ -351,91 +351,91 @@ jobs:
|
||||
-NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
|
||||
|
||||
- name: Upload portable exe artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}.yml
|
||||
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
|
||||
@@ -455,10 +455,10 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -481,14 +481,14 @@ jobs:
|
||||
|
||||
- name: Cache Build
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Cache Safari
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -581,7 +581,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: apps/desktop/desktop_native/napi/*.node
|
||||
@@ -619,10 +619,10 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -645,14 +645,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -745,7 +745,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: apps/desktop/desktop_native/napi/*.node
|
||||
@@ -761,7 +761,7 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Download Browser artifact
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: ${{ github.workspace }}/browser-build-artifacts
|
||||
|
||||
@@ -792,28 +792,28 @@ jobs:
|
||||
run: npm run pack:mac
|
||||
|
||||
- name: Upload .zip artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release_channel }}-mac.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml
|
||||
@@ -836,10 +836,10 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -862,14 +862,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -962,7 +962,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: apps/desktop/desktop_native/napi/*.node
|
||||
@@ -978,7 +978,7 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Download Browser artifact
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: ${{ github.workspace }}/browser-build-artifacts
|
||||
|
||||
@@ -1009,7 +1009,7 @@ jobs:
|
||||
run: npm run pack:mac:mas
|
||||
|
||||
- name: Upload .pkg artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
@@ -1044,10 +1044,10 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -1065,14 +1065,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -1165,7 +1165,7 @@ jobs:
|
||||
working-directory: ./
|
||||
|
||||
- name: Cache Native Module
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
id: cache
|
||||
with:
|
||||
path: apps/desktop/desktop_native/napi/*.node
|
||||
@@ -1181,7 +1181,7 @@ jobs:
|
||||
run: npm run build
|
||||
|
||||
- name: Download Browser artifact
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: ${{ github.workspace }}/browser-build-artifacts
|
||||
|
||||
@@ -1215,7 +1215,7 @@ jobs:
|
||||
zip -r Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app
|
||||
|
||||
- name: Upload masdev artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
|
||||
path: apps/desktop/dist/mas-dev-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip
|
||||
@@ -1233,7 +1233,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -1294,7 +1294,7 @@ jobs:
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
|
||||
16
.github/workflows/build-web.yml
vendored
16
.github/workflows/build-web.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get GitHub sha as version
|
||||
id: version
|
||||
@@ -91,10 +91,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build
|
||||
|
||||
- name: Upload ${{ matrix.name }} artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: web-${{ env._VERSION }}-${{ matrix.name }}.zip
|
||||
path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Check Branch to Publish
|
||||
env:
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
|
||||
- name: Download ${{ matrix.artifact_name }} artifact
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
|
||||
path: apps/web
|
||||
@@ -255,7 +255,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -345,7 +345,7 @@ jobs:
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
|
||||
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
|
||||
6
.github/workflows/chromatic.yml
vendored
6
.github/workflows/chromatic.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
@@ -38,13 +38,13 @@ jobs:
|
||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||
|
||||
- name: Cache NPM
|
||||
id: npm-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: "~/.npm"
|
||||
key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
2
.github/workflows/crowdin-pull.yml
vendored
2
.github/workflows/crowdin-pull.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
crowdin_project_id: "308189"
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
|
||||
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Lint filenames (no capital characters)
|
||||
run: |
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
|
||||
13
.github/workflows/publish-cli.yml
vendored
13
.github/workflows/publish-cli.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
||||
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -169,7 +169,7 @@ jobs:
|
||||
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -183,8 +183,11 @@ jobs:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "npm-api-key"
|
||||
|
||||
- name: Download artifacts
|
||||
run: wget https://github.com/bitwarden/clients/releases/download/cli-v${{ env._PKG_VERSION }}/bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
|
||||
- name: Download and set up artifact
|
||||
run: |
|
||||
mkdir -p build
|
||||
wget https://github.com/bitwarden/clients/releases/download/cli-v${{ env._PKG_VERSION }}/bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
|
||||
unzip bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip -d build
|
||||
|
||||
- name: Setup NPM
|
||||
run: |
|
||||
|
||||
4
.github/workflows/publish-desktop.yml
vendored
4
.github/workflows/publish-desktop.yml
vendored
@@ -184,7 +184,7 @@ jobs:
|
||||
_RELEASE_TAG: ${{ needs.setup.outputs.tag-name }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Login to Azure
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@@ -228,7 +228,7 @@ jobs:
|
||||
_RELEASE_TAG: ${{ needs.setup.outputs.tag-name }}
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Print Environment
|
||||
run: |
|
||||
|
||||
4
.github/workflows/publish-web.yml
vendored
4
.github/workflows/publish-web.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
tag_version: ${{ steps.version.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
if: ${{ inputs.publish_type != 'Dry Run' }}
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
echo "Github Release Option: $_RELEASE_OPTION"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
########## ACR ##########
|
||||
- name: Login to Azure - PROD Subscription
|
||||
|
||||
4
.github/workflows/release-browser.yml
vendored
4
.github/workflows/release-browser.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
release-version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
needs: setup
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Testing locales - extName length
|
||||
run: |
|
||||
|
||||
2
.github/workflows/release-cli.yml
vendored
2
.github/workflows/release-cli.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
release-version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
|
||||
86
.github/workflows/release-desktop-beta.yml
vendored
86
.github/workflows/release-desktop-beta.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
run: |
|
||||
@@ -125,12 +125,12 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch-name }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -159,42 +159,42 @@ jobs:
|
||||
run: npm run dist:lin
|
||||
|
||||
- name: Upload .deb artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}-linux.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-linux.yml
|
||||
@@ -215,12 +215,12 @@ jobs:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch-name }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -300,91 +300,91 @@ jobs:
|
||||
-NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
|
||||
|
||||
- name: Upload portable exe artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}.yml
|
||||
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release-channel }}.yml
|
||||
@@ -404,12 +404,12 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch-name }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -427,14 +427,14 @@ jobs:
|
||||
|
||||
- name: Cache Build
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Cache Safari
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -538,12 +538,12 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch-name }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -561,14 +561,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -708,28 +708,28 @@ jobs:
|
||||
run: npm run pack:mac
|
||||
|
||||
- name: Upload .zip artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: ${{ needs.setup.outputs.release-channel }}-mac.yml
|
||||
path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-mac.yml
|
||||
@@ -751,12 +751,12 @@ jobs:
|
||||
working-directory: apps/desktop
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.branch-name }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -774,14 +774,14 @@ jobs:
|
||||
|
||||
- name: Get Build Cache
|
||||
id: build-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/desktop/build
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-build
|
||||
|
||||
- name: Setup Safari Cache
|
||||
id: safari-cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
with:
|
||||
path: apps/browser/dist/Safari
|
||||
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
|
||||
@@ -916,7 +916,7 @@ jobs:
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: Upload .pkg artifact
|
||||
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
|
||||
@@ -958,7 +958,7 @@ jobs:
|
||||
aws-electron-bucket-name"
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
path: apps/desktop/artifacts
|
||||
|
||||
@@ -1011,7 +1011,7 @@ jobs:
|
||||
- release
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
|
||||
2
.github/workflows/release-desktop.yml
vendored
2
.github/workflows/release-desktop.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
release-channel: ${{ steps.release-channel.outputs.channel }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
|
||||
2
.github/workflows/release-web.yml
vendored
2
.github/workflows/release-web.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
tag_version: ${{ steps.version.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Branch check
|
||||
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
|
||||
|
||||
10
.github/workflows/scan.yml
vendored
10
.github/workflows/scan.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Scan with Checkmarx
|
||||
uses: checkmarx/ast-github-action@749fec53e0db0f6404a97e2e0807c3e80e3583a7 # v2.0.23
|
||||
uses: checkmarx/ast-github-action@1fe318de2993222574e6249750ba9000a4e2a6cd # 2.0.33
|
||||
env:
|
||||
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
|
||||
with:
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
--output-path . ${{ env.INCREMENTAL }}
|
||||
|
||||
- name: Upload Checkmarx results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
|
||||
uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5
|
||||
with:
|
||||
sarif_file: cx_result.sarif
|
||||
|
||||
@@ -61,13 +61,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Scan with SonarCloud
|
||||
uses: sonarsource/sonarcloud-github-action@49e6cd3b187936a73b8280d59ffd9da69df63ec9 # v2.1.1
|
||||
uses: sonarsource/sonarcloud-github-action@e44258b109568baa0df60ed515909fc6c72cba92 # v2.3.0
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get Node Version
|
||||
id: retrieve-node-version
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
|
||||
with:
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '**/package-lock.json'
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
fail-on-error: true
|
||||
|
||||
- name: Upload coverage to codecov.io
|
||||
uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0
|
||||
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
|
||||
if: ${{ needs.check-test-secrets.outputs.available == 'true' }}
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
sudo apt-get install -y gnome-keyring dbus-x11
|
||||
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Build
|
||||
working-directory: ./apps/desktop/desktop_native
|
||||
|
||||
4
.github/workflows/version-bump.yml
vendored
4
.github/workflows/version-bump.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: main
|
||||
|
||||
@@ -526,7 +526,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Branch
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
ref: main
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/browser",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.8.1",
|
||||
"scripts": {
|
||||
"build": "cross-env MANIFEST_VERSION=3 webpack",
|
||||
"build:mv2": "webpack",
|
||||
|
||||
@@ -1113,7 +1113,7 @@
|
||||
"message": "Thank you for supporting Bitwarden."
|
||||
},
|
||||
"premiumFeatures": {
|
||||
"message": "Upgrade to premium and receive:"
|
||||
"message": "Upgrade to Premium and receive:"
|
||||
},
|
||||
"premiumPrice": {
|
||||
"message": "All for just $PRICE$ /year!",
|
||||
@@ -2083,6 +2083,12 @@
|
||||
"biometricsNotUnlockedDesc": {
|
||||
"message": "Please unlock this user in the desktop application and try again."
|
||||
},
|
||||
"biometricsNotAvailableTitle": {
|
||||
"message": "Biometric unlock unavailable"
|
||||
},
|
||||
"biometricsNotAvailableDesc": {
|
||||
"message": "Biometric unlock is currently unavailable. Please try again later."
|
||||
},
|
||||
"biometricsFailedTitle": {
|
||||
"message": "Biometrics failed"
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<p class="text-center" *ngIf="!fido2Data.isFido2Session">
|
||||
<button type="button" appStopClick (click)="logOut()">{{ "logOut" | i18n }}</button>
|
||||
</p>
|
||||
<app-callout *ngIf="biometricError" type="error">{{ biometricError }}</app-callout>
|
||||
<app-callout *ngIf="biometricError" type="danger">{{ biometricError }}</app-callout>
|
||||
<p class="text-center text-muted" *ngIf="pendingBiometric">
|
||||
<i class="bwi bwi-spinner bwi-spin" aria-hidden="true"></i> {{ "awaitDesktop" | i18n }}
|
||||
</p>
|
||||
|
||||
@@ -24,6 +24,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
@@ -67,6 +68,7 @@ export class LockComponent extends BaseLockComponent implements OnInit {
|
||||
pinService: PinServiceAbstraction,
|
||||
private routerService: BrowserRouterService,
|
||||
biometricStateService: BiometricStateService,
|
||||
biometricsService: BiometricsService,
|
||||
accountService: AccountService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
syncService: SyncService,
|
||||
@@ -93,6 +95,7 @@ export class LockComponent extends BaseLockComponent implements OnInit {
|
||||
userVerificationService,
|
||||
pinService,
|
||||
biometricStateService,
|
||||
biometricsService,
|
||||
accountService,
|
||||
authService,
|
||||
kdfConfigService,
|
||||
@@ -129,22 +132,35 @@ export class LockComponent extends BaseLockComponent implements OnInit {
|
||||
this.isInitialLockScreen &&
|
||||
(await this.authService.getAuthStatus()) === AuthenticationStatus.Locked
|
||||
) {
|
||||
await this.unlockBiometric();
|
||||
await this.unlockBiometric(true);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
override async unlockBiometric(): Promise<boolean> {
|
||||
override async unlockBiometric(automaticPrompt: boolean = false): Promise<boolean> {
|
||||
if (!this.biometricLock) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pendingBiometric = true;
|
||||
this.biometricError = null;
|
||||
|
||||
let success;
|
||||
try {
|
||||
success = await super.unlockBiometric();
|
||||
const available = await super.isBiometricUnlockAvailable();
|
||||
if (!available) {
|
||||
if (!automaticPrompt) {
|
||||
await this.dialogService.openSimpleDialog({
|
||||
type: "warning",
|
||||
title: { key: "biometricsNotAvailableTitle" },
|
||||
content: { key: "biometricsNotAvailableDesc" },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.pendingBiometric = true;
|
||||
success = await super.unlockBiometric();
|
||||
}
|
||||
} catch (e) {
|
||||
const error = BiometricErrors[e?.message as BiometricErrorTypes];
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import {
|
||||
VaultTimeout,
|
||||
VaultTimeoutOption,
|
||||
@@ -94,6 +95,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
||||
private dialogService: DialogService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private biometricsService: BiometricsService,
|
||||
) {
|
||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
||||
}
|
||||
@@ -165,7 +167,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
this.form.patchValue(initialValues, { emitEvent: false });
|
||||
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.supportsBiometric = await this.biometricsService.supportsBiometric();
|
||||
this.showChangeMasterPass = await this.userVerificationService.hasMasterPassword();
|
||||
|
||||
this.form.controls.vaultTimeout.valueChanges
|
||||
@@ -405,7 +407,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
|
||||
|
||||
const biometricsPromise = async () => {
|
||||
try {
|
||||
const result = await this.platformUtilsService.authenticateBiometric();
|
||||
const result = await this.biometricsService.authenticateBiometric();
|
||||
|
||||
// prevent duplicate dialog
|
||||
biometricsResponseReceived = true;
|
||||
|
||||
@@ -97,6 +97,7 @@ import {
|
||||
BiometricStateService,
|
||||
DefaultBiometricStateService,
|
||||
} from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency creation
|
||||
@@ -228,6 +229,7 @@ import { ChromeMessageSender } from "../platform/messaging/chrome-message.sender
|
||||
import { OffscreenDocumentService } from "../platform/offscreen-document/abstractions/offscreen-document";
|
||||
import { DefaultOffscreenDocumentService } from "../platform/offscreen-document/offscreen-document.service";
|
||||
import { BrowserTaskSchedulerService } from "../platform/services/abstractions/browser-task-scheduler.service";
|
||||
import { BackgroundBrowserBiometricsService } from "../platform/services/background-browser-biometrics.service";
|
||||
import { BrowserCryptoService } from "../platform/services/browser-crypto.service";
|
||||
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
|
||||
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
|
||||
@@ -343,6 +345,7 @@ export default class MainBackground {
|
||||
organizationVaultExportService: OrganizationVaultExportServiceAbstraction;
|
||||
vaultSettingsService: VaultSettingsServiceAbstraction;
|
||||
biometricStateService: BiometricStateService;
|
||||
biometricsService: BiometricsService;
|
||||
stateEventRunnerService: StateEventRunnerService;
|
||||
ssoLoginService: SsoLoginServiceAbstraction;
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService;
|
||||
@@ -429,7 +432,6 @@ export default class MainBackground {
|
||||
this.platformUtilsService = new BackgroundPlatformUtilsService(
|
||||
this.messagingService,
|
||||
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
|
||||
async () => this.biometricUnlock(),
|
||||
self,
|
||||
this.offscreenDocumentService,
|
||||
);
|
||||
@@ -577,6 +579,7 @@ export default class MainBackground {
|
||||
);
|
||||
|
||||
this.popupViewCacheBackgroundService = new PopupViewCacheBackgroundService(
|
||||
messageListener,
|
||||
this.globalStateProvider,
|
||||
);
|
||||
|
||||
@@ -610,6 +613,8 @@ export default class MainBackground {
|
||||
|
||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||
|
||||
this.biometricsService = new BackgroundBrowserBiometricsService(this.nativeMessagingBackground);
|
||||
|
||||
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
||||
|
||||
this.pinService = new PinService(
|
||||
@@ -636,6 +641,7 @@ export default class MainBackground {
|
||||
this.accountService,
|
||||
this.stateProvider,
|
||||
this.biometricStateService,
|
||||
this.biometricsService,
|
||||
this.kdfConfigService,
|
||||
);
|
||||
|
||||
@@ -1507,17 +1513,6 @@ export default class MainBackground {
|
||||
}
|
||||
}
|
||||
|
||||
async biometricUnlock(): Promise<boolean> {
|
||||
if (this.nativeMessagingBackground == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const responsePromise = this.nativeMessagingBackground.getResponse();
|
||||
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
|
||||
const response = await responsePromise;
|
||||
return response.response === "unlocked";
|
||||
}
|
||||
|
||||
private async fullSync(override = false) {
|
||||
const syncInternal = 6 * 60 * 60 * 1000; // 6 hours
|
||||
const lastSync = await this.syncService.getLastSync();
|
||||
|
||||
@@ -285,7 +285,9 @@ export class NativeMessagingBackground {
|
||||
switch (message.command) {
|
||||
case "biometricUnlock": {
|
||||
if (
|
||||
["not enabled", "not supported", "not unlocked", "canceled"].includes(message.response)
|
||||
["not available", "not enabled", "not supported", "not unlocked", "canceled"].includes(
|
||||
message.response,
|
||||
)
|
||||
) {
|
||||
this.rejecter(message.response);
|
||||
return;
|
||||
@@ -352,6 +354,10 @@ export class NativeMessagingBackground {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "biometricUnlockAvailable": {
|
||||
this.resolver(message);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.logService.error("NativeMessage, got unknown command: " + message.command);
|
||||
break;
|
||||
|
||||
@@ -68,6 +68,7 @@ export default class RuntimeBackground {
|
||||
) => {
|
||||
const messagesWithResponse = [
|
||||
"biometricUnlock",
|
||||
"biometricUnlockAvailable",
|
||||
"getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag",
|
||||
"getInlineMenuFieldQualificationFeatureFlag",
|
||||
];
|
||||
@@ -179,7 +180,11 @@ export default class RuntimeBackground {
|
||||
}
|
||||
break;
|
||||
case "biometricUnlock": {
|
||||
const result = await this.main.biometricUnlock();
|
||||
const result = await this.main.biometricsService.authenticateBiometric();
|
||||
return result;
|
||||
}
|
||||
case "biometricUnlockAvailable": {
|
||||
const result = await this.main.biometricsService.isBiometricUnlockAvailable();
|
||||
return result;
|
||||
}
|
||||
case "getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<bit-section>
|
||||
<bit-card>
|
||||
<div class="tw-flex tw-flex-col tw-p-2">
|
||||
<ul class="tw-list-disc tw-pl-5 tw-space-y-2 tw-break-words">
|
||||
<ul class="tw-list-disc tw-pl-5 tw-space-y-2 tw-break-words tw-mb-0">
|
||||
<li>
|
||||
{{ "ppremiumSignUpStorage" | i18n }}
|
||||
</li>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.8.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"minimum_chrome_version": "102.0",
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.8.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -11,7 +11,8 @@ export type BiometricErrorTypes =
|
||||
| "not unlocked"
|
||||
| "invalidateEncryption"
|
||||
| "userkey wrong"
|
||||
| "wrongUserId";
|
||||
| "wrongUserId"
|
||||
| "not available";
|
||||
|
||||
export const BiometricErrors: Record<BiometricErrorTypes, BiometricError> = {
|
||||
startDesktop: {
|
||||
@@ -46,4 +47,8 @@ export const BiometricErrors: Record<BiometricErrorTypes, BiometricError> = {
|
||||
title: "biometricsWrongUserTitle",
|
||||
description: "biometricsWrongUserDesc",
|
||||
},
|
||||
"not available": {
|
||||
title: "biometricsNotAvailableTitle",
|
||||
description: "biometricsNotAvailableDesc",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import {
|
||||
DestroyRef,
|
||||
effect,
|
||||
inject,
|
||||
Injectable,
|
||||
Injector,
|
||||
signal,
|
||||
WritableSignal,
|
||||
} from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormGroup } from "@angular/forms";
|
||||
import { NavigationEnd, Router } from "@angular/router";
|
||||
import { filter, firstValueFrom, skip } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import {
|
||||
FormCacheOptions,
|
||||
SignalCacheOptions,
|
||||
ViewCacheService,
|
||||
} from "@bitwarden/angular/platform/abstractions/view-cache.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
import { GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
import {
|
||||
ClEAR_VIEW_CACHE_COMMAND,
|
||||
POPUP_VIEW_CACHE_KEY,
|
||||
SAVE_VIEW_CACHE_COMMAND,
|
||||
} from "../../services/popup-view-cache-background.service";
|
||||
|
||||
/**
|
||||
* Popup implementation of {@link ViewCacheService}.
|
||||
*
|
||||
* Persists user changes between popup open and close
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class PopupViewCacheService implements ViewCacheService {
|
||||
private configService = inject(ConfigService);
|
||||
private globalStateProvider = inject(GlobalStateProvider);
|
||||
private messageSender = inject(MessageSender);
|
||||
private router = inject(Router);
|
||||
|
||||
private featureEnabled: boolean;
|
||||
|
||||
private _cache: Record<string, string>;
|
||||
private get cache(): Record<string, string> {
|
||||
if (!this._cache) {
|
||||
throw new Error("Dirty View Cache not initialized");
|
||||
}
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the service. This should only be called once.
|
||||
*/
|
||||
async init() {
|
||||
this.featureEnabled = await this.configService.getFeatureFlag(FeatureFlag.PersistPopupView);
|
||||
const initialState = this.featureEnabled
|
||||
? await firstValueFrom(this.globalStateProvider.get(POPUP_VIEW_CACHE_KEY).state$)
|
||||
: {};
|
||||
this._cache = Object.freeze(initialState ?? {});
|
||||
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter((e) => e instanceof NavigationEnd),
|
||||
/** Skip the first navigation triggered by `popupRouterCacheGuard` */
|
||||
skip(1),
|
||||
)
|
||||
.subscribe(() => this.clearState());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link ViewCacheService.signal}
|
||||
*/
|
||||
signal<T>(options: SignalCacheOptions<T>): WritableSignal<T> {
|
||||
const {
|
||||
deserializer = (v: Jsonify<T>): T => v as T,
|
||||
key,
|
||||
injector = inject(Injector),
|
||||
initialValue,
|
||||
} = options;
|
||||
const cachedValue = this.cache[key] ? deserializer(JSON.parse(this.cache[key])) : initialValue;
|
||||
const _signal = signal(cachedValue);
|
||||
|
||||
effect(
|
||||
() => {
|
||||
this.updateState(key, JSON.stringify(_signal()));
|
||||
},
|
||||
{ injector },
|
||||
);
|
||||
|
||||
return _signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link ViewCacheService.formGroup}
|
||||
*/
|
||||
formGroup<TFormGroup extends FormGroup>(options: FormCacheOptions<TFormGroup>): TFormGroup {
|
||||
const { control, injector } = options;
|
||||
|
||||
const _signal = this.signal({
|
||||
...options,
|
||||
initialValue: control.getRawValue(),
|
||||
});
|
||||
|
||||
const value = _signal();
|
||||
if (value !== undefined && JSON.stringify(value) !== JSON.stringify(control.getRawValue())) {
|
||||
control.setValue(value);
|
||||
control.markAsDirty();
|
||||
}
|
||||
|
||||
control.valueChanges.pipe(takeUntilDestroyed(injector?.get(DestroyRef))).subscribe(() => {
|
||||
_signal.set(control.getRawValue());
|
||||
});
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
private updateState(key: string, value: string) {
|
||||
if (!this.featureEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.messageSender.send(SAVE_VIEW_CACHE_COMMAND, {
|
||||
key,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
private clearState() {
|
||||
this.messageSender.send(ClEAR_VIEW_CACHE_COMMAND, {});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
import { Component, inject, Injector } from "@angular/core";
|
||||
import { TestBed } from "@angular/core/testing";
|
||||
import { FormControl, FormGroup } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { RouterTestingModule } from "@angular/router/testing";
|
||||
import { MockProxy, mock } from "jest-mock-extended";
|
||||
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
import { GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||
import { FakeGlobalState, FakeGlobalStateProvider } from "@bitwarden/common/spec";
|
||||
|
||||
import {
|
||||
ClEAR_VIEW_CACHE_COMMAND,
|
||||
POPUP_VIEW_CACHE_KEY,
|
||||
SAVE_VIEW_CACHE_COMMAND,
|
||||
} from "../../services/popup-view-cache-background.service";
|
||||
|
||||
import { PopupViewCacheService } from "./popup-view-cache.service";
|
||||
|
||||
@Component({ template: "" })
|
||||
export class EmptyComponent {}
|
||||
|
||||
@Component({ template: "" })
|
||||
export class TestComponent {
|
||||
private viewCacheService = inject(PopupViewCacheService);
|
||||
|
||||
formGroup = this.viewCacheService.formGroup({
|
||||
key: "test-form-cache",
|
||||
control: new FormGroup({
|
||||
name: new FormControl("initial name"),
|
||||
}),
|
||||
});
|
||||
|
||||
signal = this.viewCacheService.signal({
|
||||
key: "test-signal",
|
||||
initialValue: "initial signal",
|
||||
});
|
||||
}
|
||||
|
||||
describe("popup view cache", () => {
|
||||
const configServiceMock = mock<ConfigService>();
|
||||
let testBed: TestBed;
|
||||
let service: PopupViewCacheService;
|
||||
let fakeGlobalState: FakeGlobalState<Record<string, string>>;
|
||||
let messageSenderMock: MockProxy<MessageSender>;
|
||||
let router: Router;
|
||||
|
||||
const initServiceWithState = async (state: Record<string, string>) => {
|
||||
await fakeGlobalState.update(() => state);
|
||||
await service.init();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.spyOn(configServiceMock, "getFeatureFlag").mockResolvedValue(true);
|
||||
messageSenderMock = mock<MessageSender>();
|
||||
|
||||
const fakeGlobalStateProvider = new FakeGlobalStateProvider();
|
||||
fakeGlobalState = fakeGlobalStateProvider.getFake(POPUP_VIEW_CACHE_KEY);
|
||||
|
||||
testBed = TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule.withRoutes([
|
||||
{ path: "a", component: EmptyComponent },
|
||||
{ path: "b", component: EmptyComponent },
|
||||
]),
|
||||
],
|
||||
providers: [
|
||||
{ provide: GlobalStateProvider, useValue: fakeGlobalStateProvider },
|
||||
{ provide: MessageSender, useValue: messageSenderMock },
|
||||
{ provide: ConfigService, useValue: configServiceMock },
|
||||
],
|
||||
});
|
||||
|
||||
await testBed.compileComponents();
|
||||
|
||||
router = testBed.inject(Router);
|
||||
service = testBed.inject(PopupViewCacheService);
|
||||
});
|
||||
|
||||
it("should initialize signal when ran within an injection context", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const signal = TestBed.runInInjectionContext(() =>
|
||||
service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(signal()).toBe("foo");
|
||||
});
|
||||
|
||||
it("should initialize signal when provided an injector", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const injector = TestBed.inject(Injector);
|
||||
|
||||
const signal = service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
injector,
|
||||
});
|
||||
|
||||
expect(signal()).toBe("foo");
|
||||
});
|
||||
|
||||
it("should initialize signal from state", async () => {
|
||||
await initServiceWithState({ "foo-123": JSON.stringify("bar") });
|
||||
|
||||
const injector = TestBed.inject(Injector);
|
||||
|
||||
const signal = service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
injector,
|
||||
});
|
||||
|
||||
expect(signal()).toBe("bar");
|
||||
});
|
||||
|
||||
it("should initialize form from state", async () => {
|
||||
await initServiceWithState({ "test-form-cache": JSON.stringify({ name: "baz" }) });
|
||||
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
const component = fixture.componentRef.instance;
|
||||
expect(component.formGroup.value.name).toBe("baz");
|
||||
expect(component.formGroup.dirty).toBe(true);
|
||||
});
|
||||
|
||||
it("should not modify form when empty", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
const component = fixture.componentRef.instance;
|
||||
expect(component.formGroup.value.name).toBe("initial name");
|
||||
expect(component.formGroup.dirty).toBe(false);
|
||||
});
|
||||
|
||||
it("should utilize deserializer", async () => {
|
||||
await initServiceWithState({ "foo-123": JSON.stringify("bar") });
|
||||
|
||||
const injector = TestBed.inject(Injector);
|
||||
|
||||
const signal = service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
injector,
|
||||
deserializer: (jsonValue) => "test",
|
||||
});
|
||||
|
||||
expect(signal()).toBe("test");
|
||||
});
|
||||
|
||||
it("should not utilize deserializer when empty", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const injector = TestBed.inject(Injector);
|
||||
|
||||
const signal = service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
injector,
|
||||
deserializer: (jsonValue) => "test",
|
||||
});
|
||||
|
||||
expect(signal()).toBe("foo");
|
||||
});
|
||||
|
||||
it("should send signal updates to message sender", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
const component = fixture.componentRef.instance;
|
||||
component.signal.set("Foobar");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(messageSenderMock.send).toHaveBeenCalledWith(SAVE_VIEW_CACHE_COMMAND, {
|
||||
key: "test-signal",
|
||||
value: JSON.stringify("Foobar"),
|
||||
});
|
||||
});
|
||||
|
||||
it("should send form updates to message sender", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
const component = fixture.componentRef.instance;
|
||||
component.formGroup.controls.name.setValue("Foobar");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(messageSenderMock.send).toHaveBeenCalledWith(SAVE_VIEW_CACHE_COMMAND, {
|
||||
key: "test-form-cache",
|
||||
value: JSON.stringify({ name: "Foobar" }),
|
||||
});
|
||||
});
|
||||
|
||||
it("should clear on 2nd navigation", async () => {
|
||||
await initServiceWithState({});
|
||||
|
||||
await router.navigate(["a"]);
|
||||
expect(messageSenderMock.send).toHaveBeenCalledTimes(0);
|
||||
|
||||
await router.navigate(["b"]);
|
||||
expect(messageSenderMock.send).toHaveBeenCalledWith(ClEAR_VIEW_CACHE_COMMAND, {});
|
||||
});
|
||||
|
||||
it("should ignore cached values when feature flag is off", async () => {
|
||||
jest.spyOn(configServiceMock, "getFeatureFlag").mockResolvedValue(false);
|
||||
|
||||
await initServiceWithState({ "foo-123": JSON.stringify("bar") });
|
||||
|
||||
const injector = TestBed.inject(Injector);
|
||||
|
||||
const signal = service.signal({
|
||||
key: "foo-123",
|
||||
initialValue: "foo",
|
||||
injector,
|
||||
});
|
||||
|
||||
// The cached state is ignored
|
||||
expect(signal()).toBe("foo");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { NativeMessagingBackground } from "../../background/nativeMessaging.background";
|
||||
|
||||
import { BrowserBiometricsService } from "./browser-biometrics.service";
|
||||
|
||||
@Injectable()
|
||||
export class BackgroundBrowserBiometricsService extends BrowserBiometricsService {
|
||||
constructor(private nativeMessagingBackground: NativeMessagingBackground) {
|
||||
super();
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
const responsePromise = this.nativeMessagingBackground.getResponse();
|
||||
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
|
||||
const response = await responsePromise;
|
||||
return response.response === "unlocked";
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
const responsePromise = this.nativeMessagingBackground.getResponse();
|
||||
await this.nativeMessagingBackground.send({ command: "biometricUnlockAvailable" });
|
||||
const response = await responsePromise;
|
||||
return response.response === "available";
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
|
||||
@Injectable()
|
||||
export abstract class BrowserBiometricsService extends BiometricsService {
|
||||
async supportsBiometric() {
|
||||
const platformInfo = await BrowserApi.getPlatformInfo();
|
||||
if (platformInfo.os === "mac" || platformInfo.os === "win" || platformInfo.os === "linux") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract authenticateBiometric(): Promise<boolean>;
|
||||
abstract isBiometricUnlockAvailable(): Promise<boolean>;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/platform/enums";
|
||||
import { CryptoService } from "@bitwarden/common/platform/services/crypto.service";
|
||||
import { USER_KEY } from "@bitwarden/common/platform/services/key-state/user-key.state";
|
||||
@@ -31,6 +32,7 @@ export class BrowserCryptoService extends CryptoService {
|
||||
accountService: AccountService,
|
||||
stateProvider: StateProvider,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private biometricsService: BiometricsService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
) {
|
||||
super(
|
||||
@@ -68,7 +70,7 @@ export class BrowserCryptoService extends CryptoService {
|
||||
userId?: UserId,
|
||||
): Promise<UserKey> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
const biometricsResult = await this.platformUtilService.authenticateBiometric();
|
||||
const biometricsResult = await this.biometricsService.authenticateBiometric();
|
||||
|
||||
if (!biometricsResult) {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
|
||||
import { BrowserBiometricsService } from "./browser-biometrics.service";
|
||||
|
||||
export class ForegroundBrowserBiometricsService extends BrowserBiometricsService {
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
const response = await BrowserApi.sendMessageWithResponse<{
|
||||
result: boolean;
|
||||
error: string;
|
||||
}>("biometricUnlock");
|
||||
if (!response.result) {
|
||||
throw response.error;
|
||||
}
|
||||
return response.result;
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
const response = await BrowserApi.sendMessageWithResponse<{
|
||||
result: boolean;
|
||||
error: string;
|
||||
}>("biometricUnlockAvailable");
|
||||
return response.result && response.result === true;
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {}
|
||||
}
|
||||
@@ -8,11 +8,10 @@ export class BackgroundPlatformUtilsService extends BrowserPlatformUtilsService
|
||||
constructor(
|
||||
private messagingService: MessagingService,
|
||||
clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
|
||||
biometricCallback: () => Promise<boolean>,
|
||||
win: Window & typeof globalThis,
|
||||
offscreenDocumentService: OffscreenDocumentService,
|
||||
) {
|
||||
super(clipboardWriteCallback, biometricCallback, win, offscreenDocumentService);
|
||||
super(clipboardWriteCallback, win, offscreenDocumentService);
|
||||
}
|
||||
|
||||
override showToast(
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestBrowserPlatformUtilsService extends BrowserPlatformUtilsService {
|
||||
win: Window & typeof globalThis,
|
||||
offscreenDocumentService: OffscreenDocumentService,
|
||||
) {
|
||||
super(clipboardSpy, null, win, offscreenDocumentService);
|
||||
super(clipboardSpy, win, offscreenDocumentService);
|
||||
}
|
||||
|
||||
showToast(
|
||||
|
||||
@@ -15,7 +15,6 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
|
||||
constructor(
|
||||
private clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
|
||||
private biometricCallback: () => Promise<boolean>,
|
||||
private globalContext: Window | ServiceWorkerGlobalScope,
|
||||
private offscreenDocumentService: OffscreenDocumentService,
|
||||
) {}
|
||||
@@ -276,30 +275,6 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic
|
||||
return await BrowserClipboardService.read(windowContext);
|
||||
}
|
||||
|
||||
async supportsBiometric() {
|
||||
const platformInfo = await BrowserApi.getPlatformInfo();
|
||||
if (platformInfo.os === "mac" || platformInfo.os === "win" || platformInfo.os === "linux") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
authenticateBiometric() {
|
||||
return this.biometricCallback();
|
||||
}
|
||||
|
||||
supportsSecureStorage(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,10 @@ export class ForegroundPlatformUtilsService extends BrowserPlatformUtilsService
|
||||
constructor(
|
||||
private toastService: ToastService,
|
||||
clipboardWriteCallback: (clipboardValue: string, clearMs: number) => void,
|
||||
biometricCallback: () => Promise<boolean>,
|
||||
win: Window & typeof globalThis,
|
||||
offscreenDocumentService: OffscreenDocumentService,
|
||||
) {
|
||||
super(clipboardWriteCallback, biometricCallback, win, offscreenDocumentService);
|
||||
super(clipboardWriteCallback, win, offscreenDocumentService);
|
||||
}
|
||||
|
||||
override showToast(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { switchMap, merge, delay, filter, map } from "rxjs";
|
||||
import { switchMap, merge, delay, filter, concatMap, map } from "rxjs";
|
||||
|
||||
import { CommandDefinition, MessageListener } from "@bitwarden/common/platform/messaging";
|
||||
import {
|
||||
POPUP_VIEW_MEMORY,
|
||||
KeyDefinition,
|
||||
@@ -11,6 +12,15 @@ import { fromChromeEvent } from "../browser/from-chrome-event";
|
||||
|
||||
const popupClosedPortName = "new_popup";
|
||||
|
||||
/** We cannot use `UserKeyDefinition` because we must be able to store state when there is no active user. */
|
||||
export const POPUP_VIEW_CACHE_KEY = KeyDefinition.record<string>(
|
||||
POPUP_VIEW_MEMORY,
|
||||
"popup-view-cache",
|
||||
{
|
||||
deserializer: (jsonValue) => jsonValue,
|
||||
},
|
||||
);
|
||||
|
||||
export const POPUP_ROUTE_HISTORY_KEY = new KeyDefinition<string[]>(
|
||||
POPUP_VIEW_MEMORY,
|
||||
"popup-route-history",
|
||||
@@ -19,12 +29,35 @@ export const POPUP_ROUTE_HISTORY_KEY = new KeyDefinition<string[]>(
|
||||
},
|
||||
);
|
||||
|
||||
export const SAVE_VIEW_CACHE_COMMAND = new CommandDefinition<{
|
||||
key: string;
|
||||
value: string;
|
||||
}>("save-view-cache");
|
||||
|
||||
export const ClEAR_VIEW_CACHE_COMMAND = new CommandDefinition("clear-view-cache");
|
||||
|
||||
export class PopupViewCacheBackgroundService {
|
||||
private popupViewCacheState = this.globalStateProvider.get(POPUP_VIEW_CACHE_KEY);
|
||||
private popupRouteHistoryState = this.globalStateProvider.get(POPUP_ROUTE_HISTORY_KEY);
|
||||
|
||||
constructor(private globalStateProvider: GlobalStateProvider) {}
|
||||
constructor(
|
||||
private messageListener: MessageListener,
|
||||
private globalStateProvider: GlobalStateProvider,
|
||||
) {}
|
||||
|
||||
startObservingTabChanges() {
|
||||
this.messageListener
|
||||
.messages$(SAVE_VIEW_CACHE_COMMAND)
|
||||
.pipe(
|
||||
concatMap(async ({ key, value }) =>
|
||||
this.popupViewCacheState.update((state) => ({
|
||||
...state,
|
||||
[key]: value,
|
||||
})),
|
||||
),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
merge(
|
||||
// on tab changed, excluding extension tabs
|
||||
fromChromeEvent(chrome.tabs.onActivated).pipe(
|
||||
@@ -45,6 +78,7 @@ export class PopupViewCacheBackgroundService {
|
||||
|
||||
async clearState() {
|
||||
return Promise.all([
|
||||
this.popupViewCacheState.update(() => ({}), { shouldUpdate: this.objNotEmpty }),
|
||||
this.popupRouteHistoryState.update(() => [], { shouldUpdate: this.objNotEmpty }),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core";
|
||||
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
|
||||
import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs";
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { BrowserApi } from "../platform/browser/browser-api";
|
||||
import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service";
|
||||
import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service";
|
||||
import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service";
|
||||
import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service";
|
||||
@@ -37,6 +38,8 @@ import { DesktopSyncVerificationDialogComponent } from "./components/desktop-syn
|
||||
</div>`,
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
private viewCacheService = inject(PopupViewCacheService);
|
||||
|
||||
private lastActivity: Date;
|
||||
private activeUserId: UserId;
|
||||
private recordActivitySubject = new Subject<void>();
|
||||
@@ -64,6 +67,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
async ngOnInit() {
|
||||
initPopupClosedListener();
|
||||
await this.viewCacheService.init();
|
||||
|
||||
// Component states must not persist between closing and reopening the popup, otherwise they become dead objects
|
||||
// Clear them aggressively to make sure this doesn't occur
|
||||
|
||||
@@ -287,102 +287,6 @@ app-vault-icon,
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.callout {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border: 1px solid #000000;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
@include themify($themes) {
|
||||
border-color: themed("calloutBorderColor");
|
||||
background-color: themed("calloutBackgroundColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3.callout-heading {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.callout-primary {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("primaryColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("infoColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("infoColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("dangerColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("dangerColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-success {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("successColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("successColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-warning {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("warningColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("warningColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.active {
|
||||
@include themify($themes) {
|
||||
background-color: themed("boxBackgroundHoverColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.enforced-policy-options ul {
|
||||
padding-left: 30px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="password"]::-ms-reveal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core";
|
||||
import { Subject, merge, of } from "rxjs";
|
||||
|
||||
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
|
||||
import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service";
|
||||
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
|
||||
import {
|
||||
@@ -62,6 +63,7 @@ import {
|
||||
ObservableStorageService,
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
@@ -102,11 +104,13 @@ import { OffscreenDocumentService } from "../../platform/offscreen-document/abst
|
||||
import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service";
|
||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
||||
import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service";
|
||||
import { PopupViewCacheService } from "../../platform/popup/view-cache/popup-view-cache.service";
|
||||
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
|
||||
import { BrowserCryptoService } from "../../platform/services/browser-crypto.service";
|
||||
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
|
||||
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
|
||||
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
|
||||
import { ForegroundBrowserBiometricsService } from "../../platform/services/foreground-browser-biometrics";
|
||||
import I18nService from "../../platform/services/i18n.service";
|
||||
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
|
||||
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
|
||||
@@ -215,6 +219,7 @@ const safeProviders: SafeProvider[] = [
|
||||
accountService: AccountServiceAbstraction,
|
||||
stateProvider: StateProvider,
|
||||
biometricStateService: BiometricStateService,
|
||||
biometricsService: BiometricsService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
) => {
|
||||
const cryptoService = new BrowserCryptoService(
|
||||
@@ -229,6 +234,7 @@ const safeProviders: SafeProvider[] = [
|
||||
accountService,
|
||||
stateProvider,
|
||||
biometricStateService,
|
||||
biometricsService,
|
||||
kdfConfigService,
|
||||
);
|
||||
new ContainerService(cryptoService, encryptService).attachToGlobal(self);
|
||||
@@ -246,6 +252,7 @@ const safeProviders: SafeProvider[] = [
|
||||
AccountServiceAbstraction,
|
||||
StateProvider,
|
||||
BiometricStateService,
|
||||
BiometricsService,
|
||||
KdfConfigService,
|
||||
],
|
||||
}),
|
||||
@@ -270,22 +277,19 @@ const safeProviders: SafeProvider[] = [
|
||||
(clipboardValue: string, clearMs: number) => {
|
||||
void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs });
|
||||
},
|
||||
async () => {
|
||||
const response = await BrowserApi.sendMessageWithResponse<{
|
||||
result: boolean;
|
||||
error: string;
|
||||
}>("biometricUnlock");
|
||||
if (!response.result) {
|
||||
throw response.error;
|
||||
}
|
||||
return response.result;
|
||||
},
|
||||
window,
|
||||
offscreenDocumentService,
|
||||
);
|
||||
},
|
||||
deps: [ToastService, OffscreenDocumentService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BiometricsService,
|
||||
useFactory: () => {
|
||||
return new ForegroundBrowserBiometricsService();
|
||||
},
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SyncService,
|
||||
useFactory: getBgService<SyncService>("syncService"),
|
||||
@@ -305,6 +309,11 @@ const safeProviders: SafeProvider[] = [
|
||||
provide: AutofillServiceAbstraction,
|
||||
useExisting: AutofillService,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ViewCacheService,
|
||||
useExisting: PopupViewCacheService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AutofillService,
|
||||
deps: [
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { CipherFormConfig, CipherFormConfigService, CipherFormMode } from "@bitwarden/vault";
|
||||
import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info";
|
||||
import {
|
||||
CipherFormConfig,
|
||||
CipherFormConfigService,
|
||||
CipherFormMode,
|
||||
OptionalInitialValues,
|
||||
} from "@bitwarden/vault";
|
||||
|
||||
import { BrowserFido2UserInterfaceSession } from "../../../../../autofill/fido2/services/browser-fido2-user-interface.service";
|
||||
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
|
||||
@@ -25,6 +33,8 @@ jest.mock("qrcode-parser", () => {});
|
||||
describe("AddEditV2Component", () => {
|
||||
let component: AddEditV2Component;
|
||||
let fixture: ComponentFixture<AddEditV2Component>;
|
||||
let addEditCipherInfo$: BehaviorSubject<AddEditCipherInfo | null>;
|
||||
let cipherServiceMock: MockProxy<CipherService>;
|
||||
|
||||
const buildConfigResponse = { originalCipher: {} } as CipherFormConfig;
|
||||
const buildConfig = jest.fn((mode: CipherFormMode) =>
|
||||
@@ -41,6 +51,10 @@ describe("AddEditV2Component", () => {
|
||||
navigate.mockClear();
|
||||
back.mockClear();
|
||||
|
||||
addEditCipherInfo$ = new BehaviorSubject(null);
|
||||
cipherServiceMock = mock<CipherService>();
|
||||
cipherServiceMock.addEditCipherInfo$ = addEditCipherInfo$.asObservable();
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [AddEditV2Component],
|
||||
providers: [
|
||||
@@ -51,6 +65,7 @@ describe("AddEditV2Component", () => {
|
||||
{ provide: Router, useValue: { navigate } },
|
||||
{ provide: ActivatedRoute, useValue: { queryParams: queryParams$ } },
|
||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
||||
{ provide: CipherService, useValue: cipherServiceMock },
|
||||
],
|
||||
})
|
||||
.overrideProvider(CipherFormConfigService, {
|
||||
@@ -107,6 +122,72 @@ describe("AddEditV2Component", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("addEditCipherInfo initialization", () => {
|
||||
it("populates config.initialValues with `addEditCipherInfo` values", fakeAsync(() => {
|
||||
const addEditCipherInfo = {
|
||||
cipher: {
|
||||
name: "test",
|
||||
folderId: "folder1",
|
||||
organizationId: "org1",
|
||||
type: CipherType.Login,
|
||||
login: {
|
||||
password: "password",
|
||||
username: "username",
|
||||
uris: [{ uri: "https://example.com" }],
|
||||
},
|
||||
},
|
||||
collectionIds: ["col1", "col2"],
|
||||
} as AddEditCipherInfo;
|
||||
addEditCipherInfo$.next(addEditCipherInfo);
|
||||
queryParams$.next({});
|
||||
|
||||
tick();
|
||||
|
||||
expect(component.config.initialValues).toEqual({
|
||||
name: "test",
|
||||
folderId: "folder1",
|
||||
organizationId: "org1",
|
||||
password: "password",
|
||||
username: "username",
|
||||
loginUri: "https://example.com",
|
||||
collectionIds: ["col1", "col2"],
|
||||
} as OptionalInitialValues);
|
||||
}));
|
||||
|
||||
it("populates config.initialValues.username when `addEditCipherInfo` is an Identity", fakeAsync(() => {
|
||||
addEditCipherInfo$.next({
|
||||
cipher: { type: CipherType.Identity, identity: { username: "identity-username" } },
|
||||
} as AddEditCipherInfo);
|
||||
queryParams$.next({});
|
||||
|
||||
tick();
|
||||
|
||||
expect(component.config.initialValues.username).toBe("identity-username");
|
||||
}));
|
||||
|
||||
it("overrides query params with `addEditCipherInfo` values", fakeAsync(() => {
|
||||
addEditCipherInfo$.next({
|
||||
cipher: { name: "AddEditCipherName" },
|
||||
} as AddEditCipherInfo);
|
||||
queryParams$.next({
|
||||
name: "QueryParamName",
|
||||
});
|
||||
|
||||
tick();
|
||||
|
||||
expect(component.config.initialValues.name).toBe("AddEditCipherName");
|
||||
}));
|
||||
|
||||
it("clears `addEditCipherInfo` after initialization", fakeAsync(() => {
|
||||
addEditCipherInfo$.next({ cipher: { name: "test" } } as AddEditCipherInfo);
|
||||
queryParams$.next({});
|
||||
|
||||
tick();
|
||||
|
||||
expect(cipherServiceMock.setAddEditCipherInfo).toHaveBeenCalledTimes(1);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("onCipherSaved", () => {
|
||||
it("disables warning when in popout", async () => {
|
||||
jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValueOnce(true);
|
||||
|
||||
@@ -8,8 +8,10 @@ import { firstValueFrom, map, switchMap } from "rxjs";
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info";
|
||||
import { AsyncActionsModule, ButtonModule, SearchModule } from "@bitwarden/components";
|
||||
import {
|
||||
CipherFormConfig,
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
CipherFormMode,
|
||||
CipherFormModule,
|
||||
DefaultCipherFormConfigService,
|
||||
OptionalInitialValues,
|
||||
TotpCaptureService,
|
||||
} from "@bitwarden/vault";
|
||||
|
||||
@@ -156,6 +159,7 @@ export class AddEditV2Component implements OnInit {
|
||||
private popupCloseWarningService: PopupCloseWarningService,
|
||||
private popupRouterCacheService: PopupRouterCacheService,
|
||||
private router: Router,
|
||||
private cipherService: CipherService,
|
||||
) {
|
||||
this.subscribeToParams();
|
||||
}
|
||||
@@ -255,7 +259,21 @@ export class AddEditV2Component implements OnInit {
|
||||
config.mode = "partial-edit";
|
||||
}
|
||||
|
||||
this.setInitialValuesFromParams(params, config);
|
||||
config.initialValues = this.setInitialValuesFromParams(params);
|
||||
|
||||
// The browser notification bar and overlay use addEditCipherInfo$ to pass modified cipher details to the form
|
||||
// Attempt to fetch them here and overwrite the initialValues if present
|
||||
const cachedCipherInfo = await firstValueFrom(this.cipherService.addEditCipherInfo$);
|
||||
|
||||
if (cachedCipherInfo != null) {
|
||||
// Cached cipher info has priority over queryParams
|
||||
config.initialValues = {
|
||||
...config.initialValues,
|
||||
...mapAddEditCipherInfoToInitialValues(cachedCipherInfo),
|
||||
};
|
||||
// Be sure to clear the "cached" cipher info, so it doesn't get used again
|
||||
await this.cipherService.setAddEditCipherInfo(null);
|
||||
}
|
||||
|
||||
return config;
|
||||
}),
|
||||
@@ -266,26 +284,27 @@ export class AddEditV2Component implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
setInitialValuesFromParams(params: QueryParams, config: CipherFormConfig) {
|
||||
config.initialValues = {};
|
||||
setInitialValuesFromParams(params: QueryParams) {
|
||||
const initialValues = {} as OptionalInitialValues;
|
||||
if (params.folderId) {
|
||||
config.initialValues.folderId = params.folderId;
|
||||
initialValues.folderId = params.folderId;
|
||||
}
|
||||
if (params.organizationId) {
|
||||
config.initialValues.organizationId = params.organizationId;
|
||||
initialValues.organizationId = params.organizationId;
|
||||
}
|
||||
if (params.collectionId) {
|
||||
config.initialValues.collectionIds = [params.collectionId];
|
||||
initialValues.collectionIds = [params.collectionId];
|
||||
}
|
||||
if (params.uri) {
|
||||
config.initialValues.loginUri = params.uri;
|
||||
initialValues.loginUri = params.uri;
|
||||
}
|
||||
if (params.username) {
|
||||
config.initialValues.username = params.username;
|
||||
initialValues.username = params.username;
|
||||
}
|
||||
if (params.name) {
|
||||
config.initialValues.name = params.name;
|
||||
initialValues.name = params.name;
|
||||
}
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
setHeader(mode: CipherFormMode, type: CipherType) {
|
||||
@@ -303,3 +322,63 @@ export class AddEditV2Component implements OnInit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to map the old AddEditCipherInfo to the new OptionalInitialValues type used by the CipherForm
|
||||
* @param cipherInfo
|
||||
*/
|
||||
const mapAddEditCipherInfoToInitialValues = (
|
||||
cipherInfo: AddEditCipherInfo | null,
|
||||
): OptionalInitialValues => {
|
||||
const initialValues: OptionalInitialValues = {};
|
||||
|
||||
if (cipherInfo == null) {
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
if (cipherInfo.collectionIds != null) {
|
||||
initialValues.collectionIds = cipherInfo.collectionIds as CollectionId[];
|
||||
}
|
||||
|
||||
if (cipherInfo.cipher == null) {
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
const cipher = cipherInfo.cipher;
|
||||
|
||||
if (cipher.folderId != null) {
|
||||
initialValues.folderId = cipher.folderId;
|
||||
}
|
||||
|
||||
if (cipher.organizationId != null) {
|
||||
initialValues.organizationId = cipher.organizationId as OrganizationId;
|
||||
}
|
||||
|
||||
if (cipher.name != null) {
|
||||
initialValues.name = cipher.name;
|
||||
}
|
||||
|
||||
if (cipher.type === CipherType.Login) {
|
||||
const login = cipher.login;
|
||||
|
||||
if (login != null) {
|
||||
if (login.uris != null && login.uris.length > 0) {
|
||||
initialValues.loginUri = login.uris[0].uri;
|
||||
}
|
||||
|
||||
if (login.username != null) {
|
||||
initialValues.username = login.username;
|
||||
}
|
||||
|
||||
if (login.password != null) {
|
||||
initialValues.password = login.password;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cipher.type === CipherType.Identity && cipher.identity?.username != null) {
|
||||
initialValues.username = cipher.identity.username;
|
||||
}
|
||||
|
||||
return initialValues;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/cli",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.8.2",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
|
||||
@@ -342,7 +342,7 @@ export class LoginCommand {
|
||||
}
|
||||
}
|
||||
|
||||
return await this.handleSuccessResponse();
|
||||
return await this.handleSuccessResponse(response);
|
||||
} catch (e) {
|
||||
return Response.error(e);
|
||||
}
|
||||
@@ -353,8 +353,8 @@ export class LoginCommand {
|
||||
process.env.BW_SESSION = Utils.fromBufferToB64(key);
|
||||
}
|
||||
|
||||
private async handleSuccessResponse(): Promise<Response> {
|
||||
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||
private async handleSuccessResponse(response: AuthResult): Promise<Response> {
|
||||
const usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector(response.userId);
|
||||
|
||||
if (
|
||||
(this.options.sso != null || this.options.apikey != null) &&
|
||||
|
||||
@@ -73,6 +73,7 @@ export class UnlockCommand {
|
||||
|
||||
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
||||
const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand(
|
||||
userId,
|
||||
this.keyConnectorService,
|
||||
this.environmentService,
|
||||
this.syncService,
|
||||
|
||||
@@ -116,20 +116,30 @@ export abstract class BaseProgram {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exist if no user is authenticated
|
||||
* @returns the userId of the active account
|
||||
*/
|
||||
protected async exitIfNotAuthed() {
|
||||
const authed = await this.serviceContainer.stateService.getIsAuthenticated();
|
||||
if (!authed) {
|
||||
this.processResponse(Response.error("You are not logged in."), true);
|
||||
const fail = () => this.processResponse(Response.error("You are not logged in."), true);
|
||||
const userId = (await firstValueFrom(this.serviceContainer.accountService.activeAccount$))?.id;
|
||||
if (!userId) {
|
||||
fail();
|
||||
}
|
||||
const authed = await this.serviceContainer.stateService.getIsAuthenticated({ userId });
|
||||
if (!authed) {
|
||||
fail();
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
protected async exitIfLocked() {
|
||||
await this.exitIfNotAuthed();
|
||||
const userId = await this.exitIfNotAuthed();
|
||||
if (await this.serviceContainer.cryptoService.hasUserKey()) {
|
||||
return;
|
||||
} else if (process.env.BW_NOINTERACTION !== "true") {
|
||||
// must unlock
|
||||
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) {
|
||||
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector(userId)) {
|
||||
const response = Response.error(
|
||||
"Your vault is locked. You must unlock your vault using your session key.\n" +
|
||||
"If you do not have your session key, you can get a new one by logging out and logging in again.",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
EnvironmentService,
|
||||
Region,
|
||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
import { Response } from "../models/response";
|
||||
@@ -14,6 +15,7 @@ import { MessageResponse } from "../models/response/message.response";
|
||||
|
||||
export class ConvertToKeyConnectorCommand {
|
||||
constructor(
|
||||
private readonly userId: UserId,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private environmentService: EnvironmentService,
|
||||
private syncService: SyncService,
|
||||
@@ -68,7 +70,7 @@ export class ConvertToKeyConnectorCommand {
|
||||
}
|
||||
|
||||
await this.keyConnectorService.removeConvertAccountRequired();
|
||||
await this.keyConnectorService.setUsesKeyConnector(true);
|
||||
await this.keyConnectorService.setUsesKeyConnector(true, this.userId);
|
||||
|
||||
// Update environment URL - required for api key login
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
|
||||
@@ -131,26 +131,6 @@ export class CliPlatformUtilsService implements PlatformUtilsService {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
|
||||
supportsBiometric(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
authenticateBiometric(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
biometricsNeedsSetup(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
biometricsSetup(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
supportsSecureStorage(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -206,9 +206,9 @@ export class Program extends BaseProgram {
|
||||
writeLn("", true);
|
||||
})
|
||||
.action(async (cmd) => {
|
||||
await this.exitIfNotAuthed();
|
||||
const userId = await this.exitIfNotAuthed();
|
||||
|
||||
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector()) {
|
||||
if (await this.serviceContainer.keyConnectorService.getUsesKeyConnector(userId)) {
|
||||
const logoutCommand = new LogoutCommand(
|
||||
this.serviceContainer.authService,
|
||||
this.serviceContainer.i18nService,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"**/node_modules/argon2/package.json",
|
||||
"**/node_modules/argon2/build/Release/argon2.node"
|
||||
],
|
||||
"electronVersion": "31.4.0",
|
||||
"electronVersion": "32.0.1",
|
||||
"generateUpdatesFilesForAllChannels": true,
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.8.1",
|
||||
"version": "2024.8.2",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
|
||||
@@ -20,6 +20,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { KeySuffixOptions, ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
@@ -133,6 +134,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
private userVerificationService: UserVerificationServiceAbstraction,
|
||||
private desktopSettingsService: DesktopSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private biometricsService: BiometricsService,
|
||||
private desktopAutofillSettingsService: DesktopAutofillSettingsService,
|
||||
private pinService: PinServiceAbstraction,
|
||||
private logService: LogService,
|
||||
@@ -287,7 +289,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
// Non-form values
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.supportsBiometric = await this.biometricsService.supportsBiometric();
|
||||
this.previousVaultTimeout = this.form.value.vaultTimeout;
|
||||
|
||||
this.refreshTimeoutSettings$
|
||||
@@ -466,13 +468,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const needsSetup = await this.platformUtilsService.biometricsNeedsSetup();
|
||||
const supportsBiometricAutoSetup =
|
||||
await this.platformUtilsService.biometricsSupportsAutoSetup();
|
||||
const needsSetup = await this.biometricsService.biometricsNeedsSetup();
|
||||
const supportsBiometricAutoSetup = await this.biometricsService.biometricsSupportsAutoSetup();
|
||||
|
||||
if (needsSetup) {
|
||||
if (supportsBiometricAutoSetup) {
|
||||
await this.platformUtilsService.biometricsSetup();
|
||||
await this.biometricsService.biometricsSetup();
|
||||
} else {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "biometricsManualSetupTitle" },
|
||||
|
||||
@@ -56,6 +56,7 @@ import { StateService as StateServiceAbstraction } from "@bitwarden/common/platf
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging";
|
||||
// eslint-disable-next-line no-restricted-imports -- Used for dependency injection
|
||||
import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal";
|
||||
@@ -72,6 +73,7 @@ import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legac
|
||||
|
||||
import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service";
|
||||
import { DesktopSettingsService } from "../../platform/services/desktop-settings.service";
|
||||
import { ElectronBiometricsService } from "../../platform/services/electron-biometrics.service";
|
||||
import { ElectronCryptoService } from "../../platform/services/electron-crypto.service";
|
||||
import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service";
|
||||
import {
|
||||
@@ -104,6 +106,11 @@ const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
*/
|
||||
const safeProviders: SafeProvider[] = [
|
||||
safeProvider(InitService),
|
||||
safeProvider({
|
||||
provide: BiometricsService,
|
||||
useClass: ElectronBiometricsService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider(NativeMessagingService),
|
||||
safeProvider(SearchBarService),
|
||||
safeProvider(DialogService),
|
||||
|
||||
@@ -28,6 +28,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService as AbstractBiometricService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
@@ -35,6 +36,8 @@ import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { BiometricsService } from "src/platform/main/biometric";
|
||||
|
||||
import { LockComponent } from "./lock.component";
|
||||
|
||||
// ipc mock global
|
||||
@@ -53,6 +56,7 @@ describe("LockComponent", () => {
|
||||
let fixture: ComponentFixture<LockComponent>;
|
||||
let stateServiceMock: MockProxy<StateService>;
|
||||
let biometricStateService: MockProxy<BiometricStateService>;
|
||||
let biometricsService: MockProxy<BiometricsService>;
|
||||
let messagingServiceMock: MockProxy<MessagingService>;
|
||||
let broadcasterServiceMock: MockProxy<BroadcasterService>;
|
||||
let platformUtilsServiceMock: MockProxy<PlatformUtilsService>;
|
||||
@@ -163,6 +167,10 @@ describe("LockComponent", () => {
|
||||
provide: BiometricStateService,
|
||||
useValue: biometricStateService,
|
||||
},
|
||||
{
|
||||
provide: AbstractBiometricService,
|
||||
useValue: biometricsService,
|
||||
},
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
|
||||
@@ -25,6 +25,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
@@ -66,6 +67,7 @@ export class LockComponent extends BaseLockComponent implements OnInit, OnDestro
|
||||
userVerificationService: UserVerificationService,
|
||||
pinService: PinServiceAbstraction,
|
||||
biometricStateService: BiometricStateService,
|
||||
biometricsService: BiometricsService,
|
||||
accountService: AccountService,
|
||||
authService: AuthService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
@@ -93,6 +95,7 @@ export class LockComponent extends BaseLockComponent implements OnInit, OnDestro
|
||||
userVerificationService,
|
||||
pinService,
|
||||
biometricStateService,
|
||||
biometricsService,
|
||||
accountService,
|
||||
authService,
|
||||
kdfConfigService,
|
||||
@@ -139,7 +142,7 @@ export class LockComponent extends BaseLockComponent implements OnInit, OnDestro
|
||||
|
||||
// start background listener until destroyed on interval
|
||||
this.timerId = setInterval(async () => {
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.supportsBiometric = await this.biometricsService.supportsBiometric();
|
||||
this.biometricReady = await this.canUseBiometric();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import { PowerMonitorMain } from "./main/power-monitor.main";
|
||||
import { TrayMain } from "./main/tray.main";
|
||||
import { UpdaterMain } from "./main/updater.main";
|
||||
import { WindowMain } from "./main/window.main";
|
||||
import { BiometricsService, BiometricsServiceAbstraction } from "./platform/main/biometric/index";
|
||||
import { BiometricsService, DesktopBiometricsService } from "./platform/main/biometric/index";
|
||||
import { ClipboardMain } from "./platform/main/clipboard.main";
|
||||
import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener";
|
||||
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
|
||||
@@ -64,7 +64,7 @@ export class Main {
|
||||
menuMain: MenuMain;
|
||||
powerMonitorMain: PowerMonitorMain;
|
||||
trayMain: TrayMain;
|
||||
biometricsService: BiometricsServiceAbstraction;
|
||||
biometricsService: DesktopBiometricsService;
|
||||
nativeMessagingMain: NativeMessagingMain;
|
||||
clipboardMain: ClipboardMain;
|
||||
desktopAutofillSettingsService: DesktopAutofillSettingsService;
|
||||
|
||||
4
apps/desktop/src/package-lock.json
generated
4
apps/desktop/src/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "2024.8.1",
|
||||
"version": "2024.8.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@bitwarden/desktop",
|
||||
"version": "2024.8.1",
|
||||
"version": "2024.8.2",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@bitwarden/desktop-napi": "file:../desktop_native",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@bitwarden/desktop",
|
||||
"productName": "Bitwarden",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.8.1",
|
||||
"version": "2024.8.2",
|
||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||
"homepage": "https://bitwarden.com",
|
||||
"license": "GPL-3.0",
|
||||
|
||||
@@ -3,7 +3,7 @@ import { systemPreferences } from "electron";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { passwords } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
export default class BiometricDarwinMain implements OsBiometricService {
|
||||
constructor(private i18nservice: I18nService) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
export default class NoopBiometricsService implements OsBiometricService {
|
||||
constructor() {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { biometrics, passwords } from "@bitwarden/desktop-napi";
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
import { isFlatpak, isLinux, isSnapStore } from "../../../utils";
|
||||
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
const polkitPolicy = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
|
||||
@@ -6,7 +6,7 @@ import { biometrics, passwords } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
const KEY_WITNESS_SUFFIX = "_witness";
|
||||
const WITNESS_VALUE = "known key";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { WindowMain } from "../../../main/window.main";
|
||||
import BiometricDarwinMain from "./biometric.darwin.main";
|
||||
import BiometricWindowsMain from "./biometric.windows.main";
|
||||
import { BiometricsService } from "./biometrics.service";
|
||||
import { OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
jest.mock("@bitwarden/desktop-napi", () => {
|
||||
return {
|
||||
|
||||
@@ -6,9 +6,9 @@ import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { WindowMain } from "../../../main/window.main";
|
||||
|
||||
import { BiometricsServiceAbstraction, OsBiometricService } from "./biometrics.service.abstraction";
|
||||
import { DesktopBiometricsService, OsBiometricService } from "./desktop.biometrics.service";
|
||||
|
||||
export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
export class BiometricsService extends DesktopBiometricsService {
|
||||
private platformSpecificService: OsBiometricService;
|
||||
private clientKeyHalves = new Map<string, string>();
|
||||
|
||||
@@ -20,6 +20,7 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
private platform: NodeJS.Platform,
|
||||
private biometricStateService: BiometricStateService,
|
||||
) {
|
||||
super();
|
||||
this.loadPlatformSpecificService(this.platform);
|
||||
}
|
||||
|
||||
@@ -63,19 +64,19 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
this.platformSpecificService = new NoopBiometricsService();
|
||||
}
|
||||
|
||||
async osSupportsBiometric() {
|
||||
async supportsBiometric() {
|
||||
return await this.platformSpecificService.osSupportsBiometric();
|
||||
}
|
||||
|
||||
async osBiometricsNeedsSetup() {
|
||||
async biometricsNeedsSetup() {
|
||||
return await this.platformSpecificService.osBiometricsNeedsSetup();
|
||||
}
|
||||
|
||||
async osBiometricsCanAutoSetup() {
|
||||
async biometricsSupportsAutoSetup() {
|
||||
return await this.platformSpecificService.osBiometricsCanAutoSetup();
|
||||
}
|
||||
|
||||
async osBiometricsSetup() {
|
||||
async biometricsSetup() {
|
||||
await this.platformSpecificService.osBiometricsSetup();
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
const requireClientKeyHalf = await this.biometricStateService.getRequirePasswordOnStart(userId);
|
||||
const clientKeyHalfB64 = this.getClientKeyHalf(service, key);
|
||||
const clientKeyHalfSatisfied = !requireClientKeyHalf || !!clientKeyHalfB64;
|
||||
return clientKeyHalfSatisfied && (await this.osSupportsBiometric());
|
||||
return clientKeyHalfSatisfied && (await this.supportsBiometric());
|
||||
}
|
||||
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
@@ -110,6 +111,10 @@ export class BiometricsService implements BiometricsServiceAbstraction {
|
||||
return result;
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
return await this.platformSpecificService.osSupportsBiometric();
|
||||
}
|
||||
|
||||
async getBiometricKey(service: string, storageKey: string): Promise<string | null> {
|
||||
return await this.interruptProcessReload(async () => {
|
||||
await this.enforceClientKeyHalf(service, storageKey);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
export abstract class BiometricsServiceAbstraction {
|
||||
abstract osSupportsBiometric(): Promise<boolean>;
|
||||
abstract osBiometricsNeedsSetup: () => Promise<boolean>;
|
||||
abstract osBiometricsCanAutoSetup: () => Promise<boolean>;
|
||||
abstract osBiometricsSetup: () => Promise<void>;
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
|
||||
/**
|
||||
* This service extends the base biometrics service to provide desktop specific functions,
|
||||
* specifically for the main process.
|
||||
*/
|
||||
export abstract class DesktopBiometricsService extends BiometricsService {
|
||||
abstract canAuthBiometric({
|
||||
service,
|
||||
key,
|
||||
@@ -12,7 +14,6 @@ export abstract class BiometricsServiceAbstraction {
|
||||
key: string;
|
||||
userId: string;
|
||||
}): Promise<boolean>;
|
||||
abstract authenticateBiometric(): Promise<boolean>;
|
||||
abstract getBiometricKey(service: string, key: string): Promise<string | null>;
|
||||
abstract setBiometricKey(service: string, key: string, value: string): Promise<void>;
|
||||
abstract setEncryptionKeyHalf({
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from "./biometrics.service.abstraction";
|
||||
export * from "./desktop.biometrics.service";
|
||||
export * from "./biometrics.service";
|
||||
|
||||
@@ -6,14 +6,14 @@ import { passwords } from "@bitwarden/desktop-napi";
|
||||
|
||||
import { BiometricMessage, BiometricAction } from "../../types/biometric-message";
|
||||
|
||||
import { BiometricsServiceAbstraction } from "./biometric/index";
|
||||
import { DesktopBiometricsService } from "./biometric/index";
|
||||
|
||||
const AuthRequiredSuffix = "_biometric";
|
||||
|
||||
export class DesktopCredentialStorageListener {
|
||||
constructor(
|
||||
private serviceName: string,
|
||||
private biometricService: BiometricsServiceAbstraction,
|
||||
private biometricService: DesktopBiometricsService,
|
||||
private logService: ConsoleLogService,
|
||||
) {}
|
||||
|
||||
@@ -77,16 +77,16 @@ export class DesktopCredentialStorageListener {
|
||||
});
|
||||
break;
|
||||
case BiometricAction.OsSupported:
|
||||
val = await this.biometricService.osSupportsBiometric();
|
||||
val = await this.biometricService.supportsBiometric();
|
||||
break;
|
||||
case BiometricAction.NeedsSetup:
|
||||
val = await this.biometricService.osBiometricsNeedsSetup();
|
||||
val = await this.biometricService.biometricsNeedsSetup();
|
||||
break;
|
||||
case BiometricAction.Setup:
|
||||
await this.biometricService.osBiometricsSetup();
|
||||
await this.biometricService.biometricsSetup();
|
||||
break;
|
||||
case BiometricAction.CanAutoSetup:
|
||||
val = await this.biometricService.osBiometricsCanAutoSetup();
|
||||
val = await this.biometricService.biometricsSupportsAutoSetup();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
|
||||
/**
|
||||
* This service implement the base biometrics service to provide desktop specific functions,
|
||||
* specifically for the renderer process by passing messages to the main process.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ElectronBiometricsService extends BiometricsService {
|
||||
async supportsBiometric(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.osSupported();
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.osSupported();
|
||||
}
|
||||
|
||||
/** This method is used to authenticate the user presence _only_.
|
||||
* It should not be used in the process to retrieve
|
||||
* biometric keys, which has a separate authentication mechanism.
|
||||
* For biometric keys, invoke "keytar" with a biometric key suffix */
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.authenticate();
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsNeedsSetup();
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsCanAutoSetup();
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {
|
||||
return await ipc.platform.biometric.biometricsSetup();
|
||||
}
|
||||
}
|
||||
@@ -131,30 +131,6 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
||||
return ipc.platform.clipboard.read();
|
||||
}
|
||||
|
||||
async supportsBiometric(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.osSupported();
|
||||
}
|
||||
|
||||
async biometricsNeedsSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsNeedsSetup();
|
||||
}
|
||||
|
||||
async biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.biometricsCanAutoSetup();
|
||||
}
|
||||
|
||||
async biometricsSetup(): Promise<void> {
|
||||
return await ipc.platform.biometric.biometricsSetup();
|
||||
}
|
||||
|
||||
/** This method is used to authenticate the user presence _only_.
|
||||
* It should not be used in the process to retrieve
|
||||
* biometric keys, which has a separate authentication mechanism.
|
||||
* For biometric keys, invoke "keytar" with a biometric key suffix */
|
||||
async authenticateBiometric(): Promise<boolean> {
|
||||
return await ipc.platform.biometric.authenticate();
|
||||
}
|
||||
|
||||
supportsSecureStorage(): boolean {
|
||||
return ELECTRON_SUPPORTS_SECURE_STORAGE;
|
||||
}
|
||||
|
||||
@@ -439,92 +439,6 @@ app-root > #loading,
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.callout {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #000000;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
@include themify($themes) {
|
||||
border-color: themed("calloutBorderColor");
|
||||
background-color: themed("calloutBackgroundColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3.callout-heading {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.callout-primary {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("primaryColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("primaryColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("infoColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("infoColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("dangerColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("dangerColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-success {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("successColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("successColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-warning {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("warningColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("warningColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 40px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.password-reprompt {
|
||||
text-align: left;
|
||||
margin-top: 15px;
|
||||
|
||||
@@ -8,8 +8,8 @@ import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/c
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
@@ -33,11 +33,11 @@ export class NativeMessagingService {
|
||||
constructor(
|
||||
private cryptoFunctionService: CryptoFunctionService,
|
||||
private cryptoService: CryptoService,
|
||||
private platformUtilService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private messagingService: MessagingService,
|
||||
private desktopSettingService: DesktopSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private biometricsService: BiometricsService,
|
||||
private nativeMessageHandler: NativeMessageHandlerService,
|
||||
private dialogService: DialogService,
|
||||
private accountService: AccountService,
|
||||
@@ -133,7 +133,14 @@ export class NativeMessagingService {
|
||||
|
||||
switch (message.command) {
|
||||
case "biometricUnlock": {
|
||||
if (!(await this.platformUtilService.supportsBiometric())) {
|
||||
const isTemporarilyDisabled =
|
||||
(await this.biometricStateService.getBiometricUnlockEnabled(message.userId as UserId)) &&
|
||||
!(await this.biometricsService.supportsBiometric());
|
||||
if (isTemporarilyDisabled) {
|
||||
return this.send({ command: "biometricUnlock", response: "not available" }, appId);
|
||||
}
|
||||
|
||||
if (!(await this.biometricsService.supportsBiometric())) {
|
||||
return this.send({ command: "biometricUnlock", response: "not supported" }, appId);
|
||||
}
|
||||
|
||||
@@ -198,8 +205,18 @@ export class NativeMessagingService {
|
||||
|
||||
break;
|
||||
}
|
||||
case "biometricUnlockAvailable": {
|
||||
const isAvailable = await this.biometricsService.supportsBiometric();
|
||||
return this.send(
|
||||
{
|
||||
command: "biometricUnlockAvailable",
|
||||
response: isAvailable ? "available" : "not available",
|
||||
},
|
||||
appId,
|
||||
);
|
||||
}
|
||||
default:
|
||||
this.logService.error("NativeMessage, got unknown command.");
|
||||
this.logService.error("NativeMessage, got unknown command: " + message.command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/web-vault",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.8.1",
|
||||
"scripts": {
|
||||
"build:oss": "webpack",
|
||||
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="tw-h-full">
|
||||
<html lang="en" class="tw-h-full theme_light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
@@ -16,9 +16,9 @@
|
||||
</head>
|
||||
|
||||
<body class="tw-min-h-screen !tw-min-w-0 tw-text-center tw-bg-background-alt tw-flex tw-flex-col">
|
||||
<main class="tw-max-w-3xl tw-mx-auto tw-mb-8 tw-px-2">
|
||||
<img src="images/logo.svg" width="200px" class="tw-py-16" alt="Bitwarden" />
|
||||
<img class="new-logo-themed tw-m-8" alt="Bitwarden" />
|
||||
|
||||
<main class="tw-max-w-3xl tw-mx-auto tw-px-2 tw-my-4">
|
||||
<h1 class="tw-mb-0 tw-h1">Sorry, this page isn't available.</h1>
|
||||
|
||||
<p class="tw-py-9 tw-mb-0">
|
||||
|
||||
@@ -28,6 +28,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
import { flagEnabled } from "../../../utils/flags";
|
||||
@@ -129,7 +130,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
async goAfterLogIn() {
|
||||
async goAfterLogIn(userId: UserId) {
|
||||
const masterPassword = this.formGroup.value.masterPassword;
|
||||
|
||||
// Check master password against policy
|
||||
@@ -150,7 +151,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
|
||||
) {
|
||||
const policiesData: { [id: string]: PolicyData } = {};
|
||||
this.policies.map((p) => (policiesData[p.id] = PolicyData.fromPolicy(p)));
|
||||
await this.policyService.replace(policiesData);
|
||||
await this.policyService.replace(policiesData, userId);
|
||||
await this.router.navigate(["update-password"]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
bitIconButton="bwi-clone"
|
||||
bitSuffix
|
||||
type="button"
|
||||
showToast
|
||||
[valueLabel]="'billingSyncKey' | i18n"
|
||||
[appCopyClick]="clientSecret"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
></button>
|
||||
|
||||
@@ -186,20 +186,6 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
throw new Error("Cannot read from clipboard on web.");
|
||||
}
|
||||
|
||||
supportsBiometric() {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
authenticateBiometric() {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
biometricsNeedsSetup: () => Promise<boolean>;
|
||||
biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
biometricsSetup: () => Promise<void>;
|
||||
|
||||
supportsSecureStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,10 @@ export class PreferencesComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const values = this.form.value;
|
||||
|
||||
// must get raw value b/c the vault timeout action is disabled when a policy is applied
|
||||
// which removes the timeout action property and value from the normal form.value.
|
||||
const values = this.form.getRawValue();
|
||||
|
||||
const activeAcct = await firstValueFrom(this.accountService.activeAccount$);
|
||||
|
||||
|
||||
6
apps/web/src/images/logo-white.svg
Normal file
6
apps/web/src/images/logo-white.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 290 45" fill="#FFF" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M69.799 10.713c3.325 0 5.911 1.248 7.811 3.848 1.9 2.549 2.85 6.033 2.85 10.453 0 4.576-.95 8.113-2.902 10.61-1.953 2.547-4.592 3.743-7.918 3.743-3.325 0-5.858-1.144-7.758-3.536h-.528l-1.003 2.444a.976.976 0 0 1-.897.572H55.23a.94.94 0 0 1-.95-.936V1.352a.94.94 0 0 1 .95-.936h5.7a.94.94 0 0 1 .95.936v8.009c0 1.144-.105 2.964-.316 5.46h.317c1.741-2.704 4.433-4.108 7.917-4.108Zm-2.428 6.084c-1.847 0-3.273.572-4.17 1.717-.844 1.144-1.32 3.068-1.32 5.668v.832c0 2.964.423 5.097 1.32 6.345.897 1.248 2.322 1.924 4.275 1.924 1.531 0 2.85-.728 3.748-2.184.897-1.404 1.372-3.537 1.372-6.189 0-2.704-.475-4.732-1.372-6.084-.95-1.352-2.27-2.029-3.853-2.029ZM93.022 38.9h-5.7a.94.94 0 0 1-.95-.936V12.221a.94.94 0 0 1 .95-.936h5.7a.94.94 0 0 1 .95.936v25.69c.053.468-.422.988-.95.988Zm20.849-5.564c1.108 0 2.428-.208 4.011-.624a.632.632 0 0 1 .792.624v4.316a.64.64 0 0 1-.37.572c-1.794.728-4.064 1.092-6.597 1.092-3.062 0-5.278-.728-6.651-2.288-1.372-1.508-2.111-3.796-2.111-6.812V16.953h-3.008c-.37 0-.634-.26-.634-.624v-2.444c0-.052.053-.104.053-.156l4.17-2.444 2.058-5.408c.106-.26.317-.417.581-.417h3.8c.369 0 .633.26.633.625v5.252h7.548c.158 0 .317.156.317.312v4.68c0 .364-.264.624-.634.624h-7.178v13.21c0 1.04.317 1.872.897 2.34.528.572 1.373.832 2.323.832Zm35.521 5.564c-.739 0-1.319-.468-1.636-1.144l-5.595-16.797c-.369-1.196-.844-3.016-1.478-5.357h-.158l-.528 1.873-1.108 3.536-5.753 16.797c-.211.676-.845 1.092-1.584 1.092a1.628 1.628 0 0 1-1.583-1.196l-7.02-24.182c-.211-.728.369-1.508 1.214-1.508h.158c.528 0 1.003.364 1.161.884l4.117 14.717c1.003 3.849 1.689 6.657 2.006 8.53h.158c.95-3.85 1.689-6.397 2.164-7.698l5.331-15.393c.211-.624.792-1.04 1.531-1.04.686 0 1.267.416 1.478 1.04l4.961 15.29c1.214 3.9 1.953 6.396 2.217 7.696h.158c.159-1.04.792-3.952 2.006-8.633l3.958-14.509c.159-.52.634-.884 1.162-.884.791 0 1.372.728 1.161 1.508l-6.651 24.182c-.211.728-.844 1.196-1.636 1.196h-.211Zm31.352 0a.962.962 0 0 1-.95-.832l-.475-3.432h-.264c-1.372 1.716-2.745 2.964-4.223 3.692-1.425.728-3.166 1.04-5.119 1.04-2.692 0-4.751-.676-6.228-2.028-1.32-1.196-2.059-2.808-2.164-4.836-.212-2.704.95-5.305 3.166-6.813 2.27-1.456 5.437-2.34 9.712-2.34l5.173-.156v-1.768c0-2.6-.528-4.473-1.637-5.773-1.108-1.3-2.744-1.924-5.067-1.924-2.216 0-4.433.52-6.756 1.612-.58.26-1.266 0-1.53-.572s0-1.248.58-1.456c2.639-1.04 5.226-1.612 7.865-1.612 3.008 0 5.225.78 6.756 2.34 1.478 1.508 2.216 3.953 2.216 7.125v16.901c-.052.312-.527.832-1.055.832Zm-10.926-1.768c2.956 0 5.226-.832 6.862-2.444 1.689-1.612 2.533-3.952 2.533-6.813v-2.6l-4.75.208c-3.853.156-6.545.78-8.234 1.768-1.636.988-2.481 2.6-2.481 4.68 0 1.665.528 3.017 1.531 3.953 1.161.78 2.639 1.248 4.539 1.248Zm31.246-25.638c.792 0 1.584.052 2.481.156a1.176 1.176 0 0 1 1.003 1.352c-.106.624-.739.988-1.372.884-.792-.104-1.584-.208-2.375-.208-2.323 0-4.223.988-5.701 2.912-1.478 1.925-2.217 4.42-2.217 7.333v13.625c0 .676-.527 1.196-1.214 1.196-.686 0-1.213-.52-1.213-1.196V13.105c0-.572.475-1.04 1.055-1.04.581 0 1.056.416 1.056.988l.211 3.848h.158c1.109-1.976 2.323-3.38 3.589-4.16 1.214-.832 2.745-1.248 4.539-1.248Zm18.579 0c1.953 0 3.695.364 5.12 1.04 1.478.676 2.745 1.924 3.853 3.64h.158a122.343 122.343 0 0 1-.158-6.084V1.612c0-.676.528-1.196 1.214-1.196.686 0 1.214.52 1.214 1.196v36.351c0 .468-.37.832-.845.832a.852.852 0 0 1-.844-.78l-.528-3.38h-.211c-2.058 3.068-5.067 4.576-8.92 4.576-3.8 0-6.598-1.144-8.656-3.484-1.953-2.34-3.008-5.668-3.008-10.089 0-4.628.95-8.165 2.955-10.66 2.006-2.237 4.856-3.485 8.656-3.485Zm0 2.236c-3.008 0-5.225 1.04-6.756 3.12-1.478 2.029-2.216 4.993-2.216 8.945 0 7.593 3.008 11.39 9.025 11.39 3.114 0 5.331-.885 6.756-2.653 1.478-1.768 2.164-4.68 2.164-8.737v-.416c0-4.16-.686-7.124-2.164-8.893-1.372-1.872-3.642-2.756-6.809-2.756Zm31.616 25.638c-3.959 0-7.02-1.196-9.289-3.64-2.217-2.392-3.326-5.772-3.326-10.089 0-4.316 1.056-7.748 3.22-10.297 2.164-2.6 5.014-3.9 8.656-3.9 3.167 0 5.753 1.092 7.548 3.276 1.9 2.184 2.797 5.2 2.797 8.997v1.976h-19.634c.052 3.692.897 6.5 2.639 8.477 1.741 1.976 4.169 2.86 7.389 2.86 1.531 0 2.956-.104 4.117-.312.844-.156 1.847-.416 3.061-.832.686-.26 1.425.26 1.425.988 0 .416-.264.832-.686.988-1.267.52-2.481.832-3.589 1.04-1.32.364-2.745.468-4.328.468Zm-.739-25.69c-2.639 0-4.75.832-6.334 2.548-1.583 1.665-2.48 4.16-2.797 7.333h16.89c0-3.068-.686-5.564-2.059-7.28-1.372-1.717-3.272-2.6-5.7-2.6ZM288.733 38.9c-.686 0-1.214-.52-1.214-1.196V21.426c0-2.704-.58-4.68-1.689-5.877-1.214-1.196-2.955-1.872-5.383-1.872-3.273 0-5.648.78-7.126 2.444-1.478 1.613-2.322 4.265-2.322 7.853V37.6c0 .676-.528 1.196-1.214 1.196-.686 0-1.214-.52-1.214-1.196V13.105c0-.624.475-1.092 1.108-1.092.581 0 1.003.416 1.109.936l.316 2.704h.159c1.794-2.808 4.908-4.212 9.448-4.212 6.175 0 9.289 3.276 9.289 9.829V37.6c-.053.727-.633 1.3-1.267 1.3ZM90.225 0c-2.48 0-4.486 1.872-4.486 4.212v.416c0 2.289 2.058 4.213 4.486 4.213s4.486-1.924 4.486-4.213v-.364C94.711 1.872 92.653 0 90.225 0Z" />
|
||||
<path
|
||||
d="M32.041 24.546V5.95H18.848v33.035c2.336-1.22 4.427-2.547 6.272-3.98 4.614-3.565 6.921-7.051 6.921-10.46Zm5.654-22.314v22.314c0 1.665-.329 3.317-.986 4.953-.658 1.637-1.473 3.09-2.445 4.359-.971 1.268-2.13 2.503-3.475 3.704-1.345 1.2-2.586 2.199-3.725 2.993a46.963 46.963 0 0 1-3.563 2.251c-1.237.707-2.116 1.187-2.636 1.439-.52.251-.938.445-1.252.58-.235.117-.49.175-.765.175s-.53-.058-.766-.174c-.314-.136-.731-.33-1.252-.581-.52-.252-1.398-.732-2.635-1.439a47.003 47.003 0 0 1-3.564-2.251c-1.138-.794-2.38-1.792-3.725-2.993-1.345-1.2-2.503-2.436-3.475-3.704-.972-1.27-1.787-2.722-2.444-4.359C.329 27.863 0 26.211 0 24.546V2.232c0-.504.187-.94.56-1.308A1.823 1.823 0 0 1 1.885.372H35.81c.511 0 .953.184 1.326.552.373.368.56.804.56 1.308Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.7 KiB |
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=1010" />
|
||||
<meta name="theme-color" content="#175DDC" />
|
||||
|
||||
<title page-title>Bitwarden Web Vault</title>
|
||||
<title page-title>Bitwarden Web vault</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="images/icons/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/favicon-32x32.png" />
|
||||
@@ -15,16 +15,14 @@
|
||||
</head>
|
||||
<body class="layout_frontend">
|
||||
<app-root>
|
||||
<div class="mt-5 d-flex justify-content-center">
|
||||
<div>
|
||||
<img class="mb-4 logo logo-themed" alt="Bitwarden" />
|
||||
<p class="text-center">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-2x text-muted"
|
||||
title="Loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</p>
|
||||
<div class="tw-p-8 tw-flex">
|
||||
<img class="new-logo-themed" alt="Bitwarden" />
|
||||
<div class="spinner-container tw-justify-center">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-3x tw-text-muted"
|
||||
title="Loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
</app-root>
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
.callout {
|
||||
border-left-width: 5px !important;
|
||||
border-radius: $card-inner-border-radius;
|
||||
margin-bottom: $alert-margin-bottom;
|
||||
padding: $alert-padding-y $alert-padding-x;
|
||||
@include themify($themes) {
|
||||
background-color: themed("calloutBackground");
|
||||
border: 1px solid themed("borderColor");
|
||||
color: themed("calloutColor");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3.callout-heading {
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&.callout-primary {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("primary");
|
||||
}
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("primary");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-info {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("info");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("info");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-danger {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("danger");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("danger");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-success {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("success");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("success");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.callout-warning {
|
||||
@include themify($themes) {
|
||||
border-left-color: themed("warning");
|
||||
}
|
||||
|
||||
.callout-heading {
|
||||
@include themify($themes) {
|
||||
color: themed("warning");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,6 @@
|
||||
|
||||
@import "./base";
|
||||
@import "./buttons";
|
||||
@import "./callouts";
|
||||
@import "./cards";
|
||||
@import "./forms";
|
||||
@import "./modals";
|
||||
|
||||
@@ -5,9 +5,14 @@
|
||||
@import "../../../../libs/components/src/tw-theme.css";
|
||||
|
||||
/*
|
||||
* Duplicated styling from Angular components.
|
||||
* Web specific global styling.
|
||||
*
|
||||
* For use in non Angular pages like the 404 and connectors.
|
||||
* Be mindful of what is added here. Generally use Tailwind classes directly in Angular components.
|
||||
*
|
||||
* Some valid scenarios for adding styles here:
|
||||
*
|
||||
* - Duplicated styling for CL components used in non Angular pages like connectors and 404.
|
||||
* - Shared styles like Logo.
|
||||
*/
|
||||
@layer components {
|
||||
.tw-h1 {
|
||||
@@ -24,4 +29,35 @@
|
||||
@apply tw-bg-transparent tw-border-text-muted hover:tw-bg-text-muted hover:tw-border-text-muted hover:!tw-text-contrast disabled:tw-bg-transparent disabled:tw-border-text-muted/60 disabled:!tw-text-muted/60 disabled:tw-cursor-not-allowed;
|
||||
@apply tw-text-muted !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading page
|
||||
*/
|
||||
body.layout_frontend {
|
||||
/* We apply the background color here since body classes are dynamically added and removed */
|
||||
@apply tw-bg-background-alt !important;
|
||||
|
||||
/* Spinner requires fixed height and width to appear centered */
|
||||
.spinner-container {
|
||||
@apply tw-fixed tw-inset-2/4 -tw-translate-x-1/2 -tw-translate-y-1/2;
|
||||
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logo, used both in loading and on "frontend" pages.
|
||||
*/
|
||||
img.new-logo-themed {
|
||||
@apply tw-block;
|
||||
|
||||
width: 128px;
|
||||
}
|
||||
.theme_light img.new-logo-themed {
|
||||
content: url("../images/logo.svg");
|
||||
}
|
||||
.theme_dark img.new-logo-themed {
|
||||
content: url("../images/logo-white.svg");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
|
||||
import { BiometricsService } from "@bitwarden/common/platform/biometrics/biometric.service";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/platform/enums";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
@@ -84,6 +85,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
protected userVerificationService: UserVerificationService,
|
||||
protected pinService: PinServiceAbstraction,
|
||||
protected biometricStateService: BiometricStateService,
|
||||
protected biometricsService: BiometricsService,
|
||||
protected accountService: AccountService,
|
||||
protected authService: AuthService,
|
||||
protected kdfConfigService: KdfConfigService,
|
||||
@@ -146,6 +148,13 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
return !!userKey;
|
||||
}
|
||||
|
||||
async isBiometricUnlockAvailable(): Promise<boolean> {
|
||||
if (!(await this.biometricsService.supportsBiometric())) {
|
||||
return false;
|
||||
}
|
||||
return this.biometricsService.isBiometricUnlockAvailable();
|
||||
}
|
||||
|
||||
togglePassword() {
|
||||
this.showPassword = !this.showPassword;
|
||||
const input = document.getElementById(this.pinEnabled ? "pin" : "masterPassword");
|
||||
@@ -327,7 +336,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.masterPasswordEnabled = await this.userVerificationService.hasMasterPassword();
|
||||
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.supportsBiometric = await this.biometricsService.supportsBiometric();
|
||||
this.biometricLock =
|
||||
(await this.vaultTimeoutSettingsService.isBiometricLockSet()) &&
|
||||
((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) ||
|
||||
|
||||
@@ -23,6 +23,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
import {
|
||||
@@ -39,7 +40,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
showPassword = false;
|
||||
formPromise: Promise<AuthResult>;
|
||||
onSuccessfulLogin: () => Promise<any>;
|
||||
onSuccessfulLoginNavigate: () => Promise<any>;
|
||||
onSuccessfulLoginNavigate: (userId: UserId) => Promise<any>;
|
||||
onSuccessfulLoginTwoFactorNavigate: () => Promise<any>;
|
||||
onSuccessfulLoginForceResetNavigate: () => Promise<any>;
|
||||
showLoginWithDevice: boolean;
|
||||
@@ -185,7 +186,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit,
|
||||
if (this.onSuccessfulLoginNavigate != null) {
|
||||
// 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.onSuccessfulLoginNavigate();
|
||||
this.onSuccessfulLoginNavigate(response.userId);
|
||||
} else {
|
||||
this.loginEmailService.clearValues();
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
</ng-container>
|
||||
<!-- Account Credit -->
|
||||
<ng-container *ngIf="showAccountCredit && usingAccountCredit">
|
||||
<app-callout type="note">
|
||||
<app-callout type="info">
|
||||
{{ "makeSureEnoughCredit" | i18n }}
|
||||
</app-callout>
|
||||
</ng-container>
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
<div
|
||||
#callout
|
||||
class="callout callout-{{ calloutStyle }}"
|
||||
[ngClass]="{ clickable: clickable }"
|
||||
[attr.role]="useAlertRole ? 'alert' : null"
|
||||
>
|
||||
<h3 class="callout-heading" *ngIf="title">
|
||||
<i class="bwi {{ icon }}" *ngIf="icon" aria-hidden="true"></i>
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="enforced-policy-options" *ngIf="enforcedPolicyOptions">
|
||||
<bit-callout [icon]="icon" [title]="title" [type]="$any(type)" [useAlertRole]="useAlertRole">
|
||||
<div class="tw-pl-7 tw-m-0" *ngIf="enforcedPolicyOptions">
|
||||
{{ enforcedPolicyMessage }}
|
||||
<ul>
|
||||
<li *ngIf="enforcedPolicyOptions?.minComplexity > 0">
|
||||
@@ -32,4 +23,4 @@
|
||||
</ul>
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</bit-callout>
|
||||
|
||||
@@ -2,16 +2,19 @@ import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CalloutTypes } from "@bitwarden/components";
|
||||
|
||||
/**
|
||||
* @deprecated use the CL's `CalloutComponent` instead
|
||||
*/
|
||||
@Component({
|
||||
selector: "app-callout",
|
||||
templateUrl: "callout.component.html",
|
||||
})
|
||||
export class CalloutComponent implements OnInit {
|
||||
@Input() type = "info";
|
||||
export class DeprecatedCalloutComponent implements OnInit {
|
||||
@Input() type: CalloutTypes = "info";
|
||||
@Input() icon: string;
|
||||
@Input() title: string;
|
||||
@Input() clickable: boolean;
|
||||
@Input() enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
@Input() enforcedPolicyMessage: string;
|
||||
@Input() useAlertRole = false;
|
||||
@@ -26,34 +29,6 @@ export class CalloutComponent implements OnInit {
|
||||
if (this.enforcedPolicyMessage === undefined) {
|
||||
this.enforcedPolicyMessage = this.i18nService.t("masterPasswordPolicyInEffect");
|
||||
}
|
||||
|
||||
if (this.type === "warning" || this.type === "danger") {
|
||||
if (this.type === "danger") {
|
||||
this.calloutStyle = "danger";
|
||||
}
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("warning");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-exclamation-triangle";
|
||||
}
|
||||
} else if (this.type === "error") {
|
||||
this.calloutStyle = "danger";
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("error");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-error";
|
||||
}
|
||||
} else if (this.type === "tip") {
|
||||
this.calloutStyle = "success";
|
||||
if (this.title === undefined) {
|
||||
this.title = this.i18nService.t("tip");
|
||||
}
|
||||
if (this.icon === undefined) {
|
||||
this.icon = "bwi-lightbulb";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getPasswordScoreAlertDisplay() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
AsyncActionsModule,
|
||||
AutofocusDirective,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
CheckboxModule,
|
||||
DialogModule,
|
||||
FormFieldModule,
|
||||
@@ -29,7 +30,7 @@ import {
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { TwoFactorIconComponent } from "./auth/components/two-factor-icon.component";
|
||||
import { CalloutComponent } from "./components/callout.component";
|
||||
import { DeprecatedCalloutComponent } from "./components/callout.component";
|
||||
import { A11yInvalidDirective } from "./directives/a11y-invalid.directive";
|
||||
import { A11yTitleDirective } from "./directives/a11y-title.directive";
|
||||
import { ApiActionDirective } from "./directives/api-action.directive";
|
||||
@@ -72,6 +73,7 @@ import { IconComponent } from "./vault/components/icon.component";
|
||||
FormFieldModule,
|
||||
SelectModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
CheckboxModule,
|
||||
DialogModule,
|
||||
TypographyModule,
|
||||
@@ -88,7 +90,7 @@ import { IconComponent } from "./vault/components/icon.component";
|
||||
ApiActionDirective,
|
||||
AutofocusDirective,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
DeprecatedCalloutComponent,
|
||||
CopyTextDirective,
|
||||
CreditCardNumberPipe,
|
||||
EllipsisPipe,
|
||||
@@ -125,7 +127,7 @@ import { IconComponent } from "./vault/components/icon.component";
|
||||
AutofocusDirective,
|
||||
ToastModule,
|
||||
BoxRowDirective,
|
||||
CalloutComponent,
|
||||
DeprecatedCalloutComponent,
|
||||
CopyTextDirective,
|
||||
CreditCardNumberPipe,
|
||||
EllipsisPipe,
|
||||
|
||||
83
libs/angular/src/platform/abstractions/view-cache.service.ts
Normal file
83
libs/angular/src/platform/abstractions/view-cache.service.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Injector, WritableSignal } from "@angular/core";
|
||||
import type { FormGroup } from "@angular/forms";
|
||||
import type { Jsonify, JsonValue } from "type-fest";
|
||||
|
||||
type Deserializer<T> = {
|
||||
/**
|
||||
* A function to use to safely convert your type from json to your expected type.
|
||||
*
|
||||
* @param jsonValue The JSON object representation of your state.
|
||||
* @returns The fully typed version of your state.
|
||||
*/
|
||||
readonly deserializer?: (jsonValue: Jsonify<T>) => T;
|
||||
};
|
||||
|
||||
type BaseCacheOptions<T> = {
|
||||
/** A unique key for saving the cached value to state */
|
||||
key: string;
|
||||
|
||||
/** An optional injector. Required if the method is called outside of an injection context. */
|
||||
injector?: Injector;
|
||||
} & (T extends JsonValue ? Deserializer<T> : Required<Deserializer<T>>);
|
||||
|
||||
export type SignalCacheOptions<T> = BaseCacheOptions<T> & {
|
||||
/** The initial value for the signal. */
|
||||
initialValue: T;
|
||||
};
|
||||
|
||||
/** Extract the value type from a FormGroup */
|
||||
type FormValue<TFormGroup extends FormGroup> = TFormGroup["value"];
|
||||
|
||||
export type FormCacheOptions<TFormGroup extends FormGroup> = BaseCacheOptions<
|
||||
FormValue<TFormGroup>
|
||||
> & {
|
||||
control: TFormGroup;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cache for temporary component state
|
||||
*
|
||||
* #### Implementations
|
||||
* - browser extension popup: used to persist UI between popup open and close
|
||||
* - all other clients: noop
|
||||
*/
|
||||
export abstract class ViewCacheService {
|
||||
/**
|
||||
* Create a signal from a previously cached value. Whenever the signal is updated, the new value is saved to the cache.
|
||||
*
|
||||
* Non browser extension implementations are noop and return a normal signal.
|
||||
*
|
||||
* @returns the created signal
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const mySignal = this.viewCacheService.signal({
|
||||
* key: "popup-search-text"
|
||||
* initialValue: ""
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
abstract signal<T>(options: SignalCacheOptions<T>): WritableSignal<T>;
|
||||
|
||||
/**
|
||||
* - Initialize a form from a cached value
|
||||
* - Save form value to cache when it changes
|
||||
* - The form is marked dirty if the restored value is not `undefined`.
|
||||
*
|
||||
* Non browser extension implementations are noop and return the original form group.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* this.loginDetailsForm = this.viewCacheService.formGroup({
|
||||
* key: "vault-login-details-form",
|
||||
* control: this.formBuilder.group({
|
||||
* username: [""],
|
||||
* email: [""],
|
||||
* })
|
||||
* });
|
||||
* ```
|
||||
**/
|
||||
abstract formGroup<TFormGroup extends FormGroup>(
|
||||
options: FormCacheOptions<TFormGroup>,
|
||||
): TFormGroup;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Injectable, signal, WritableSignal } from "@angular/core";
|
||||
import type { FormGroup } from "@angular/forms";
|
||||
|
||||
import {
|
||||
FormCacheOptions,
|
||||
SignalCacheOptions,
|
||||
ViewCacheService,
|
||||
} from "../abstractions/view-cache.service";
|
||||
|
||||
/**
|
||||
* The functionality of the {@link ViewCacheService} is only needed in the browser extension popup,
|
||||
* yet is provided to all clients to make sharing components easier.
|
||||
*
|
||||
* Non-extension clients use this noop implementation.
|
||||
* */
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class NoopViewCacheService implements ViewCacheService {
|
||||
/**
|
||||
* Return a normal signal.
|
||||
*/
|
||||
signal<T>(options: SignalCacheOptions<T>): WritableSignal<T> {
|
||||
return signal(options.initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original form group.
|
||||
**/
|
||||
formGroup<TFormGroup extends FormGroup>(options: FormCacheOptions<TFormGroup>): TFormGroup {
|
||||
return options.control;
|
||||
}
|
||||
}
|
||||
@@ -268,8 +268,10 @@ import {
|
||||
} from "@bitwarden/vault-export-core";
|
||||
|
||||
import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service";
|
||||
import { ViewCacheService } from "../platform/abstractions/view-cache.service";
|
||||
import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service";
|
||||
import { LoggingErrorHandler } from "../platform/services/logging-error-handler";
|
||||
import { NoopViewCacheService } from "../platform/services/noop-view-cache.service";
|
||||
import { AngularThemingService } from "../platform/services/theming/angular-theming.service";
|
||||
import { AbstractThemingService } from "../platform/services/theming/theming.service.abstraction";
|
||||
import { safeProvider, SafeProvider } from "../platform/utils/safe-provider";
|
||||
@@ -1290,6 +1292,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: DefaultRegistrationFinishService,
|
||||
deps: [CryptoServiceAbstraction, AccountApiServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ViewCacheService,
|
||||
useExisting: NoopViewCacheService,
|
||||
deps: [],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
class="tw-flex tw-min-h-screen tw-w-full tw-mx-auto tw-flex-col tw-gap-7 tw-bg-background-alt tw-px-8 tw-pb-4 tw-text-main"
|
||||
[ngClass]="{ 'tw-pt-0': decreaseTopPadding, 'tw-pt-8': !decreaseTopPadding }"
|
||||
>
|
||||
<bit-icon *ngIf="!hideLogo" [icon]="logo" class="tw-max-w-36"></bit-icon>
|
||||
<bit-icon *ngIf="!hideLogo" [icon]="logo" class="tw-w-[128px] [&>*]:tw-align-top"></bit-icon>
|
||||
|
||||
<div class="tw-text-center">
|
||||
<div class="tw-mx-auto tw-max-w-28 sm:tw-max-w-32">
|
||||
|
||||
@@ -158,7 +158,10 @@ describe("AuthRequestLoginStrategy", () => {
|
||||
decMasterKeyHash,
|
||||
mockUserId,
|
||||
);
|
||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(
|
||||
tokenResponse.key,
|
||||
mockUserId,
|
||||
);
|
||||
expect(cryptoService.setUserKey).toHaveBeenCalledWith(userKey, mockUserId);
|
||||
expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled();
|
||||
expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId);
|
||||
@@ -183,7 +186,10 @@ describe("AuthRequestLoginStrategy", () => {
|
||||
expect(masterPasswordService.mock.setMasterKeyHash).not.toHaveBeenCalled();
|
||||
|
||||
// setMasterKeyEncryptedUserKey, setUserKey, and setPrivateKey should still be called
|
||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(tokenResponse.key);
|
||||
expect(cryptoService.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith(
|
||||
tokenResponse.key,
|
||||
mockUserId,
|
||||
);
|
||||
expect(cryptoService.setUserKey).toHaveBeenCalledWith(decUserKey, mockUserId);
|
||||
expect(cryptoService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId);
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy {
|
||||
const authRequestCredentials = this.cache.value.authRequestCredentials;
|
||||
// User now may or may not have a master password
|
||||
// but set the master key encrypted user key if it exists regardless
|
||||
await this.cryptoService.setMasterKeyEncryptedUserKey(response.key);
|
||||
await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, userId);
|
||||
|
||||
if (authRequestCredentials.decryptedUserKey) {
|
||||
await this.cryptoService.setUserKey(authRequestCredentials.decryptedUserKey, userId);
|
||||
|
||||
@@ -222,7 +222,11 @@ export abstract class LoginStrategy {
|
||||
),
|
||||
);
|
||||
|
||||
await this.billingAccountProfileStateService.setHasPremium(accountInformation.premium, false);
|
||||
await this.billingAccountProfileStateService.setHasPremium(
|
||||
accountInformation.premium,
|
||||
false,
|
||||
userId,
|
||||
);
|
||||
return userId;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user