1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 09:43:23 +00:00

Merge branch 'main' into autofill/pm-6546-blurring-of-autofilled-elements-causes-problems-in-blur-event-listeners

This commit is contained in:
Cesar Gonzalez
2024-03-07 13:25:14 -06:00
committed by GitHub
76 changed files with 961 additions and 472 deletions

View File

@@ -29,7 +29,7 @@ jobs:
secrets: "brew-bump-workflow-pat" secrets: "brew-bump-workflow-pat"
- name: Update Homebrew formula - name: Update Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@75ed025ff3ad1d617862838b342b06d613a0ddf3 # v3.10.1 uses: dawidd6/action-homebrew-bump-formula@baf2b60c51fc1f8453c884b0c61052668a71bd1d # v3.11.0
with: with:
# Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes # Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes
token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }} token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }}

View File

@@ -371,7 +371,7 @@ jobs:
secrets: "crowdin-api-token" secrets: "crowdin-api-token"
- name: Upload Sources - name: Upload Sources
uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0 uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -297,7 +297,7 @@ jobs:
echo "BW Package Version: $_PACKAGE_VERSION" echo "BW Package Version: $_PACKAGE_VERSION"
- name: Get bw linux cli - name: Get bw linux cli
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
name: bw-linux-${{ env._PACKAGE_VERSION }}.zip name: bw-linux-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/snap path: apps/cli/dist/snap

View File

@@ -167,7 +167,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: | path: |
@@ -296,7 +296,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: apps/desktop/desktop_native/*.node path: apps/desktop/desktop_native/*.node
@@ -476,14 +476,14 @@ jobs:
- name: Cache Build - name: Cache Build
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Cache Safari - name: Cache Safari
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -576,7 +576,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: apps/desktop/desktop_native/*.node path: apps/desktop/desktop_native/*.node
@@ -637,14 +637,14 @@ jobs:
- name: Get Build Cache - name: Get Build Cache
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache - name: Setup Safari Cache
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -737,7 +737,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: apps/desktop/desktop_native/*.node path: apps/desktop/desktop_native/*.node
@@ -753,7 +753,7 @@ jobs:
run: npm run build run: npm run build
- name: Download Browser artifact - name: Download Browser artifact
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
path: ${{ github.workspace }}/browser-build-artifacts path: ${{ github.workspace }}/browser-build-artifacts
@@ -843,14 +843,14 @@ jobs:
- name: Get Build Cache - name: Get Build Cache
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache - name: Setup Safari Cache
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -943,7 +943,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: apps/desktop/desktop_native/*.node path: apps/desktop/desktop_native/*.node
@@ -959,7 +959,7 @@ jobs:
run: npm run build run: npm run build
- name: Download Browser artifact - name: Download Browser artifact
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
path: ${{ github.workspace }}/browser-build-artifacts path: ${{ github.workspace }}/browser-build-artifacts
@@ -1036,14 +1036,14 @@ jobs:
- name: Get Build Cache - name: Get Build Cache
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache - name: Setup Safari Cache
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -1136,7 +1136,7 @@ jobs:
working-directory: ./ working-directory: ./
- name: Cache Native Module - name: Cache Native Module
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
id: cache id: cache
with: with:
path: apps/desktop/desktop_native/*.node path: apps/desktop/desktop_native/*.node
@@ -1152,7 +1152,7 @@ jobs:
run: npm run build run: npm run build
- name: Download Browser artifact - name: Download Browser artifact
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
path: ${{ github.workspace }}/browser-build-artifacts path: ${{ github.workspace }}/browser-build-artifacts
@@ -1211,7 +1211,7 @@ jobs:
secrets: "crowdin-api-token" secrets: "crowdin-api-token"
- name: Upload Sources - name: Upload Sources
uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0 uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -194,7 +194,7 @@ jobs:
secrets: "github-pat-bitwarden-devops-bot-repo-scope" secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Download ${{ matrix.artifact_name }} artifact - name: Download ${{ matrix.artifact_name }} artifact
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
path: apps/web path: apps/web
@@ -270,7 +270,7 @@ jobs:
secrets: "crowdin-api-token" secrets: "crowdin-api-token"
- name: Upload Sources - name: Upload Sources
uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0 uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -33,7 +33,7 @@ jobs:
- name: Cache npm - name: Cache npm
id: npm-cache id: npm-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: "~/.npm" path: "~/.npm"
key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }}
@@ -46,7 +46,7 @@ jobs:
run: npm run build-storybook:ci run: npm run build-storybook:ci
- name: Publish to Chromatic - name: Publish to Chromatic
uses: chromaui/action@76bda3648003815314bd50adaa553ee612a7f36c # v10.9.2 uses: chromaui/action@c9067691aca4a28d6fbb40d9eea6e144369fbcae # v10.9.5
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

View File

@@ -19,14 +19,14 @@ on:
description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')" description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')"
type: string type: string
default: main default: main
invert-default-sync-delete-destination-files-value: force-delete-destination:
description: "Invert the default sync-delete-destination-files value" description: "Delete remote files that are not found locally"
type: boolean type: boolean
default: false default: false
debug: debug:
description: "Debug mode" description: "Debug mode"
type: boolean type: boolean
default: false default: true
workflow_call: workflow_call:
inputs: inputs:
@@ -38,8 +38,8 @@ on:
description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')" description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')"
type: string type: string
default: main default: main
invert-default-sync-delete-destination-files-value: force-delete-destination:
description: "Invert the default sync-delete-destination-files value" description: "Delete remote files that are not found locally"
type: boolean type: boolean
default: false default: false
debug: debug:
@@ -71,14 +71,6 @@ jobs:
echo "configuring the Web deploy for ${{ inputs.environment }}" echo "configuring the Web deploy for ${{ inputs.environment }}"
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
# Invert the default value for sync-delete-destination-files
if [ ${{ inputs.invert-default-sync-delete-destination-files-value }} ]; then
echo "sync-delete-destination-files=true" >> $GITHUB_OUTPUT
else
# This is the default value for USQA, EUQA, USPROD, and EUPROD
echo "sync-delete-destination-files=false" >> $GITHUB_OUTPUT
fi
case ${{ inputs.environment }} in case ${{ inputs.environment }} in
"USQA") "USQA")
echo "azure-login-creds=AZURE_KV_US_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT echo "azure-login-creds=AZURE_KV_US_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
@@ -114,13 +106,6 @@ jobs:
echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT echo "environment-name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
if [ ${{ inputs.invert-default-sync-delete-destination-files-value }} ]; then
echo "sync-delete-destination-files=false" >> $GITHUB_OUTPUT
else
# This is the default value for USDEV
echo "sync-delete-destination-files=true" >> $GITHUB_OUTPUT
fi
;; ;;
esac esac
# Set the sync utility to use for deployment to the environment (az-sync or azcopy) # Set the sync utility to use for deployment to the environment (az-sync or azcopy)
@@ -285,7 +270,7 @@ jobs:
--source "./build" \ --source "./build" \
--container '$web' \ --container '$web' \
--connection-string "${{ steps.retrieve-secrets-az-sync.outputs.sa-bitwarden-web-vault-dev-key-temp }}" \ --connection-string "${{ steps.retrieve-secrets-az-sync.outputs.sa-bitwarden-web-vault-dev-key-temp }}" \
--delete-destination=${{ needs.setup.outputs.sync-delete-destination-files }} --delete-destination=${{ inputs.force-delete-destination }}
- name: Sync to Azure Storage Account using azcopy - name: Sync to Azure Storage Account using azcopy
if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }} if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }}
@@ -297,7 +282,7 @@ jobs:
AZCOPY_TENANT_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-tenant }} AZCOPY_TENANT_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-tenant }}
run: | run: |
azcopy sync ./build 'https://${{ steps.retrieve-secrets-azcopy.outputs.sa-bitwarden-web-vault-name }}.blob.core.windows.net/$web/' \ azcopy sync ./build 'https://${{ steps.retrieve-secrets-azcopy.outputs.sa-bitwarden-web-vault-name }}.blob.core.windows.net/$web/' \
--delete-destination=${{ needs.setup.outputs.sync-delete-destination-files }} --compare-hash="MD5" --delete-destination=${{ inputs.force-delete-destination }} --compare-hash="MD5"
- name: Debug sync logs - name: Debug sync logs
if: ${{ inputs.debug }} if: ${{ inputs.debug }}
@@ -309,7 +294,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: success() if: success()
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: ${{ env._ENVIRONMENT_URL }} environment-url: ${{ env._ENVIRONMENT_URL }}
@@ -318,7 +303,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: failure() if: failure()
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: ${{ env._ENVIRONMENT_URL }} environment-url: ${{ env._ENVIRONMENT_URL }}

View File

@@ -155,7 +155,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ success() }} if: ${{ success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success' state: 'success'
@@ -163,7 +163,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ failure() }} if: ${{ failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure' state: 'failure'

View File

@@ -118,7 +118,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success' state: 'success'
@@ -126,7 +126,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure' state: 'failure'

View File

@@ -424,14 +424,14 @@ jobs:
- name: Cache Build - name: Cache Build
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Cache Safari - name: Cache Safari
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -555,14 +555,14 @@ jobs:
- name: Get Build Cache - name: Get Build Cache
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache - name: Setup Safari Cache
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -659,7 +659,7 @@ jobs:
- name: Download artifact from hotfix-rc - name: Download artifact from hotfix-rc
if: github.ref == 'refs/heads/hotfix-rc' if: github.ref == 'refs/heads/hotfix-rc'
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -668,7 +668,7 @@ jobs:
- name: Download artifact from rc - name: Download artifact from rc
if: github.ref == 'refs/heads/rc' if: github.ref == 'refs/heads/rc'
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -677,7 +677,7 @@ jobs:
- name: Download artifacts from main - name: Download artifacts from main
if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }} if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -765,14 +765,14 @@ jobs:
- name: Get Build Cache - name: Get Build Cache
id: build-cache id: build-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/desktop/build path: apps/desktop/build
key: ${{ runner.os }}-${{ github.run_id }}-build key: ${{ runner.os }}-${{ github.run_id }}-build
- name: Setup Safari Cache - name: Setup Safari Cache
id: safari-cache id: safari-cache
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
with: with:
path: apps/browser/dist/Safari path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
@@ -864,7 +864,7 @@ jobs:
- name: Download artifact from hotfix-rc - name: Download artifact from hotfix-rc
if: github.ref == 'refs/heads/hotfix-rc' if: github.ref == 'refs/heads/hotfix-rc'
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -873,7 +873,7 @@ jobs:
- name: Download artifact from rc - name: Download artifact from rc
if: github.ref == 'refs/heads/rc' if: github.ref == 'refs/heads/rc'
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -882,7 +882,7 @@ jobs:
- name: Download artifact from main - name: Download artifact from main
if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }} if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
with: with:
workflow: build-browser.yml workflow: build-browser.yml
workflow_conclusion: success workflow_conclusion: success
@@ -953,7 +953,7 @@ jobs:
cf-prod-account" cf-prod-account"
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2 uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with: with:
path: apps/desktop/artifacts path: apps/desktop/artifacts
@@ -992,7 +992,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ success() }} if: ${{ success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success' state: 'success'
@@ -1000,7 +1000,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ failure() }} if: ${{ failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure' state: 'failure'

View File

@@ -231,7 +231,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success' state: 'success'
@@ -239,7 +239,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure' state: 'failure'

View File

@@ -268,7 +268,7 @@ jobs:
- name: Update deployment status to Success - name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com environment-url: http://vault.bitwarden.com
@@ -277,7 +277,7 @@ jobs:
- name: Update deployment status to Failure - name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }} if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with: with:
token: '${{ secrets.GITHUB_TOKEN }}' token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com environment-url: http://vault.bitwarden.com

View File

@@ -63,7 +63,7 @@ jobs:
fail-on-error: true fail-on-error: true
- name: Upload to codecov.io - name: Upload to codecov.io
uses: codecov/codecov-action@e0b68c6749509c5f83f984dd99a76a1c1a231044 # v4.0.1 uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -4,6 +4,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { NOTIFICATION_BAR_LIFESPAN_MS } from "@bitwarden/common/autofill/constants";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -19,7 +20,6 @@ import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service"; import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service";
import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window"; import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window";
import { NOTIFICATION_BAR_LIFESPAN_MS } from "../constants";
import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum"; import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum";
import { AutofillService } from "../services/abstractions/autofill.service"; import { AutofillService } from "../services/abstractions/autofill.service";

View File

@@ -2,6 +2,10 @@ import { mock, mockReset } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import {
SHOW_AUTOFILL_BUTTON,
AutofillOverlayVisibility,
} from "@bitwarden/common/autofill/constants";
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeType } from "@bitwarden/common/platform/enums";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
@@ -15,7 +19,6 @@ import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
import BrowserPlatformUtilsService from "../../platform/services/browser-platform-utils.service"; import BrowserPlatformUtilsService from "../../platform/services/browser-platform-utils.service";
import { BrowserStateService } from "../../platform/services/browser-state.service"; import { BrowserStateService } from "../../platform/services/browser-state.service";
import { SHOW_AUTOFILL_BUTTON } from "../constants";
import { AutofillService } from "../services/abstractions/autofill.service"; import { AutofillService } from "../services/abstractions/autofill.service";
import { import {
createAutofillPageDetailsMock, createAutofillPageDetailsMock,
@@ -28,7 +31,6 @@ import { flushPromises, sendExtensionRuntimeMessage, sendPortMessage } from "../
import { import {
AutofillOverlayElement, AutofillOverlayElement,
AutofillOverlayPort, AutofillOverlayPort,
AutofillOverlayVisibility,
RedirectFocusDirection, RedirectFocusDirection,
} from "../utils/autofill-overlay.enum"; } from "../utils/autofill-overlay.enum";

View File

@@ -3,7 +3,9 @@ import { firstValueFrom } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -22,13 +24,8 @@ import {
openViewVaultItemPopout, openViewVaultItemPopout,
openAddEditVaultItemPopout, openAddEditVaultItemPopout,
} from "../../vault/popup/utils/vault-popout-window"; } from "../../vault/popup/utils/vault-popout-window";
import { SHOW_AUTOFILL_BUTTON } from "../constants";
import { AutofillService, PageDetail } from "../services/abstractions/autofill.service"; import { AutofillService, PageDetail } from "../services/abstractions/autofill.service";
import { import { AutofillOverlayElement, AutofillOverlayPort } from "../utils/autofill-overlay.enum";
InlineMenuVisibilitySetting,
AutofillOverlayElement,
AutofillOverlayPort,
} from "../utils/autofill-overlay.enum";
import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background"; import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background";
import { import {

View File

@@ -3,14 +3,6 @@ import { mock, MockProxy } from "jest-mock-extended";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { import {
AUTOFILL_ID, AUTOFILL_ID,
COPY_PASSWORD_ID, COPY_PASSWORD_ID,
@@ -18,7 +10,14 @@ import {
COPY_VERIFICATION_CODE_ID, COPY_VERIFICATION_CODE_ID,
GENERATE_PASSWORD_ID, GENERATE_PASSWORD_ID,
NOOP_COMMAND_SUFFIX, NOOP_COMMAND_SUFFIX,
} from "../constants"; } from "@bitwarden/common/autofill/constants";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { import {
CopyToClipboardAction, CopyToClipboardAction,

View File

@@ -2,6 +2,20 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
AUTOFILL_IDENTITY_ID,
COPY_IDENTIFIER_ID,
COPY_PASSWORD_ID,
COPY_USERNAME_ID,
COPY_VERIFICATION_CODE_ID,
CREATE_CARD_ID,
CREATE_IDENTITY_ID,
CREATE_LOGIN_ID,
GENERATE_PASSWORD_ID,
NOOP_COMMAND_SUFFIX,
} from "@bitwarden/common/autofill/constants";
import { EventType } from "@bitwarden/common/enums"; import { EventType } from "@bitwarden/common/enums";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@@ -38,20 +52,6 @@ import { LockedVaultPendingNotificationsData } from "../background/abstractions/
import { autofillServiceFactory } from "../background/service_factories/autofill-service.factory"; import { autofillServiceFactory } from "../background/service_factories/autofill-service.factory";
import { copyToClipboard, GeneratePasswordToClipboardCommand } from "../clipboard"; import { copyToClipboard, GeneratePasswordToClipboardCommand } from "../clipboard";
import { AutofillTabCommand } from "../commands/autofill-tab-command"; import { AutofillTabCommand } from "../commands/autofill-tab-command";
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
AUTOFILL_IDENTITY_ID,
COPY_IDENTIFIER_ID,
COPY_PASSWORD_ID,
COPY_USERNAME_ID,
COPY_VERIFICATION_CODE_ID,
CREATE_CARD_ID,
CREATE_IDENTITY_ID,
CREATE_LOGIN_ID,
GENERATE_PASSWORD_ID,
NOOP_COMMAND_SUFFIX,
} from "../constants";
import { AutofillCipherTypeId } from "../types"; import { AutofillCipherTypeId } from "../types";
export type CopyToClipboardOptions = { text: string; tab: chrome.tabs.Tab }; export type CopyToClipboardOptions = { text: string; tab: chrome.tabs.Tab };

View File

@@ -1,5 +1,6 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { NOOP_COMMAND_SUFFIX } from "@bitwarden/common/autofill/constants";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
@@ -7,7 +8,6 @@ import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service"; import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service";
import { NOOP_COMMAND_SUFFIX } from "../constants";
import { MainContextMenuHandler } from "./main-context-menu-handler"; import { MainContextMenuHandler } from "./main-context-menu-handler";

View File

@@ -1,3 +1,19 @@
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
AUTOFILL_IDENTITY_ID,
COPY_IDENTIFIER_ID,
COPY_PASSWORD_ID,
COPY_USERNAME_ID,
COPY_VERIFICATION_CODE_ID,
CREATE_CARD_ID,
CREATE_IDENTITY_ID,
CREATE_LOGIN_ID,
GENERATE_PASSWORD_ID,
NOOP_COMMAND_SUFFIX,
ROOT_ID,
SEPARATOR_ID,
} from "@bitwarden/common/autofill/constants";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@@ -21,22 +37,6 @@ import {
StateServiceInitOptions, StateServiceInitOptions,
} from "../../platform/background/service-factories/state-service.factory"; } from "../../platform/background/service-factories/state-service.factory";
import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service"; import { BrowserStateService } from "../../platform/services/abstractions/browser-state.service";
import {
AUTOFILL_CARD_ID,
AUTOFILL_ID,
AUTOFILL_IDENTITY_ID,
COPY_IDENTIFIER_ID,
COPY_PASSWORD_ID,
COPY_USERNAME_ID,
COPY_VERIFICATION_CODE_ID,
CREATE_CARD_ID,
CREATE_IDENTITY_ID,
CREATE_LOGIN_ID,
GENERATE_PASSWORD_ID,
NOOP_COMMAND_SUFFIX,
ROOT_ID,
SEPARATOR_ID,
} from "../constants";
import { InitContextMenuItems } from "./abstractions/main-context-menu-handler"; import { InitContextMenuItems } from "./abstractions/main-context-menu-handler";

View File

@@ -1,12 +1,13 @@
import { mock } from "jest-mock-extended"; import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import AutofillPageDetails from "../models/autofill-page-details"; import AutofillPageDetails from "../models/autofill-page-details";
import AutofillScript from "../models/autofill-script"; import AutofillScript from "../models/autofill-script";
import AutofillOverlayContentService from "../services/autofill-overlay-content.service"; import AutofillOverlayContentService from "../services/autofill-overlay-content.service";
import { flushPromises, sendExtensionRuntimeMessage } from "../spec/testing-utils"; import { flushPromises, sendExtensionRuntimeMessage } from "../spec/testing-utils";
import { AutofillOverlayVisibility, RedirectFocusDirection } from "../utils/autofill-overlay.enum"; import { RedirectFocusDirection } from "../utils/autofill-overlay.enum";
import { AutofillExtensionMessage } from "./abstractions/autofill-init"; import { AutofillExtensionMessage } from "./abstractions/autofill-init";
import AutofillInit from "./autofill-init"; import AutofillInit from "./autofill-init";

View File

@@ -1,8 +1,8 @@
import { mock } from "jest-mock-extended"; import { mock } from "jest-mock-extended";
import { EVENTS } from "@bitwarden/common/autofill/constants";
import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeType } from "@bitwarden/common/platform/enums";
import { EVENTS } from "../../constants";
import { createPortSpyMock } from "../../spec/autofill-mocks"; import { createPortSpyMock } from "../../spec/autofill-mocks";
import { import {
flushPromises, flushPromises,

View File

@@ -1,6 +1,6 @@
import { EVENTS } from "@bitwarden/common/autofill/constants";
import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeType } from "@bitwarden/common/platform/enums";
import { EVENTS } from "../../constants";
import { setElementStyles } from "../../utils"; import { setElementStyles } from "../../utils";
import { import {
BackgroundPortMessageHandlers, BackgroundPortMessageHandlers,

View File

@@ -1,8 +1,8 @@
import "@webcomponents/custom-elements"; import "@webcomponents/custom-elements";
import "lit/polyfill-support.js"; import "lit/polyfill-support.js";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS } from "@bitwarden/common/autofill/constants";
import { EVENTS } from "../../../constants";
import { buildSvgDomElement } from "../../../utils"; import { buildSvgDomElement } from "../../../utils";
import { logoIcon, logoLockedIcon } from "../../../utils/svg-icons"; import { logoIcon, logoLockedIcon } from "../../../utils/svg-icons";
import { import {

View File

@@ -1,9 +1,9 @@
import "@webcomponents/custom-elements"; import "@webcomponents/custom-elements";
import "lit/polyfill-support.js"; import "lit/polyfill-support.js";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS } from "@bitwarden/common/autofill/constants";
import { OverlayCipherData } from "../../../background/abstractions/overlay.background"; import { OverlayCipherData } from "../../../background/abstractions/overlay.background";
import { EVENTS } from "../../../constants";
import { buildSvgDomElement } from "../../../utils"; import { buildSvgDomElement } from "../../../utils";
import { globeIcon, lockIcon, plusIcon, viewCipherIcon } from "../../../utils/svg-icons"; import { globeIcon, lockIcon, plusIcon, viewCipherIcon } from "../../../utils/svg-icons";
import { import {

View File

@@ -1,4 +1,5 @@
import { EVENTS } from "../../../constants"; import { EVENTS } from "@bitwarden/common/autofill/constants";
import { RedirectFocusDirection } from "../../../utils/autofill-overlay.enum"; import { RedirectFocusDirection } from "../../../utils/autofill-overlay.enum";
import { import {
AutofillOverlayPageElementWindowMessage, AutofillOverlayPageElementWindowMessage,

View File

@@ -2,7 +2,9 @@ import { Component, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs"; import { firstValueFrom } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@@ -12,10 +14,6 @@ import { DialogService } from "@bitwarden/components";
import { BrowserApi } from "../../../platform/browser/browser-api"; import { BrowserApi } from "../../../platform/browser/browser-api";
import { enableAccountSwitching } from "../../../platform/flags"; import { enableAccountSwitching } from "../../../platform/flags";
import { AutofillService } from "../../services/abstractions/autofill.service"; import { AutofillService } from "../../services/abstractions/autofill.service";
import {
AutofillOverlayVisibility,
InlineMenuVisibilitySetting,
} from "../../utils/autofill-overlay.enum";
@Component({ @Component({
selector: "app-autofill", selector: "app-autofill",

View File

@@ -1,17 +1,13 @@
import { mock } from "jest-mock-extended"; import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS, AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { EVENTS } from "../constants";
import AutofillField from "../models/autofill-field"; import AutofillField from "../models/autofill-field";
import { createAutofillFieldMock } from "../spec/autofill-mocks"; import { createAutofillFieldMock } from "../spec/autofill-mocks";
import { flushPromises } from "../spec/testing-utils"; import { flushPromises } from "../spec/testing-utils";
import { ElementWithOpId, FormFieldElement } from "../types"; import { ElementWithOpId, FormFieldElement } from "../types";
import { import { AutofillOverlayElement, RedirectFocusDirection } from "../utils/autofill-overlay.enum";
AutofillOverlayElement,
AutofillOverlayVisibility,
RedirectFocusDirection,
} from "../utils/autofill-overlay.enum";
import { AutoFillConstants } from "./autofill-constants"; import { AutoFillConstants } from "./autofill-constants";
import AutofillOverlayContentService from "./autofill-overlay-content.service"; import AutofillOverlayContentService from "./autofill-overlay-content.service";

View File

@@ -3,9 +3,9 @@ import "lit/polyfill-support.js";
import { FocusableElement, tabbable } from "tabbable"; import { FocusableElement, tabbable } from "tabbable";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS, AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { FocusedFieldData } from "../background/abstractions/overlay.background"; import { FocusedFieldData } from "../background/abstractions/overlay.background";
import { EVENTS } from "../constants";
import AutofillField from "../models/autofill-field"; import AutofillField from "../models/autofill-field";
import AutofillOverlayButtonIframe from "../overlay/iframe-content/autofill-overlay-button-iframe"; import AutofillOverlayButtonIframe from "../overlay/iframe-content/autofill-overlay-button-iframe";
import AutofillOverlayListIframe from "../overlay/iframe-content/autofill-overlay-list-iframe"; import AutofillOverlayListIframe from "../overlay/iframe-content/autofill-overlay-list-iframe";
@@ -16,11 +16,7 @@ import {
sendExtensionMessage, sendExtensionMessage,
setElementStyles, setElementStyles,
} from "../utils"; } from "../utils";
import { import { AutofillOverlayElement, RedirectFocusDirection } from "../utils/autofill-overlay.enum";
AutofillOverlayElement,
RedirectFocusDirection,
AutofillOverlayVisibility,
} from "../utils/autofill-overlay.enum";
import { import {
AutofillOverlayContentService as AutofillOverlayContentServiceInterface, AutofillOverlayContentService as AutofillOverlayContentServiceInterface,

View File

@@ -1,6 +1,7 @@
import { mock, mockReset } from "jest-mock-extended"; import { mock, mockReset } from "jest-mock-extended";
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service"; import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { EventType } from "@bitwarden/common/enums"; import { EventType } from "@bitwarden/common/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -37,7 +38,6 @@ import {
createGenerateFillScriptOptionsMock, createGenerateFillScriptOptionsMock,
} from "../spec/autofill-mocks"; } from "../spec/autofill-mocks";
import { triggerTestFailure } from "../spec/testing-utils"; import { triggerTestFailure } from "../spec/testing-utils";
import { AutofillOverlayVisibility } from "../utils/autofill-overlay.enum";
import { import {
AutoFillOptions, AutoFillOptions,

View File

@@ -4,6 +4,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { EventType } from "@bitwarden/common/enums"; import { EventType } from "@bitwarden/common/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -20,7 +21,6 @@ import { AutofillPort } from "../enums/autofill-port.enums";
import AutofillField from "../models/autofill-field"; import AutofillField from "../models/autofill-field";
import AutofillPageDetails from "../models/autofill-page-details"; import AutofillPageDetails from "../models/autofill-page-details";
import AutofillScript from "../models/autofill-script"; import AutofillScript from "../models/autofill-script";
import { InlineMenuVisibilitySetting } from "../utils/autofill-overlay.enum";
import { import {
AutoFillOptions, AutoFillOptions,

View File

@@ -1,4 +1,5 @@
import { EVENTS } from "../constants"; import { EVENTS } from "@bitwarden/common/autofill/constants";
import AutofillScript, { FillScript, FillScriptActions } from "../models/autofill-script"; import AutofillScript, { FillScript, FillScriptActions } from "../models/autofill-script";
import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types";

View File

@@ -1,4 +1,5 @@
import { EVENTS, TYPE_CHECK } from "../constants"; import { EVENTS, TYPE_CHECK } from "@bitwarden/common/autofill/constants";
import AutofillScript, { AutofillInsertActions, FillScript } from "../models/autofill-script"; import AutofillScript, { AutofillInsertActions, FillScript } from "../models/autofill-script";
import { FormFieldElement } from "../types"; import { FormFieldElement } from "../types";
import { import {

View File

@@ -14,19 +14,4 @@ const RedirectFocusDirection = {
Next: "next", Next: "next",
} as const; } as const;
const AutofillOverlayVisibility = { export { AutofillOverlayElement, AutofillOverlayPort, RedirectFocusDirection };
Off: 0,
OnButtonClick: 1,
OnFieldFocus: 2,
} as const;
type InlineMenuVisibilitySetting =
(typeof AutofillOverlayVisibility)[keyof typeof AutofillOverlayVisibility];
export {
AutofillOverlayElement,
AutofillOverlayPort,
RedirectFocusDirection,
AutofillOverlayVisibility,
InlineMenuVisibilitySetting,
};

View File

@@ -1,4 +1,5 @@
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -15,7 +16,6 @@ import {
} from "../auth/popup/utils/auth-popout-window"; } from "../auth/popup/utils/auth-popout-window";
import { LockedVaultPendingNotificationsData } from "../autofill/background/abstractions/notification.background"; import { LockedVaultPendingNotificationsData } from "../autofill/background/abstractions/notification.background";
import { AutofillService } from "../autofill/services/abstractions/autofill.service"; import { AutofillService } from "../autofill/services/abstractions/autofill.service";
import { AutofillOverlayVisibility } from "../autofill/utils/autofill-overlay.enum";
import { BrowserApi } from "../platform/browser/browser-api"; import { BrowserApi } from "../platform/browser/browser-api";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service"; import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service"; import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";

View File

@@ -6,6 +6,7 @@ import { SettingsService } from "@bitwarden/common/abstractions/settings.service
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service"; import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { ClearClipboardDelaySetting } from "@bitwarden/common/autofill/types";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@@ -13,7 +14,6 @@ import { ThemeType } from "@bitwarden/common/platform/enums";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { UriMatchType } from "@bitwarden/common/vault/enums"; import { UriMatchType } from "@bitwarden/common/vault/enums";
import { ClearClipboardDelaySetting } from "../../../../../apps/browser/src/autofill/constants";
import { enableAccountSwitching } from "../../platform/flags"; import { enableAccountSwitching } from "../../platform/flags";
@Component({ @Component({

View File

@@ -5,6 +5,7 @@ import { debounceTime, takeUntil } from "rxjs/operators";
import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -19,7 +20,6 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault"; import { PasswordRepromptService } from "@bitwarden/vault";
import { AutofillService } from "../../../../autofill/services/abstractions/autofill.service"; import { AutofillService } from "../../../../autofill/services/abstractions/autofill.service";
import { AutofillOverlayVisibility } from "../../../../autofill/utils/autofill-overlay.enum";
import { BrowserApi } from "../../../../platform/browser/browser-api"; import { BrowserApi } from "../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../platform/popup/browser-popup-utils"; import BrowserPopupUtils from "../../../../platform/popup/browser-popup-utils";
import { VaultFilterService } from "../../../services/vault-filter.service"; import { VaultFilterService } from "../../../services/vault-filter.service";
@@ -123,15 +123,32 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
.pipe(debounceTime(500), takeUntil(this.destroy$)) .pipe(debounceTime(500), takeUntil(this.destroy$))
.subscribe(() => this.searchVault()); .subscribe(() => this.searchVault());
// activate autofill on page load if policy is set const autofillOnPageLoadOrgPolicy = await firstValueFrom(
if (await this.getActivateAutofillOnPageLoadFromPolicy()) { this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$,
);
const autofillOnPageLoadPolicyToastHasDisplayed = await firstValueFrom(
this.autofillSettingsService.autofillOnPageLoadPolicyToastHasDisplayed$,
);
// If the org "autofill on page load" policy is set, set the user setting to match it
// @TODO override user setting instead of overwriting
if (autofillOnPageLoadOrgPolicy === true) {
await this.autofillSettingsService.setAutofillOnPageLoad(true); await this.autofillSettingsService.setAutofillOnPageLoad(true);
await this.autofillSettingsService.setActivateAutofillOnPageLoadFromPolicy(false);
if (!autofillOnPageLoadPolicyToastHasDisplayed) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"info", "info",
null, null,
this.i18nService.t("autofillPageLoadPolicyActivated"), this.i18nService.t("autofillPageLoadPolicyActivated"),
); );
await this.autofillSettingsService.setAutofillOnPageLoadPolicyToastHasDisplayed(true);
}
}
// If the org policy is ever disabled after being enabled, reset the toast notification
if (!autofillOnPageLoadOrgPolicy && autofillOnPageLoadPolicyToastHasDisplayed) {
await this.autofillSettingsService.setAutofillOnPageLoadPolicyToastHasDisplayed(false);
} }
} }
@@ -303,10 +320,6 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
this.router.navigate(["autofill"]); this.router.navigate(["autofill"]);
} }
private async getActivateAutofillOnPageLoadFromPolicy(): Promise<boolean> {
return await firstValueFrom(this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$);
}
async dismissCallout() { async dismissCallout() {
await this.autofillSettingsService.setAutofillOnPageLoadCalloutIsDismissed(true); await this.autofillSettingsService.setAutofillOnPageLoadCalloutIsDismissed(true);
this.showHowToAutofill = false; this.showHowToAutofill = false;

View File

@@ -31,5 +31,9 @@
"strictTemplates": true, "strictTemplates": true,
"preserveWhitespaces": true "preserveWhitespaces": true
}, },
"include": ["src", "../../libs/common/src/platform/services/**/*.worker.ts"] "include": [
"src",
"../../libs/common/src/platform/services/**/*.worker.ts",
"../../libs/common/src/autofill/constants"
]
} }

View File

@@ -1607,21 +1607,31 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.52.0" version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
dependencies = [ dependencies = [
"windows-core", "windows-core",
"windows-targets 0.52.0", "windows-targets 0.52.4",
] ]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
dependencies = [ dependencies = [
"windows-targets 0.52.0", "windows-result",
"windows-targets 0.52.4",
]
[[package]]
name = "windows-result"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64"
dependencies = [
"windows-targets 0.52.4",
] ]
[[package]] [[package]]
@@ -1639,7 +1649,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.0", "windows-targets 0.52.4",
] ]
[[package]] [[package]]
@@ -1659,17 +1669,17 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.0", "windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.0", "windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.0", "windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.0", "windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.0", "windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.0", "windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.0", "windows_x86_64_msvc 0.52.4",
] ]
[[package]] [[package]]
@@ -1680,9 +1690,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@@ -1692,9 +1702,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@@ -1704,9 +1714,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@@ -1716,9 +1726,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@@ -1728,9 +1738,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@@ -1740,9 +1750,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@@ -1752,9 +1762,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]] [[package]]
name = "winnow" name = "winnow"

View File

@@ -33,7 +33,7 @@ napi-build = "=2.0.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
widestring = "=1.0.2" widestring = "=1.0.2"
windows = { version = "=0.52.0", features = [ windows = { version = "=0.54.0", features = [
"Foundation", "Foundation",
"Security_Credentials_UI", "Security_Credentials_UI",
"Security_Cryptography", "Security_Cryptography",

View File

@@ -24,7 +24,7 @@
"**/node_modules/argon2/package.json", "**/node_modules/argon2/package.json",
"**/node_modules/argon2/lib/binding/napi-v3/argon2.node" "**/node_modules/argon2/lib/binding/napi-v3/argon2.node"
], ],
"electronVersion": "28.2.5", "electronVersion": "28.2.6",
"generateUpdatesFilesForAllChannels": true, "generateUpdatesFilesForAllChannels": true,
"publish": { "publish": {
"provider": "generic", "provider": "generic",

View File

@@ -1,7 +1,7 @@
{ {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.", "description": "A secure and free password manager for all of your devices.",
"version": "2024.2.1", "version": "2024.2.2",
"keywords": [ "keywords": [
"bitwarden", "bitwarden",
"password", "password",

View File

@@ -1,12 +1,12 @@
{ {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"version": "2024.2.1", "version": "2024.2.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"version": "2024.2.1", "version": "2024.2.2",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native" "@bitwarden/desktop-native": "file:../desktop_native"

View File

@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"productName": "Bitwarden", "productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.", "description": "A secure and free password manager for all of your devices.",
"version": "2024.2.1", "version": "2024.2.2",
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)", "author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com", "homepage": "https://bitwarden.com",
"license": "GPL-3.0", "license": "GPL-3.0",

View File

@@ -1,33 +1,30 @@
<h1 class="tw-text-4xl !tw-text-alt2">{{ header }}</h1> <h1 class="tw-text-4xl !tw-text-alt2">{{ header }}</h1>
<div class="tw-pt-16"> <div class="tw-pt-16">
<h2 class="tw-text-2xl tw-font-semibold"> <h2 class="tw-text-2xl tw-font-semibold">
Secure your business with a simpler, faster way to secure and manage secrets {{ headline }}
</h2> </h2>
</div> </div>
<ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main"> <ul class="tw-mt-12 tw-flex tw-flex-col tw-gap-10 tw-text-2xl tw-text-main">
<li>Unlimited secrets, users, and projects</li> <li *ngFor="let primaryPoint of primaryPoints">
<li>Simple and transparent pricing</li> {{ primaryPoint }}
<li>End-to-end encryption</li> </li>
</ul> </ul>
<div class="tw-mt-12 tw-flex tw-flex-col"> <div class="tw-mt-12 tw-flex tw-flex-col">
<div class="tw-rounded-[32px] tw-bg-background"> <div class="tw-rounded-[32px] tw-bg-background">
<div class="tw-my-8 tw-mx-6"> <div class="tw-my-8 tw-mx-6">
<h2 class="tw-pl-5 tw-font-semibold">Limited time offer</h2> <h2 class="tw-pl-5 tw-font-semibold">{{ calloutHeadline }}</h2>
<ul class="tw-space-y-4 tw-mt-4 tw-pl-10"> <ul class="tw-space-y-4 tw-mt-4 tw-pl-10">
<li> <li *ngFor="let callout of callouts">
Sign up today and receive a complimentary 12-month subscription to Bitwarden Password {{ callout }}
Manager
</li> </li>
<li>Experience complete security across your organization</li>
<li>Secure all your sensitive credentials, from passwords to machine secrets</li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<div class="tw-mt-12 tw-flex tw-flex-col tw-items-center tw-gap-5"> <div class="tw-mt-12 tw-flex tw-flex-col tw-items-center tw-gap-5">
<app-review-blurb <app-review-blurb
header="Businesses trust Bitwarden to secure their secrets" header="Businesses trust Bitwarden to secure their infrastructure"
quote="At this point, it would be almost impossible to leak our secrets. It's just one less thing we have to worry about." quote="At this point, it would be almost impossible to leak our secrets. It's just one less thing we have to worry about."
source="Head of IT, Titanom Technologies" source="Titanom Technologies"
></app-review-blurb> ></app-review-blurb>
</div> </div>

View File

@@ -8,6 +8,38 @@ import { Subject, takeUntil } from "rxjs";
}) })
export class SecretsManagerContentComponent implements OnInit, OnDestroy { export class SecretsManagerContentComponent implements OnInit, OnDestroy {
header: string; header: string;
headline =
"A simpler, faster way to secure and automate secrets across code and infrastructure deployments";
primaryPoints: string[];
calloutHeadline: string;
callouts: string[];
private paidPrimaryPoints = [
"Unlimited secrets, users, and projects",
"Simple and transparent pricing",
"Zero-knowledge, end-to-end encryption",
];
private paidCalloutHeadline = "Limited time offer";
private paidCallouts = [
"Sign up today and receive a complimentary 12-month subscription to Bitwarden Password Manager",
"Experience complete security across your organization",
"Secure all your sensitive credentials, from user applications to machine secrets",
];
private freePrimaryPoints = [
"Unlimited secrets",
"Simple and transparent pricing",
"Zero-knowledge, end-to-end encryption",
];
private freeCalloutHeadline = "Go beyond developer security!";
private freeCallouts = [
"Your Bitwarden account will also grant complimentary access to Bitwarden Password Manager",
"Extend end-to-end encryption to your personal passwords, addresses, credit cards and notes",
];
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
@@ -23,13 +55,22 @@ export class SecretsManagerContentComponent implements OnInit, OnDestroy {
switch (queryParameters.org) { switch (queryParameters.org) {
case "enterprise": case "enterprise":
this.header = "Secrets Manager for Enterprise"; this.header = "Secrets Manager for Enterprise";
this.primaryPoints = this.paidPrimaryPoints;
this.calloutHeadline = this.paidCalloutHeadline;
this.callouts = this.paidCallouts;
break; break;
case "free": case "free":
this.header = "Bitwarden Secrets Manager"; this.header = "Bitwarden Secrets Manager";
this.primaryPoints = this.freePrimaryPoints;
this.calloutHeadline = this.freeCalloutHeadline;
this.callouts = this.freeCallouts;
break; break;
case "teams": case "teams":
case "teamsStarter": case "teamsStarter":
this.header = "Secrets Manager for Teams"; this.header = "Secrets Manager for Teams";
this.primaryPoints = this.paidPrimaryPoints;
this.calloutHeadline = this.paidCalloutHeadline;
this.callouts = this.paidCallouts;
break; break;
} }
}); });

View File

@@ -41,14 +41,14 @@
</ul> </ul>
</div> </div>
<div class="tw-mb-3 tw-flex"> <div class="tw-mb-3 tw-flex">
<button type="button" bitButton buttonType="primary" (click)="navigateTo('vault')"> <button type="button" bitButton buttonType="primary" (click)="navigateToSecretsManager()">
{{ "getStarted" | i18n | titlecase }} {{ "getStarted" | i18n | titlecase }}
</button> </button>
<button <button
type="button" type="button"
bitButton bitButton
buttonType="secondary" buttonType="secondary"
(click)="navigateTo('members')" (click)="navigateToMembers()"
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3" class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
> >
{{ "inviteUsers" | i18n }} {{ "inviteUsers" | i18n }}

View File

@@ -78,7 +78,11 @@ export class SecretsManagerTrialFreeStepperComponent implements OnInit {
this.verticalStepper.next(); this.verticalStepper.next();
} }
async navigateTo(organizationRoute: string): Promise<void> { async navigateToMembers(): Promise<void> {
await this.router.navigate(["organizations", this.organizationId, organizationRoute]); await this.router.navigate(["organizations", this.organizationId, "members"]);
}
async navigateToSecretsManager(): Promise<void> {
await this.router.navigate(["sm", this.organizationId]);
} }
} }

View File

@@ -46,14 +46,14 @@
[orgLabel]="organizationTypeQueryParameter" [orgLabel]="organizationTypeQueryParameter"
></app-trial-confirmation-details> ></app-trial-confirmation-details>
<div class="tw-mb-3 tw-flex"> <div class="tw-mb-3 tw-flex">
<button type="button" bitButton buttonType="primary" (click)="navigateTo('vault')"> <button type="button" bitButton buttonType="primary" (click)="navigateToSecretsManager()">
{{ "getStarted" | i18n | titlecase }} {{ "getStarted" | i18n | titlecase }}
</button> </button>
<button <button
type="button" type="button"
bitButton bitButton
buttonType="secondary" buttonType="secondary"
(click)="navigateTo('members')" (click)="navigateToMembers()"
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3" class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
> >
{{ "inviteUsers" | i18n }} {{ "inviteUsers" | i18n }}

View File

@@ -27,12 +27,9 @@
<button type="button" bitLink (click)="emitToAddCipher()"> <button type="button" bitLink (click)="emitToAddCipher()">
{{ "onboardingImportDataDetailsLink" | i18n }} {{ "onboardingImportDataDetailsLink" | i18n }}
</button> </button>
<span *ngIf="orgs == null || orgs.length === 0"> <span>
{{ "onboardingImportDataDetailsPartTwoNoOrgs" | i18n }} {{ "onboardingImportDataDetailsPartTwoNoOrgs" | i18n }}
</span> </span>
<span *ngIf="orgs.length > 0">
{{ "onboardingImportDataDetailsPartTwoWithOrgs" | i18n }}
</span>
</p> </p>
</app-onboarding-task> </app-onboarding-task>

View File

@@ -32,9 +32,6 @@ export const ClearClipboardDelay = {
FiveMinutes: 300, FiveMinutes: 300,
} as const; } as const;
export type ClearClipboardDelaySetting =
(typeof ClearClipboardDelay)[keyof typeof ClearClipboardDelay];
/* Context Menu item Ids */ /* Context Menu item Ids */
export const AUTOFILL_CARD_ID = "autofill-card"; export const AUTOFILL_CARD_ID = "autofill-card";
export const AUTOFILL_ID = "autofill"; export const AUTOFILL_ID = "autofill";
@@ -53,3 +50,9 @@ export const ROOT_ID = "root";
export const SEPARATOR_ID = "separator"; export const SEPARATOR_ID = "separator";
export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds
export const AutofillOverlayVisibility = {
Off: 0,
OnButtonClick: 1,
OnFieldFocus: 2,
} as const;

View File

@@ -1,16 +1,7 @@
import { filter, switchMap, tap, firstValueFrom, map, Observable } from "rxjs"; import { map, Observable } from "rxjs";
import {
ClearClipboardDelaySetting,
ClearClipboardDelay,
} from "../../../../../apps/browser/src/autofill/constants";
import {
AutofillOverlayVisibility,
InlineMenuVisibilitySetting,
} from "../../../../../apps/browser/src/autofill/utils/autofill-overlay.enum";
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "../../admin-console/enums/index"; import { PolicyType } from "../../admin-console/enums";
import { Policy } from "../../admin-console/models/domain/policy";
import { import {
AUTOFILL_SETTINGS_DISK, AUTOFILL_SETTINGS_DISK,
AUTOFILL_SETTINGS_DISK_LOCAL, AUTOFILL_SETTINGS_DISK_LOCAL,
@@ -19,6 +10,8 @@ import {
KeyDefinition, KeyDefinition,
StateProvider, StateProvider,
} from "../../platform/state"; } from "../../platform/state";
import { ClearClipboardDelay, AutofillOverlayVisibility } from "../constants";
import { ClearClipboardDelaySetting, InlineMenuVisibilitySetting } from "../types";
const AUTOFILL_ON_PAGE_LOAD = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autofillOnPageLoad", { const AUTOFILL_ON_PAGE_LOAD = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autofillOnPageLoad", {
deserializer: (value: boolean) => value ?? false, deserializer: (value: boolean) => value ?? false,
@@ -32,10 +25,6 @@ const AUTOFILL_ON_PAGE_LOAD_DEFAULT = new KeyDefinition(
}, },
); );
const AUTO_COPY_TOTP = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autoCopyTotp", {
deserializer: (value: boolean) => value ?? false,
});
const AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED = new KeyDefinition( const AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED = new KeyDefinition(
AUTOFILL_SETTINGS_DISK, AUTOFILL_SETTINGS_DISK,
"autofillOnPageLoadCalloutIsDismissed", "autofillOnPageLoadCalloutIsDismissed",
@@ -44,14 +33,18 @@ const AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED = new KeyDefinition(
}, },
); );
const ACTIVATE_AUTOFILL_ON_PAGE_LOAD_FROM_POLICY = new KeyDefinition( const AUTOFILL_ON_PAGE_LOAD_POLICY_TOAST_HAS_DISPLAYED = new KeyDefinition(
AUTOFILL_SETTINGS_DISK_LOCAL, AUTOFILL_SETTINGS_DISK,
"activateAutofillOnPageLoadFromPolicy", "autofillOnPageLoadPolicyToastHasDisplayed",
{ {
deserializer: (value: boolean) => value ?? false, deserializer: (value: boolean) => value ?? false,
}, },
); );
const AUTO_COPY_TOTP = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autoCopyTotp", {
deserializer: (value: boolean) => value ?? false,
});
const INLINE_MENU_VISIBILITY = new KeyDefinition( const INLINE_MENU_VISIBILITY = new KeyDefinition(
AUTOFILL_SETTINGS_DISK_LOCAL, AUTOFILL_SETTINGS_DISK_LOCAL,
"inlineMenuVisibility", "inlineMenuVisibility",
@@ -73,17 +66,17 @@ export abstract class AutofillSettingsServiceAbstraction {
setAutofillOnPageLoad: (newValue: boolean) => Promise<void>; setAutofillOnPageLoad: (newValue: boolean) => Promise<void>;
autofillOnPageLoadDefault$: Observable<boolean>; autofillOnPageLoadDefault$: Observable<boolean>;
setAutofillOnPageLoadDefault: (newValue: boolean) => Promise<void>; setAutofillOnPageLoadDefault: (newValue: boolean) => Promise<void>;
autoCopyTotp$: Observable<boolean>;
setAutoCopyTotp: (newValue: boolean) => Promise<void>;
autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>; autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>;
setAutofillOnPageLoadCalloutIsDismissed: (newValue: boolean) => Promise<void>; setAutofillOnPageLoadCalloutIsDismissed: (newValue: boolean) => Promise<void>;
activateAutofillOnPageLoadFromPolicy$: Observable<boolean>; activateAutofillOnPageLoadFromPolicy$: Observable<boolean>;
setActivateAutofillOnPageLoadFromPolicy: (newValue: boolean) => Promise<void>; setAutofillOnPageLoadPolicyToastHasDisplayed: (newValue: boolean) => Promise<void>;
autofillOnPageLoadPolicyToastHasDisplayed$: Observable<boolean>;
autoCopyTotp$: Observable<boolean>;
setAutoCopyTotp: (newValue: boolean) => Promise<void>;
inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>; inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>;
setInlineMenuVisibility: (newValue: InlineMenuVisibilitySetting) => Promise<void>; setInlineMenuVisibility: (newValue: InlineMenuVisibilitySetting) => Promise<void>;
clearClipboardDelay$: Observable<ClearClipboardDelaySetting>; clearClipboardDelay$: Observable<ClearClipboardDelaySetting>;
setClearClipboardDelay: (newValue: ClearClipboardDelaySetting) => Promise<void>; setClearClipboardDelay: (newValue: ClearClipboardDelaySetting) => Promise<void>;
handleActivateAutofillPolicy: (policies: Observable<Policy[]>) => Observable<boolean[]>;
} }
export class AutofillSettingsService implements AutofillSettingsServiceAbstraction { export class AutofillSettingsService implements AutofillSettingsServiceAbstraction {
@@ -93,15 +86,17 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
private autofillOnPageLoadDefaultState: ActiveUserState<boolean>; private autofillOnPageLoadDefaultState: ActiveUserState<boolean>;
readonly autofillOnPageLoadDefault$: Observable<boolean>; readonly autofillOnPageLoadDefault$: Observable<boolean>;
private autoCopyTotpState: ActiveUserState<boolean>;
readonly autoCopyTotp$: Observable<boolean>;
private autofillOnPageLoadCalloutIsDismissedState: ActiveUserState<boolean>; private autofillOnPageLoadCalloutIsDismissedState: ActiveUserState<boolean>;
readonly autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>; readonly autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>;
private activateAutofillOnPageLoadFromPolicyState: ActiveUserState<boolean>;
readonly activateAutofillOnPageLoadFromPolicy$: Observable<boolean>; readonly activateAutofillOnPageLoadFromPolicy$: Observable<boolean>;
private autofillOnPageLoadPolicyToastHasDisplayedState: ActiveUserState<boolean>;
readonly autofillOnPageLoadPolicyToastHasDisplayed$: Observable<boolean>;
private autoCopyTotpState: ActiveUserState<boolean>;
readonly autoCopyTotp$: Observable<boolean>;
private inlineMenuVisibilityState: GlobalState<InlineMenuVisibilitySetting>; private inlineMenuVisibilityState: GlobalState<InlineMenuVisibilitySetting>;
readonly inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>; readonly inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>;
@@ -110,7 +105,7 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
constructor( constructor(
private stateProvider: StateProvider, private stateProvider: StateProvider,
policyService: PolicyService, private policyService: PolicyService,
) { ) {
this.autofillOnPageLoadState = this.stateProvider.getActive(AUTOFILL_ON_PAGE_LOAD); this.autofillOnPageLoadState = this.stateProvider.getActive(AUTOFILL_ON_PAGE_LOAD);
this.autofillOnPageLoad$ = this.autofillOnPageLoadState.state$.pipe(map((x) => x ?? false)); this.autofillOnPageLoad$ = this.autofillOnPageLoadState.state$.pipe(map((x) => x ?? false));
@@ -122,20 +117,25 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
map((x) => x ?? true), map((x) => x ?? true),
); );
this.autoCopyTotpState = this.stateProvider.getActive(AUTO_COPY_TOTP);
this.autoCopyTotp$ = this.autoCopyTotpState.state$.pipe(map((x) => x ?? false));
this.autofillOnPageLoadCalloutIsDismissedState = this.stateProvider.getActive( this.autofillOnPageLoadCalloutIsDismissedState = this.stateProvider.getActive(
AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED, AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED,
); );
this.autofillOnPageLoadCalloutIsDismissed$ = this.autofillOnPageLoadCalloutIsDismissed$ =
this.autofillOnPageLoadCalloutIsDismissedState.state$.pipe(map((x) => x ?? false)); this.autofillOnPageLoadCalloutIsDismissedState.state$.pipe(map((x) => x ?? false));
this.activateAutofillOnPageLoadFromPolicyState = this.stateProvider.getActive( this.activateAutofillOnPageLoadFromPolicy$ = this.policyService.policyAppliesToActiveUser$(
ACTIVATE_AUTOFILL_ON_PAGE_LOAD_FROM_POLICY, PolicyType.ActivateAutofill,
); );
this.activateAutofillOnPageLoadFromPolicy$ =
this.activateAutofillOnPageLoadFromPolicyState.state$.pipe(map((x) => x ?? false)); this.autofillOnPageLoadPolicyToastHasDisplayedState = this.stateProvider.getActive(
AUTOFILL_ON_PAGE_LOAD_POLICY_TOAST_HAS_DISPLAYED,
);
this.autofillOnPageLoadPolicyToastHasDisplayed$ = this.autofillOnPageLoadState.state$.pipe(
map((x) => x ?? false),
);
this.autoCopyTotpState = this.stateProvider.getActive(AUTO_COPY_TOTP);
this.autoCopyTotp$ = this.autoCopyTotpState.state$.pipe(map((x) => x ?? false));
this.inlineMenuVisibilityState = this.stateProvider.getGlobal(INLINE_MENU_VISIBILITY); this.inlineMenuVisibilityState = this.stateProvider.getGlobal(INLINE_MENU_VISIBILITY);
this.inlineMenuVisibility$ = this.inlineMenuVisibilityState.state$.pipe( this.inlineMenuVisibility$ = this.inlineMenuVisibilityState.state$.pipe(
@@ -146,8 +146,6 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
this.clearClipboardDelay$ = this.clearClipboardDelayState.state$.pipe( this.clearClipboardDelay$ = this.clearClipboardDelayState.state$.pipe(
map((x) => x ?? ClearClipboardDelay.Never), map((x) => x ?? ClearClipboardDelay.Never),
); );
policyService.policies$.pipe(this.handleActivateAutofillPolicy.bind(this)).subscribe();
} }
async setAutofillOnPageLoad(newValue: boolean): Promise<void> { async setAutofillOnPageLoad(newValue: boolean): Promise<void> {
@@ -158,16 +156,16 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
await this.autofillOnPageLoadDefaultState.update(() => newValue); await this.autofillOnPageLoadDefaultState.update(() => newValue);
} }
async setAutoCopyTotp(newValue: boolean): Promise<void> {
await this.autoCopyTotpState.update(() => newValue);
}
async setAutofillOnPageLoadCalloutIsDismissed(newValue: boolean): Promise<void> { async setAutofillOnPageLoadCalloutIsDismissed(newValue: boolean): Promise<void> {
await this.autofillOnPageLoadCalloutIsDismissedState.update(() => newValue); await this.autofillOnPageLoadCalloutIsDismissedState.update(() => newValue);
} }
async setActivateAutofillOnPageLoadFromPolicy(newValue: boolean): Promise<void> { async setAutofillOnPageLoadPolicyToastHasDisplayed(newValue: boolean): Promise<void> {
await this.activateAutofillOnPageLoadFromPolicyState.update(() => newValue); await this.autofillOnPageLoadPolicyToastHasDisplayedState.update(() => newValue);
}
async setAutoCopyTotp(newValue: boolean): Promise<void> {
await this.autoCopyTotpState.update(() => newValue);
} }
async setInlineMenuVisibility(newValue: InlineMenuVisibilitySetting): Promise<void> { async setInlineMenuVisibility(newValue: InlineMenuVisibilitySetting): Promise<void> {
@@ -177,24 +175,4 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
async setClearClipboardDelay(newValue: ClearClipboardDelaySetting): Promise<void> { async setClearClipboardDelay(newValue: ClearClipboardDelaySetting): Promise<void> {
await this.clearClipboardDelayState.update(() => newValue); await this.clearClipboardDelayState.update(() => newValue);
} }
/**
* If the ActivateAutofill policy is enabled, save a flag indicating if we need to
* enable Autofill on page load.
*/
handleActivateAutofillPolicy(policies$: Observable<Policy[]>): Observable<boolean[]> {
return policies$.pipe(
map((policies) => policies.find((p) => p.type == PolicyType.ActivateAutofill && p.enabled)),
filter((p) => p != null),
switchMap(async (_) => [
await firstValueFrom(this.activateAutofillOnPageLoadFromPolicy$),
await firstValueFrom(this.autofillOnPageLoad$),
]),
tap(([activated, autofillEnabled]) => {
if (activated === undefined) {
void this.setActivateAutofillOnPageLoadFromPolicy(!autofillEnabled);
}
}),
);
}
} }

View File

@@ -0,0 +1,7 @@
import { ClearClipboardDelay, AutofillOverlayVisibility } from "../constants";
export type ClearClipboardDelaySetting =
(typeof ClearClipboardDelay)[keyof typeof ClearClipboardDelay];
export type InlineMenuVisibilitySetting =
(typeof AutofillOverlayVisibility)[keyof typeof AutofillOverlayVisibility];

View File

@@ -1,7 +1,15 @@
import { InlineMenuVisibilitySetting } from "../../../../../apps/browser/src/autofill/utils/autofill-overlay.enum";
import { StateDefinitionLike, MigrationHelper } from "../migration-helper"; import { StateDefinitionLike, MigrationHelper } from "../migration-helper";
import { Migrator } from "../migrator"; import { Migrator } from "../migrator";
const AutofillOverlayVisibility = {
Off: 0,
OnButtonClick: 1,
OnFieldFocus: 2,
} as const;
type InlineMenuVisibilitySetting =
(typeof AutofillOverlayVisibility)[keyof typeof AutofillOverlayVisibility];
type ExpectedAccountState = { type ExpectedAccountState = {
settings?: { settings?: {
autoFillOnPageLoadDefault?: boolean; autoFillOnPageLoadDefault?: boolean;

View File

@@ -1,7 +1,18 @@
import { ClearClipboardDelaySetting } from "../../../../../apps/browser/src/autofill/constants";
import { StateDefinitionLike, MigrationHelper } from "../migration-helper"; import { StateDefinitionLike, MigrationHelper } from "../migration-helper";
import { Migrator } from "../migrator"; import { Migrator } from "../migrator";
const ClearClipboardDelay = {
Never: null as null,
TenSeconds: 10,
TwentySeconds: 20,
ThirtySeconds: 30,
OneMinute: 60,
TwoMinutes: 120,
FiveMinutes: 300,
} as const;
type ClearClipboardDelaySetting = (typeof ClearClipboardDelay)[keyof typeof ClearClipboardDelay];
type ExpectedAccountState = { type ExpectedAccountState = {
settings?: { settings?: {
clearClipboard?: ClearClipboardDelaySetting; clearClipboard?: ClearClipboardDelaySetting;

View File

@@ -5,6 +5,12 @@ import {
SUBADDRESS_SETTINGS, SUBADDRESS_SETTINGS,
PASSPHRASE_SETTINGS, PASSPHRASE_SETTINGS,
PASSWORD_SETTINGS, PASSWORD_SETTINGS,
SIMPLE_LOGIN_FORWARDER,
FORWARD_EMAIL_FORWARDER,
FIREFOX_RELAY_FORWARDER,
FASTMAIL_FORWARDER,
DUCK_DUCK_GO_FORWARDER,
ADDY_IO_FORWARDER,
} from "./key-definitions"; } from "./key-definitions";
describe("Key definitions", () => { describe("Key definitions", () => {
@@ -48,6 +54,54 @@ describe("Key definitions", () => {
}); });
}); });
describe("ADDY_IO_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = ADDY_IO_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("DUCK_DUCK_GO_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = DUCK_DUCK_GO_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("FASTMAIL_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = FASTMAIL_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("FIREFOX_RELAY_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = FIREFOX_RELAY_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("FORWARD_EMAIL_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = FORWARD_EMAIL_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("SIMPLE_LOGIN_FORWARDER", () => {
it("should pass through deserialization", () => {
const value: any = {};
const result = SIMPLE_LOGIN_FORWARDER.deserializer(value);
expect(result).toBe(value);
});
});
describe("ENCRYPTED_HISTORY", () => { describe("ENCRYPTED_HISTORY", () => {
it("should pass through deserialization", () => { it("should pass through deserialization", () => {
const value = {}; const value = {};

View File

@@ -5,6 +5,12 @@ import { GeneratedPasswordHistory } from "./password/generated-password-history"
import { PasswordGenerationOptions } from "./password/password-generation-options"; import { PasswordGenerationOptions } from "./password/password-generation-options";
import { CatchallGenerationOptions } from "./username/catchall-generator-options"; import { CatchallGenerationOptions } from "./username/catchall-generator-options";
import { EffUsernameGenerationOptions } from "./username/eff-username-generator-options"; import { EffUsernameGenerationOptions } from "./username/eff-username-generator-options";
import {
ApiOptions,
EmailDomainOptions,
EmailPrefixOptions,
SelfHostedApiOptions,
} from "./username/options/forwarder-options";
import { SubaddressGenerationOptions } from "./username/subaddress-generator-options"; import { SubaddressGenerationOptions } from "./username/subaddress-generator-options";
/** plaintext password generation options */ /** plaintext password generation options */
@@ -52,6 +58,54 @@ export const SUBADDRESS_SETTINGS = new KeyDefinition<SubaddressGenerationOptions
}, },
); );
export const ADDY_IO_FORWARDER = new KeyDefinition<SelfHostedApiOptions & EmailDomainOptions>(
GENERATOR_DISK,
"addyIoForwarder",
{
deserializer: (value) => value,
},
);
export const DUCK_DUCK_GO_FORWARDER = new KeyDefinition<ApiOptions>(
GENERATOR_DISK,
"duckDuckGoForwarder",
{
deserializer: (value) => value,
},
);
export const FASTMAIL_FORWARDER = new KeyDefinition<ApiOptions & EmailPrefixOptions>(
GENERATOR_DISK,
"fastmailForwarder",
{
deserializer: (value) => value,
},
);
export const FIREFOX_RELAY_FORWARDER = new KeyDefinition<ApiOptions>(
GENERATOR_DISK,
"firefoxRelayForwarder",
{
deserializer: (value) => value,
},
);
export const FORWARD_EMAIL_FORWARDER = new KeyDefinition<ApiOptions & EmailDomainOptions>(
GENERATOR_DISK,
"forwardEmailForwarder",
{
deserializer: (value) => value,
},
);
export const SIMPLE_LOGIN_FORWARDER = new KeyDefinition<SelfHostedApiOptions>(
GENERATOR_DISK,
"simpleLoginForwarder",
{
deserializer: (value) => value,
},
);
/** encrypted password generation history */ /** encrypted password generation history */
export const ENCRYPTED_HISTORY = new KeyDefinition<GeneratedPasswordHistory>( export const ENCRYPTED_HISTORY = new KeyDefinition<GeneratedPasswordHistory>(
GENERATOR_DISK, GENERATOR_DISK,

View File

@@ -0,0 +1,73 @@
import { mock } from "jest-mock-extended";
import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { StateProvider } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import { DefaultPolicyEvaluator } from "../default-policy-evaluator";
import { DUCK_DUCK_GO_FORWARDER } from "../key-definitions";
import { SecretState } from "../state/secret-state";
import { ForwarderGeneratorStrategy } from "./forwarder-generator-strategy";
import { ApiOptions } from "./options/forwarder-options";
class TestForwarder extends ForwarderGeneratorStrategy<ApiOptions> {
constructor(
encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
get key() {
// arbitrary.
return DUCK_DUCK_GO_FORWARDER;
}
}
const SomeUser = "some user" as UserId;
const AnotherUser = "another user" as UserId;
describe("ForwarderGeneratorStrategy", () => {
const encryptService = mock<EncryptService>();
const keyService = mock<CryptoService>();
const stateProvider = new FakeStateProvider(mockAccountServiceWith(SomeUser));
describe("durableState", () => {
it("constructs a secret state", () => {
const strategy = new TestForwarder(encryptService, keyService, stateProvider);
const result = strategy.durableState(SomeUser);
expect(result).toBeInstanceOf(SecretState);
});
it("returns the same secret state for a single user", () => {
const strategy = new TestForwarder(encryptService, keyService, stateProvider);
const firstResult = strategy.durableState(SomeUser);
const secondResult = strategy.durableState(SomeUser);
expect(firstResult).toBe(secondResult);
});
it("returns a different secret state for a different user", () => {
const strategy = new TestForwarder(encryptService, keyService, stateProvider);
const firstResult = strategy.durableState(SomeUser);
const secondResult = strategy.durableState(AnotherUser);
expect(firstResult).not.toBe(secondResult);
});
});
it("evaluator returns the default policy evaluator", () => {
const strategy = new TestForwarder(null, null, null);
const result = strategy.evaluator(null);
expect(result).toBeInstanceOf(DefaultPolicyEvaluator);
});
});

View File

@@ -0,0 +1,73 @@
import { PolicyType } from "../../../admin-console/enums";
import { Policy } from "../../../admin-console/models/domain/policy";
import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { KeyDefinition, StateProvider } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import { GeneratorStrategy } from "../abstractions";
import { DefaultPolicyEvaluator } from "../default-policy-evaluator";
import { NoPolicy } from "../no-policy";
import { PaddedDataPacker } from "../state/padded-data-packer";
import { SecretClassifier } from "../state/secret-classifier";
import { SecretState } from "../state/secret-state";
import { UserKeyEncryptor } from "../state/user-key-encryptor";
import { ApiOptions } from "./options/forwarder-options";
const ONE_MINUTE = 60 * 1000;
const OPTIONS_FRAME_SIZE = 512;
/** An email forwarding service configurable through an API. */
export abstract class ForwarderGeneratorStrategy<
Options extends ApiOptions,
> extends GeneratorStrategy<Options, NoPolicy> {
/** Initializes the generator strategy
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/
constructor(
private readonly encryptService: EncryptService,
private readonly keyService: CryptoService,
private stateProvider: StateProvider,
) {
super();
// Uses password generator since there aren't policies
// specific to usernames.
this.policy = PolicyType.PasswordGenerator;
this.cache_ms = ONE_MINUTE;
}
private durableStates = new Map<UserId, SecretState<Options, Record<string, never>>>();
/** {@link GeneratorStrategy.durableState} */
durableState = (userId: UserId) => {
let state = this.durableStates.get(userId);
if (!state) {
const encryptor = this.createEncryptor();
state = SecretState.from(userId, this.key, this.stateProvider, encryptor);
this.durableStates.set(userId, state);
}
return state;
};
private createEncryptor() {
// always exclude request properties
const classifier = SecretClassifier.allSecret<Options>().exclude("website");
// construct the encryptor
const packer = new PaddedDataPacker(OPTIONS_FRAME_SIZE);
return new UserKeyEncryptor(this.encryptService, this.keyService, classifier, packer);
}
/** Determine where forwarder configuration is stored */
protected abstract readonly key: KeyDefinition<Options>;
/** {@link GeneratorStrategy.evaluator} */
evaluator = (_policy: Policy) => {
return new DefaultPolicyEvaluator<Options>();
};
}

View File

@@ -2,22 +2,30 @@
* include Request in test environment. * include Request in test environment.
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { ADDY_IO_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { AddyIoForwarder } from "./addy-io"; import { AddyIoForwarder } from "./addy-io";
import { mockApiService, mockI18nService } from "./mocks.jest"; import { mockApiService, mockI18nService } from "./mocks.jest";
describe("Addy.io Forwarder", () => { describe("Addy.io Forwarder", () => {
it("key returns the Addy IO forwarder key", () => {
const forwarder = new AddyIoForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(ADDY_IO_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -34,11 +42,12 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain, domain,
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -56,11 +65,12 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl, baseUrl,
@@ -83,9 +93,10 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await forwarder.generate(website, { await forwarder.generate({
website,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -107,9 +118,10 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(status, { data: { email } }); const apiService = mockApiService(status, { data: { email } });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -124,11 +136,12 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(401, {}); const apiService = mockApiService(401, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -148,11 +161,12 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(500, {}); const apiService = mockApiService(500, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
@@ -181,11 +195,12 @@ describe("Addy.io Forwarder", () => {
const apiService = mockApiService(statusCode, {}, statusText); const apiService = mockApiService(statusCode, {}, statusText);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new AddyIoForwarder(apiService, i18nService); const forwarder = new AddyIoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",

View File

@@ -1,24 +1,41 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { StateProvider } from "../../../../platform/state";
import { ADDY_IO_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { EmailDomainOptions, Forwarder, SelfHostedApiOptions } from "../options/forwarder-options"; import { EmailDomainOptions, SelfHostedApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for addy.io (formerly anon addy) */ /** Generates a forwarding address for addy.io (formerly anon addy) */
export class AddyIoForwarder implements Forwarder { export class AddyIoForwarder extends ForwarderGeneratorStrategy<
SelfHostedApiOptions & EmailDomainOptions
> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate( get key() {
website: string | null, return ADDY_IO_FORWARDER;
options: SelfHostedApiOptions & EmailDomainOptions, }
): Promise<string> {
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: SelfHostedApiOptions & EmailDomainOptions) => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.AddyIo.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.AddyIo.name);
throw error; throw error;
@@ -32,9 +49,11 @@ export class AddyIoForwarder implements Forwarder {
throw error; throw error;
} }
const descriptionId = let descriptionId = "forwarderGeneratedByWithWebsite";
website && website !== "" ? "forwarderGeneratedByWithWebsite" : "forwarderGeneratedBy"; if (!options.website || options.website === "") {
const description = this.i18nService.t(descriptionId, website ?? ""); descriptionId = "forwarderGeneratedBy";
}
const description = this.i18nService.t(descriptionId, options.website ?? "");
const url = options.baseUrl + "/api/v1/aliases"; const url = options.baseUrl + "/api/v1/aliases";
const request = new Request(url, { const request = new Request(url, {
@@ -70,5 +89,5 @@ export class AddyIoForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.AddyIo.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.AddyIo.name);
throw error; throw error;
} }
} };
} }

View File

@@ -2,22 +2,30 @@
* include Request in test environment. * include Request in test environment.
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { DUCK_DUCK_GO_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { DuckDuckGoForwarder } from "./duck-duck-go"; import { DuckDuckGoForwarder } from "./duck-duck-go";
import { mockApiService, mockI18nService } from "./mocks.jest"; import { mockApiService, mockI18nService } from "./mocks.jest";
describe("DuckDuckGo Forwarder", () => { describe("DuckDuckGo Forwarder", () => {
it("key returns the Duck Duck Go forwarder key", () => {
const forwarder = new DuckDuckGoForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(DUCK_DUCK_GO_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new DuckDuckGoForwarder(apiService, i18nService); const forwarder = new DuckDuckGoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
}), }),
).rejects.toEqual("forwaderInvalidToken"); ).rejects.toEqual("forwaderInvalidToken");
@@ -40,9 +48,10 @@ describe("DuckDuckGo Forwarder", () => {
const apiService = mockApiService(status, { address }); const apiService = mockApiService(status, { address });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new DuckDuckGoForwarder(apiService, i18nService); const forwarder = new DuckDuckGoForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
}); });
@@ -55,11 +64,12 @@ describe("DuckDuckGo Forwarder", () => {
const apiService = mockApiService(401, {}); const apiService = mockApiService(401, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new DuckDuckGoForwarder(apiService, i18nService); const forwarder = new DuckDuckGoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
}), }),
).rejects.toEqual("forwaderInvalidToken"); ).rejects.toEqual("forwaderInvalidToken");
@@ -76,11 +86,12 @@ describe("DuckDuckGo Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new DuckDuckGoForwarder(apiService, i18nService); const forwarder = new DuckDuckGoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
}), }),
).rejects.toEqual("forwarderUnknownError"); ).rejects.toEqual("forwarderUnknownError");
@@ -99,11 +110,12 @@ describe("DuckDuckGo Forwarder", () => {
const apiService = mockApiService(statusCode, {}); const apiService = mockApiService(statusCode, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new DuckDuckGoForwarder(apiService, i18nService); const forwarder = new DuckDuckGoForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
}), }),
).rejects.toEqual("forwarderUnknownError"); ).rejects.toEqual("forwarderUnknownError");

View File

@@ -1,21 +1,39 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { StateProvider } from "../../../../platform/state";
import { DUCK_DUCK_GO_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { ApiOptions, Forwarder } from "../options/forwarder-options"; import { ApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for DuckDuckGo */ /** Generates a forwarding address for DuckDuckGo */
export class DuckDuckGoForwarder implements Forwarder { export class DuckDuckGoForwarder extends ForwarderGeneratorStrategy<ApiOptions> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate(_website: string | null, options: ApiOptions): Promise<string> { get key() {
return DUCK_DUCK_GO_FORWARDER;
}
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: ApiOptions): Promise<string> => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.DuckDuckGo.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.DuckDuckGo.name);
throw error; throw error;
@@ -48,5 +66,5 @@ export class DuckDuckGoForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.DuckDuckGo.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.DuckDuckGo.name);
throw error; throw error;
} }
} };
} }

View File

@@ -3,6 +3,7 @@
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { FASTMAIL_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { FastmailForwarder } from "./fastmail"; import { FastmailForwarder } from "./fastmail";
@@ -45,16 +46,23 @@ const AccountIdSuccess: MockResponse = Object.freeze({
// the tests // the tests
describe("Fastmail Forwarder", () => { describe("Fastmail Forwarder", () => {
it("key returns the Fastmail forwarder key", () => {
const forwarder = new FastmailForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(FASTMAIL_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(AccountIdSuccess, EmptyResponse); const apiService = mockApiService(AccountIdSuccess, EmptyResponse);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -71,11 +79,12 @@ describe("Fastmail Forwarder", () => {
const apiService = mockApiService({ status, body: {} }, EmptyResponse); const apiService = mockApiService({ status, body: {} }, EmptyResponse);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -105,9 +114,10 @@ describe("Fastmail Forwarder", () => {
}); });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -138,11 +148,12 @@ describe("Fastmail Forwarder", () => {
}); });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -165,11 +176,12 @@ describe("Fastmail Forwarder", () => {
const apiService = mockApiService(AccountIdSuccess, { status, body: {} }); const apiService = mockApiService(AccountIdSuccess, { status, body: {} });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -206,11 +218,12 @@ describe("Fastmail Forwarder", () => {
}); });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",
@@ -232,11 +245,12 @@ describe("Fastmail Forwarder", () => {
const apiService = mockApiService(AccountIdSuccess, { status: statusCode, body: {} }); const apiService = mockApiService(AccountIdSuccess, { status: statusCode, body: {} });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FastmailForwarder(apiService, i18nService); const forwarder = new FastmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
prefix: "prefix", prefix: "prefix",

View File

@@ -1,24 +1,39 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { StateProvider } from "../../../../platform/state";
import { FASTMAIL_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { EmailPrefixOptions, Forwarder, ApiOptions } from "../options/forwarder-options"; import { EmailPrefixOptions, ApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for Fastmail */ /** Generates a forwarding address for Fastmail */
export class FastmailForwarder implements Forwarder { export class FastmailForwarder extends ForwarderGeneratorStrategy<ApiOptions & EmailPrefixOptions> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate( get key() {
website: string | null, return FASTMAIL_FORWARDER;
options: ApiOptions & EmailPrefixOptions, }
): Promise<string> {
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: ApiOptions & EmailPrefixOptions) => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.Fastmail.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.Fastmail.name);
throw error; throw error;
@@ -41,7 +56,7 @@ export class FastmailForwarder implements Forwarder {
"new-masked-email": { "new-masked-email": {
state: "enabled", state: "enabled",
description: "", description: "",
forDomain: website, forDomain: options.website,
emailPrefix: options.prefix, emailPrefix: options.prefix,
}, },
}, },
@@ -104,7 +119,7 @@ export class FastmailForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.Fastmail.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.Fastmail.name);
throw error; throw error;
} };
private async getAccountId(options: ApiOptions): Promise<string> { private async getAccountId(options: ApiOptions): Promise<string> {
const requestInit: RequestInit = { const requestInit: RequestInit = {

View File

@@ -2,22 +2,30 @@
* include Request in test environment. * include Request in test environment.
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { FIREFOX_RELAY_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { FirefoxRelayForwarder } from "./firefox-relay"; import { FirefoxRelayForwarder } from "./firefox-relay";
import { mockApiService, mockI18nService } from "./mocks.jest"; import { mockApiService, mockI18nService } from "./mocks.jest";
describe("Firefox Relay Forwarder", () => { describe("Firefox Relay Forwarder", () => {
it("key returns the Firefox Relay forwarder key", () => {
const forwarder = new FirefoxRelayForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(FIREFOX_RELAY_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FirefoxRelayForwarder(apiService, i18nService); const forwarder = new FirefoxRelayForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
}), }),
).rejects.toEqual("forwaderInvalidToken"); ).rejects.toEqual("forwaderInvalidToken");
@@ -40,9 +48,10 @@ describe("Firefox Relay Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FirefoxRelayForwarder(apiService, i18nService); const forwarder = new FirefoxRelayForwarder(apiService, i18nService, null, null, null);
await forwarder.generate(website, { await forwarder.generate({
website,
token: "token", token: "token",
}); });
@@ -62,9 +71,10 @@ describe("Firefox Relay Forwarder", () => {
const apiService = mockApiService(status, { full_address }); const apiService = mockApiService(status, { full_address });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FirefoxRelayForwarder(apiService, i18nService); const forwarder = new FirefoxRelayForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
}); });
@@ -77,11 +87,12 @@ describe("Firefox Relay Forwarder", () => {
const apiService = mockApiService(401, {}); const apiService = mockApiService(401, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FirefoxRelayForwarder(apiService, i18nService); const forwarder = new FirefoxRelayForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
}), }),
).rejects.toEqual("forwaderInvalidToken"); ).rejects.toEqual("forwaderInvalidToken");
@@ -101,11 +112,12 @@ describe("Firefox Relay Forwarder", () => {
const apiService = mockApiService(statusCode, {}); const apiService = mockApiService(statusCode, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new FirefoxRelayForwarder(apiService, i18nService); const forwarder = new FirefoxRelayForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
}), }),
).rejects.toEqual("forwarderUnknownError"); ).rejects.toEqual("forwarderUnknownError");

View File

@@ -1,21 +1,39 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { StateProvider } from "../../../../platform/state";
import { FIREFOX_RELAY_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { Forwarder, ApiOptions } from "../options/forwarder-options"; import { ApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for Firefox Relay */ /** Generates a forwarding address for Firefox Relay */
export class FirefoxRelayForwarder implements Forwarder { export class FirefoxRelayForwarder extends ForwarderGeneratorStrategy<ApiOptions> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate(website: string | null, options: ApiOptions): Promise<string> { get key() {
return FIREFOX_RELAY_FORWARDER;
}
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: ApiOptions) => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.FirefoxRelay.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.FirefoxRelay.name);
throw error; throw error;
@@ -23,9 +41,11 @@ export class FirefoxRelayForwarder implements Forwarder {
const url = "https://relay.firefox.com/api/v1/relayaddresses/"; const url = "https://relay.firefox.com/api/v1/relayaddresses/";
const descriptionId = let descriptionId = "forwarderGeneratedByWithWebsite";
website && website !== "" ? "forwarderGeneratedByWithWebsite" : "forwarderGeneratedBy"; if (!options.website || options.website === "") {
const description = this.i18nService.t(descriptionId, website ?? ""); descriptionId = "forwarderGeneratedBy";
}
const description = this.i18nService.t(descriptionId, options.website ?? "");
const request = new Request(url, { const request = new Request(url, {
redirect: "manual", redirect: "manual",
@@ -37,7 +57,7 @@ export class FirefoxRelayForwarder implements Forwarder {
}), }),
body: JSON.stringify({ body: JSON.stringify({
enabled: true, enabled: true,
generated_for: website, generated_for: options.website,
description, description,
}), }),
}); });
@@ -53,5 +73,5 @@ export class FirefoxRelayForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.FirefoxRelay.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.FirefoxRelay.name);
throw error; throw error;
} }
} };
} }

View File

@@ -2,22 +2,30 @@
* include Request in test environment. * include Request in test environment.
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { FORWARD_EMAIL_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { ForwardEmailForwarder } from "./forward-email"; import { ForwardEmailForwarder } from "./forward-email";
import { mockApiService, mockI18nService } from "./mocks.jest"; import { mockApiService, mockI18nService } from "./mocks.jest";
describe("ForwardEmail Forwarder", () => { describe("ForwardEmail Forwarder", () => {
it("key returns the Forward Email forwarder key", () => {
const forwarder = new ForwardEmailForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(FORWARD_EMAIL_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
domain: "example.com", domain: "example.com",
}), }),
@@ -36,11 +44,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain, domain,
}), }),
@@ -65,9 +74,10 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await forwarder.generate(website, { await forwarder.generate({
website,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}); });
@@ -92,9 +102,10 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(status, response); const apiService = mockApiService(status, response);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}); });
@@ -108,11 +119,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(401, {}); const apiService = mockApiService(401, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}), }),
@@ -132,11 +144,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(401, { message: "A message" }); const apiService = mockApiService(401, { message: "A message" });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}), }),
@@ -158,11 +171,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(500, json); const apiService = mockApiService(500, json);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}), }),
@@ -191,11 +205,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(statusCode, { message }); const apiService = mockApiService(statusCode, { message });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}), }),
@@ -225,11 +240,12 @@ describe("ForwardEmail Forwarder", () => {
const apiService = mockApiService(statusCode, { error }); const apiService = mockApiService(statusCode, { error });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new ForwardEmailForwarder(apiService, i18nService); const forwarder = new ForwardEmailForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
domain: "example.com", domain: "example.com",
}), }),

View File

@@ -1,25 +1,42 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { Utils } from "../../../../platform/misc/utils"; import { Utils } from "../../../../platform/misc/utils";
import { StateProvider } from "../../../../platform/state";
import { FORWARD_EMAIL_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { EmailDomainOptions, Forwarder, ApiOptions } from "../options/forwarder-options"; import { EmailDomainOptions, ApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for Forward Email */ /** Generates a forwarding address for Forward Email */
export class ForwardEmailForwarder implements Forwarder { export class ForwardEmailForwarder extends ForwarderGeneratorStrategy<
ApiOptions & EmailDomainOptions
> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate( get key() {
website: string | null, return FORWARD_EMAIL_FORWARDER;
options: ApiOptions & EmailDomainOptions, }
): Promise<string> {
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: ApiOptions & EmailDomainOptions) => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.ForwardEmail.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.ForwardEmail.name);
throw error; throw error;
@@ -31,9 +48,11 @@ export class ForwardEmailForwarder implements Forwarder {
const url = `https://api.forwardemail.net/v1/domains/${options.domain}/aliases`; const url = `https://api.forwardemail.net/v1/domains/${options.domain}/aliases`;
const descriptionId = let descriptionId = "forwarderGeneratedByWithWebsite";
website && website !== "" ? "forwarderGeneratedByWithWebsite" : "forwarderGeneratedBy"; if (!options.website || options.website === "") {
const description = this.i18nService.t(descriptionId, website ?? ""); descriptionId = "forwarderGeneratedBy";
}
const description = this.i18nService.t(descriptionId, options.website ?? "");
const request = new Request(url, { const request = new Request(url, {
redirect: "manual", redirect: "manual",
@@ -44,7 +63,7 @@ export class ForwardEmailForwarder implements Forwarder {
"Content-Type": "application/json", "Content-Type": "application/json",
}), }),
body: JSON.stringify({ body: JSON.stringify({
labels: website, labels: options.website,
description, description,
}), }),
}); });
@@ -75,5 +94,5 @@ export class ForwardEmailForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.ForwardEmail.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.ForwardEmail.name);
throw error; throw error;
} }
} };
} }

View File

@@ -2,22 +2,30 @@
* include Request in test environment. * include Request in test environment.
* @jest-environment ../../../../shared/test.environment.ts * @jest-environment ../../../../shared/test.environment.ts
*/ */
import { SIMPLE_LOGIN_FORWARDER } from "../../key-definitions";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { mockApiService, mockI18nService } from "./mocks.jest"; import { mockApiService, mockI18nService } from "./mocks.jest";
import { SimpleLoginForwarder } from "./simple-login"; import { SimpleLoginForwarder } from "./simple-login";
describe("SimpleLogin Forwarder", () => { describe("SimpleLogin Forwarder", () => {
it("key returns the Simple Login forwarder key", () => {
const forwarder = new SimpleLoginForwarder(null, null, null, null, null);
expect(forwarder.key).toBe(SIMPLE_LOGIN_FORWARDER);
});
describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => { describe("generate(string | null, SelfHostedApiOptions & EmailDomainOptions)", () => {
it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => { it.each([null, ""])("throws an error if the token is missing (token = %p)", async (token) => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token, token,
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}), }),
@@ -36,11 +44,12 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
baseUrl, baseUrl,
}), }),
@@ -62,9 +71,10 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(200, {}); const apiService = mockApiService(200, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await forwarder.generate(website, { await forwarder.generate({
website,
token: "token", token: "token",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}); });
@@ -85,9 +95,10 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(status, { alias }); const apiService = mockApiService(status, { alias });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
const result = await forwarder.generate(null, { const result = await forwarder.generate({
website: null,
token: "token", token: "token",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}); });
@@ -101,11 +112,12 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(401, {}); const apiService = mockApiService(401, {});
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}), }),
@@ -126,11 +138,12 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(500, body); const apiService = mockApiService(500, body);
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}), }),
@@ -159,11 +172,12 @@ describe("SimpleLogin Forwarder", () => {
const apiService = mockApiService(statusCode, { error }); const apiService = mockApiService(statusCode, { error });
const i18nService = mockI18nService(); const i18nService = mockI18nService();
const forwarder = new SimpleLoginForwarder(apiService, i18nService); const forwarder = new SimpleLoginForwarder(apiService, i18nService, null, null, null);
await expect( await expect(
async () => async () =>
await forwarder.generate(null, { await forwarder.generate({
website: null,
token: "token", token: "token",
baseUrl: "https://api.example.com", baseUrl: "https://api.example.com",
}), }),

View File

@@ -1,21 +1,39 @@
import { ApiService } from "../../../../abstractions/api.service"; import { ApiService } from "../../../../abstractions/api.service";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../../platform/abstractions/i18n.service";
import { StateProvider } from "../../../../platform/state";
import { SIMPLE_LOGIN_FORWARDER } from "../../key-definitions";
import { ForwarderGeneratorStrategy } from "../forwarder-generator-strategy";
import { Forwarders } from "../options/constants"; import { Forwarders } from "../options/constants";
import { Forwarder, SelfHostedApiOptions } from "../options/forwarder-options"; import { SelfHostedApiOptions } from "../options/forwarder-options";
/** Generates a forwarding address for Simple Login */ /** Generates a forwarding address for Simple Login */
export class SimpleLoginForwarder implements Forwarder { export class SimpleLoginForwarder extends ForwarderGeneratorStrategy<SelfHostedApiOptions> {
/** Instantiates the forwarder /** Instantiates the forwarder
* @param apiService used for ajax requests to the forwarding service * @param apiService used for ajax requests to the forwarding service
* @param i18nService used to look up error strings * @param i18nService used to look up error strings
* @param encryptService protects sensitive forwarder options
* @param keyService looks up the user key when protecting data.
* @param stateProvider creates the durable state for options storage
*/ */
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
private i18nService: I18nService, private i18nService: I18nService,
) {} encryptService: EncryptService,
keyService: CryptoService,
stateProvider: StateProvider,
) {
super(encryptService, keyService, stateProvider);
}
/** {@link Forwarder.generate} */ /** {@link ForwarderGeneratorStrategy.key} */
async generate(website: string, options: SelfHostedApiOptions): Promise<string> { get key() {
return SIMPLE_LOGIN_FORWARDER;
}
/** {@link ForwarderGeneratorStrategy.generate} */
generate = async (options: SelfHostedApiOptions) => {
if (!options.token || options.token === "") { if (!options.token || options.token === "") {
const error = this.i18nService.t("forwaderInvalidToken", Forwarders.SimpleLogin.name); const error = this.i18nService.t("forwaderInvalidToken", Forwarders.SimpleLogin.name);
throw error; throw error;
@@ -27,11 +45,11 @@ export class SimpleLoginForwarder implements Forwarder {
let url = options.baseUrl + "/api/alias/random/new"; let url = options.baseUrl + "/api/alias/random/new";
let noteId = "forwarderGeneratedBy"; let noteId = "forwarderGeneratedBy";
if (website && website !== "") { if (options.website && options.website !== "") {
url += "?hostname=" + website; url += "?hostname=" + options.website;
noteId = "forwarderGeneratedByWithWebsite"; noteId = "forwarderGeneratedByWithWebsite";
} }
const note = this.i18nService.t(noteId, website ?? ""); const note = this.i18nService.t(noteId, options.website ?? "");
const request = new Request(url, { const request = new Request(url, {
redirect: "manual", redirect: "manual",
@@ -60,5 +78,5 @@ export class SimpleLoginForwarder implements Forwarder {
const error = this.i18nService.t("forwarderUnknownError", Forwarders.SimpleLogin.name); const error = this.i18nService.t("forwarderUnknownError", Forwarders.SimpleLogin.name);
throw error; throw error;
} }
} };
} }

View File

@@ -85,27 +85,33 @@ export const DefaultOptions: UsernameGeneratorOptions = Object.freeze({
forwarders: Object.freeze({ forwarders: Object.freeze({
service: Forwarders.Fastmail.id, service: Forwarders.Fastmail.id,
fastMail: Object.freeze({ fastMail: Object.freeze({
website: null,
domain: "", domain: "",
prefix: "", prefix: "",
token: "", token: "",
}), }),
addyIo: Object.freeze({ addyIo: Object.freeze({
website: null,
baseUrl: "https://app.addy.io", baseUrl: "https://app.addy.io",
domain: "", domain: "",
token: "", token: "",
}), }),
forwardEmail: Object.freeze({ forwardEmail: Object.freeze({
website: null,
token: "", token: "",
domain: "", domain: "",
}), }),
simpleLogin: Object.freeze({ simpleLogin: Object.freeze({
website: null,
baseUrl: "https://app.simplelogin.io", baseUrl: "https://app.simplelogin.io",
token: "", token: "",
}), }),
duckDuckGo: Object.freeze({ duckDuckGo: Object.freeze({
website: null,
token: "", token: "",
}), }),
firefoxRelay: Object.freeze({ firefoxRelay: Object.freeze({
website: null,
token: "", token: "",
}), }),
}), }),

View File

@@ -1,5 +1,3 @@
import { EncString } from "../../../../platform/models/domain/enc-string";
/** Identifiers for email forwarding services. /** Identifiers for email forwarding services.
* @remarks These are used to select forwarder-specific options. * @remarks These are used to select forwarder-specific options.
* The must be kept in sync with the forwarder implementations. * The must be kept in sync with the forwarder implementations.
@@ -24,26 +22,24 @@ export type ForwarderMetadata = {
validForSelfHosted: boolean; validForSelfHosted: boolean;
}; };
/** An email forwarding service configurable through an API. */
export interface Forwarder {
/** Generate a forwarding email.
* @param website The website to generate a username for.
* @param options The options to use when generating the username.
*/
generate(website: string | null, options: ApiOptions): Promise<string>;
}
/** Options common to all forwarder APIs */ /** Options common to all forwarder APIs */
export type ApiOptions = { export type ApiOptions = {
/** bearer token that authenticates bitwarden to the forwarder. /** bearer token that authenticates bitwarden to the forwarder.
* This is required to issue an API request. * This is required to issue an API request.
*/ */
token?: string; token?: string;
} & RequestOptions;
/** encrypted bearer token that authenticates bitwarden to the forwarder. /** Options that provide contextual information about the application state
* This is used to store the token at rest and must be decoded before use. * when a forwarder is invoked.
* @remarks these fields should always be omitted when saving options.
*/ */
encryptedToken?: EncString; export type RequestOptions = {
/** @param website The domain of the website the generated email is used
* within. This should be set to `null` when the request is not specific
* to any website.
*/
website: string | null;
}; };
/** Api configuration for forwarders that support self-hosted installations. */ /** Api configuration for forwarders that support self-hosted installations. */

View File

@@ -24,27 +24,33 @@ const TestOptions: UsernameGeneratorOptions = {
forwarders: { forwarders: {
service: Forwarders.Fastmail.id, service: Forwarders.Fastmail.id,
fastMail: { fastMail: {
website: null,
domain: "httpbin.com", domain: "httpbin.com",
prefix: "foo", prefix: "foo",
token: "some-token", token: "some-token",
}, },
addyIo: { addyIo: {
website: null,
baseUrl: "https://app.addy.io", baseUrl: "https://app.addy.io",
domain: "example.com", domain: "example.com",
token: "some-token", token: "some-token",
}, },
forwardEmail: { forwardEmail: {
website: null,
token: "some-token", token: "some-token",
domain: "example.com", domain: "example.com",
}, },
simpleLogin: { simpleLogin: {
website: null,
baseUrl: "https://app.simplelogin.io", baseUrl: "https://app.simplelogin.io",
token: "some-token", token: "some-token",
}, },
duckDuckGo: { duckDuckGo: {
website: null,
token: "some-token", token: "some-token",
}, },
firefoxRelay: { firefoxRelay: {
website: null,
token: "some-token", token: "some-token",
}, },
}, },

18
package-lock.json generated
View File

@@ -94,7 +94,7 @@
"@storybook/jest": "0.2.3", "@storybook/jest": "0.2.3",
"@storybook/testing-library": "0.2.2", "@storybook/testing-library": "0.2.2",
"@types/argon2-browser": "1.18.1", "@types/argon2-browser": "1.18.1",
"@types/chrome": "0.0.243", "@types/chrome": "0.0.262",
"@types/duo_web_sdk": "2.7.1", "@types/duo_web_sdk": "2.7.1",
"@types/firefox-webext-browser": "111.0.1", "@types/firefox-webext-browser": "111.0.1",
"@types/inquirer": "8.2.10", "@types/inquirer": "8.2.10",
@@ -128,7 +128,7 @@
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "11.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "6.8.1", "css-loader": "6.8.1",
"electron": "28.2.5", "electron": "28.2.6",
"electron-builder": "24.9.1", "electron-builder": "24.9.1",
"electron-log": "5.0.1", "electron-log": "5.0.1",
"electron-reload": "2.0.0-alpha.1", "electron-reload": "2.0.0-alpha.1",
@@ -234,7 +234,7 @@
}, },
"apps/desktop": { "apps/desktop": {
"name": "@bitwarden/desktop", "name": "@bitwarden/desktop",
"version": "2024.2.1", "version": "2024.2.2",
"hasInstallScript": true, "hasInstallScript": true,
"license": "GPL-3.0" "license": "GPL-3.0"
}, },
@@ -10867,9 +10867,9 @@
} }
}, },
"node_modules/@types/chrome": { "node_modules/@types/chrome": {
"version": "0.0.243", "version": "0.0.262",
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.243.tgz", "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.262.tgz",
"integrity": "sha512-4PHv0kxxxpZFHWPBiJJ9TWH8kbx0567j1b2djnhpJjpiSGNI7UKkz7dSEECBtQ0B3N5nQTMwSB/5IopkWGAbEA==", "integrity": "sha512-TOoj3dqSYE13PD2fRuMQ6X6pggEvL9rRk/yOYOyWE6sfqRWxsJm4VoVm+wr9pkr4Sht/M5t7FFL4vXato8d1gA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/filesystem": "*", "@types/filesystem": "*",
@@ -17807,9 +17807,9 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "28.2.5", "version": "28.2.6",
"resolved": "https://registry.npmjs.org/electron/-/electron-28.2.5.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-28.2.6.tgz",
"integrity": "sha512-qlvQkDNVAzN647NpiJJw7GYJqE0NwK4+1evkhrQ0Xv6Qgab1EtN50G4oDr4/x/+O5pGUG2P5d3isXu+37O3RDw==", "integrity": "sha512-RuhbW+ifvh3DqnVlHCcCKhKIFOxTktq1GN1gkIkEZ8y5LEZfcjOkxB2s6Fd1S6MzsMZbiJti+ZJG5hXS4SDVLQ==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {

View File

@@ -55,7 +55,7 @@
"@storybook/jest": "0.2.3", "@storybook/jest": "0.2.3",
"@storybook/testing-library": "0.2.2", "@storybook/testing-library": "0.2.2",
"@types/argon2-browser": "1.18.1", "@types/argon2-browser": "1.18.1",
"@types/chrome": "0.0.243", "@types/chrome": "0.0.262",
"@types/duo_web_sdk": "2.7.1", "@types/duo_web_sdk": "2.7.1",
"@types/firefox-webext-browser": "111.0.1", "@types/firefox-webext-browser": "111.0.1",
"@types/inquirer": "8.2.10", "@types/inquirer": "8.2.10",
@@ -89,7 +89,7 @@
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "11.0.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "6.8.1", "css-loader": "6.8.1",
"electron": "28.2.5", "electron": "28.2.6",
"electron-builder": "24.9.1", "electron-builder": "24.9.1",
"electron-log": "5.0.1", "electron-log": "5.0.1",
"electron-reload": "2.0.0-alpha.1", "electron-reload": "2.0.0-alpha.1",