mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 18:23:31 +00:00
[PM-5189] Merging main into current branch
This commit is contained in:
@@ -7,5 +7,6 @@ checkmarx:
|
||||
scan:
|
||||
configs:
|
||||
sast:
|
||||
presetName: "BW ASA Premium"
|
||||
# Exclude spec files, and test specific files
|
||||
filter: "!*.spec.ts,!**/spec/**,!apps/desktop/native-messaging-test-runner/**"
|
||||
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -61,6 +61,7 @@ apps/web/src/app/billing @bitwarden/team-billing-dev
|
||||
libs/angular/src/billing @bitwarden/team-billing-dev
|
||||
libs/common/src/billing @bitwarden/team-billing-dev
|
||||
libs/billing @bitwarden/team-billing-dev
|
||||
bitwarden_license/bit-web/src/app/billing @bitwarden/team-billing-dev
|
||||
|
||||
## Platform team files ##
|
||||
apps/browser/src/platform @bitwarden/team-platform-dev
|
||||
|
||||
4
.github/renovate.json
vendored
4
.github/renovate.json
vendored
@@ -16,6 +16,10 @@
|
||||
"matchManagers": ["cargo"],
|
||||
"commitMessagePrefix": "[deps] Platform:"
|
||||
},
|
||||
{
|
||||
"groupName": "napi",
|
||||
"matchPackageNames": ["napi", "napi-build", "napi-derive"]
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["typescript", "zone.js"],
|
||||
"matchUpdateTypes": ["major", "minor"],
|
||||
|
||||
20
.github/workflows/build-desktop.yml
vendored
20
.github/workflows/build-desktop.yml
vendored
@@ -444,7 +444,10 @@ jobs:
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs: setup
|
||||
env:
|
||||
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||
@@ -602,7 +605,10 @@ jobs:
|
||||
|
||||
macos-package-github:
|
||||
name: MacOS Package GitHub Release Assets
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs:
|
||||
- browser-build
|
||||
- macos-build
|
||||
@@ -808,7 +814,10 @@ jobs:
|
||||
|
||||
macos-package-mas:
|
||||
name: MacOS Package Prod Release Asset
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs:
|
||||
- browser-build
|
||||
- macos-build
|
||||
@@ -1006,7 +1015,10 @@ jobs:
|
||||
macos-package-dev:
|
||||
name: MacOS Package Dev Release Asset
|
||||
if: false # We need to look into how code signing works for dev
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs:
|
||||
- browser-build
|
||||
- macos-build
|
||||
|
||||
4
.github/workflows/build-web.yml
vendored
4
.github/workflows/build-web.yml
vendored
@@ -299,7 +299,7 @@ jobs:
|
||||
keyvault: "bitwarden-ci"
|
||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
||||
|
||||
- name: Trigger web vault deploy
|
||||
- name: Trigger web vault deploy using GitHub Run ID
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
'environment': 'USDEV',
|
||||
'branch-or-tag': 'main'
|
||||
'build-web-run-id': '${{ github.run_id }}'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
34
.github/workflows/deploy-web.yml
vendored
34
.github/workflows/deploy-web.yml
vendored
@@ -27,6 +27,10 @@ on:
|
||||
description: "Debug mode"
|
||||
type: boolean
|
||||
default: true
|
||||
build-web-run-id:
|
||||
description: "Build-web workflow Run ID to use for artifact download"
|
||||
type: string
|
||||
required: false
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
@@ -46,6 +50,10 @@ on:
|
||||
description: "Debug mode"
|
||||
type: boolean
|
||||
default: true
|
||||
build-web-run-id:
|
||||
description: "Build-web workflow Run ID to use for artifact download"
|
||||
type: string
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
deployments: write
|
||||
@@ -168,7 +176,20 @@ jobs:
|
||||
env:
|
||||
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }}
|
||||
steps:
|
||||
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}'
|
||||
if: ${{ inputs.build-web-run-id }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
id: download-latest-artifacts
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: build-web.yml
|
||||
path: apps/web
|
||||
workflow_conclusion: success
|
||||
run_id: ${{ inputs.build-web-run-id }}
|
||||
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }}
|
||||
|
||||
- name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}'
|
||||
if: ${{ !inputs.build-web-run-id }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
id: download-artifacts
|
||||
continue-on-error: true
|
||||
@@ -249,7 +270,20 @@ jobs:
|
||||
keyvault: ${{ needs.setup.outputs.retrieve-secrets-keyvault }}
|
||||
secrets: "sa-bitwarden-web-vault-name,sp-bitwarden-web-vault-password,sp-bitwarden-web-vault-appid,sp-bitwarden-web-vault-tenant"
|
||||
|
||||
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}'
|
||||
if: ${{ inputs.build-web-run-id }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
id: download-latest-artifacts
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: build-web.yml
|
||||
path: apps/web
|
||||
workflow_conclusion: success
|
||||
run_id: ${{ inputs.build-web-run-id }}
|
||||
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }}
|
||||
|
||||
- name: 'Download cloud asset from branch/tag: ${{ inputs.branch-or-tag }}'
|
||||
if: ${{ !inputs.build-web-run-id }}
|
||||
uses: bitwarden/gh-actions/download-artifacts@main
|
||||
with:
|
||||
workflow: build-web.yml
|
||||
|
||||
15
.github/workflows/release-desktop-beta.yml
vendored
15
.github/workflows/release-desktop-beta.yml
vendored
@@ -393,7 +393,10 @@ jobs:
|
||||
|
||||
macos-build:
|
||||
name: MacOS Build
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs: setup
|
||||
env:
|
||||
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
|
||||
@@ -522,7 +525,10 @@ jobs:
|
||||
|
||||
macos-package-github:
|
||||
name: MacOS Package GitHub Release Assets
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs:
|
||||
- setup
|
||||
- macos-build
|
||||
@@ -732,7 +738,10 @@ jobs:
|
||||
|
||||
macos-package-mas:
|
||||
name: MacOS Package Prod Release Asset
|
||||
runs-on: macos-13
|
||||
# Note, this workflow is running on macOS 11 to maintain compatibility with macOS 10.15 Catalina,
|
||||
# as the newer versions will case the native modules to be incompatible with older macOS systems
|
||||
# This version should stay pinned until we drop support for macOS 10.15, or we drop the native modules
|
||||
runs-on: macos-11
|
||||
needs:
|
||||
- setup
|
||||
- macos-build
|
||||
|
||||
9
.github/workflows/scan.yml
vendored
9
.github/workflows/scan.yml
vendored
@@ -10,8 +10,6 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
check-run:
|
||||
name: Check PR run
|
||||
@@ -22,6 +20,8 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-run
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
additional_params: --report-format sarif --output-path . ${{ env.INCREMENTAL }}
|
||||
|
||||
- name: Upload Checkmarx results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
|
||||
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
|
||||
with:
|
||||
sarif_file: cx_result.sarif
|
||||
|
||||
@@ -51,6 +51,9 @@ jobs:
|
||||
name: Quality scan
|
||||
runs-on: ubuntu-22.04
|
||||
needs: check-run
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
|
||||
28
.github/workflows/version-bump.yml
vendored
28
.github/workflows/version-bump.yml
vendored
@@ -367,21 +367,27 @@ jobs:
|
||||
id: set-final-version-output
|
||||
run: |
|
||||
if [[ "${{ steps.bump-browser-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
echo "version_browser=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-browser-version-automatic.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ steps.calculate-next-browser-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-cli-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
echo "version_browser=${{ steps.calculate-next-browser-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${{ steps.bump-cli-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version_cli=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-cli-version-automatic.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ steps.calculate-next-cli-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-desktop-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
echo "version_cli=${{ steps.calculate-next-cli-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${{ steps.bump-desktop-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version_desktop=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-desktop-version-automatic.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ steps.calculate-next-desktop-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-web-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
echo "version_desktop=${{ steps.calculate-next-desktop-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
if [[ "${{ steps.bump-web-version-override.outcome }}" = "success" ]]; then
|
||||
echo "version_web=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ steps.bump-web-version-automatic.outcome }}" = "success" ]]; then
|
||||
echo "version=${{ steps.calculate-next-web-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
echo "version_web=${{ steps.calculate-next-web-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Check if version changed
|
||||
|
||||
26
apps/browser/.eslintrc.json
Normal file
26
apps/browser/.eslintrc.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"webextensions": true
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["src/**/*.ts"],
|
||||
"excludedFiles": [
|
||||
"src/**/{content,popup,spec}/**/*.ts",
|
||||
"src/**/autofill/{notification,overlay}/**/*.ts",
|
||||
"src/**/autofill/**/{autofill-overlay-content,collect-autofill-content,dom-element-visibility,insert-autofill-content}.service.ts",
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"rules": {
|
||||
"no-restricted-globals": [
|
||||
"error",
|
||||
{
|
||||
"name": "window",
|
||||
"message": "The `window` object is not available in service workers and may not be available within the background script. Consider using `self`, `globalThis`, or another global property instead."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/browser",
|
||||
"version": "2024.3.0",
|
||||
"version": "2024.3.1",
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
|
||||
|
||||
@@ -709,7 +709,7 @@
|
||||
"message": "Vis indstillinger i kontekstmenuen"
|
||||
},
|
||||
"contextMenuItemDesc": {
|
||||
"message": "Brug et sekundært klik for at få adgang til adgangskodegenerering og matchende logins til hjemmesiden."
|
||||
"message": "Brug et sekundært klik for at tilgå adgangskodegenerering og matchende logins til webstedet."
|
||||
},
|
||||
"contextMenuItemDescAlt": {
|
||||
"message": "Brug et sekundært klik for at få adgang til adgangskodegenerering og matchende logins til webstedet. Gælder alle indloggede konti."
|
||||
@@ -1033,7 +1033,7 @@
|
||||
"message": "Server URL"
|
||||
},
|
||||
"apiUrl": {
|
||||
"message": "API server URL"
|
||||
"message": "API-server URL"
|
||||
},
|
||||
"webVaultUrl": {
|
||||
"message": "Web-boks server URL"
|
||||
@@ -1574,7 +1574,7 @@
|
||||
"message": "Advarsel: Dette er en ikke-sikret HTTP side, og alle indsendte oplysninger kan potentielt ses og ændres af andre. Dette login blev oprindeligt gemt på en sikker (HTTPS) side."
|
||||
},
|
||||
"insecurePageWarningFillPrompt": {
|
||||
"message": "Do you still wish to fill this login?"
|
||||
"message": "Ønsker dette login stadig udfyldt?"
|
||||
},
|
||||
"autofillIframeWarning": {
|
||||
"message": "Formularen hostes af et andet domæne end URI'en for det gemte login. Vælg OK for at autoudfylde alligevel, eller Afbryd for at stoppe."
|
||||
@@ -1712,7 +1712,7 @@
|
||||
"message": "Biometri mislykkedes"
|
||||
},
|
||||
"biometricsFailedDesc": {
|
||||
"message": "Biometri kan ikke fuldføres, overvej at bruge en hovedadgangskode eller logge ud og ind igen. Fortsætter problemet, kontakt Bitwarden-supporten."
|
||||
"message": "Biometri kan ikke gennemføres. Overvej at bruge en hovedadgangskode eller at logge ud. Fortsætter problemet, kontakt Bitwarden-supporten."
|
||||
},
|
||||
"nativeMessaginPermissionErrorTitle": {
|
||||
"message": "Tilladelse ikke givet"
|
||||
@@ -2027,7 +2027,7 @@
|
||||
"message": "Minutter"
|
||||
},
|
||||
"vaultTimeoutPolicyInEffect": {
|
||||
"message": "Organisationspolitikker har sat maks. tilladt boks-timeout. til $HOURS$ time(r) og $MINUTES$ minut(ter).",
|
||||
"message": "Organisationspolitikkerne har fastsat den maksimalt tilladte boks-timeout til $HOURS$ time(r) og $MINUTES$ minut(ter).",
|
||||
"placeholders": {
|
||||
"hours": {
|
||||
"content": "$1",
|
||||
@@ -2314,7 +2314,7 @@
|
||||
"message": "Sådan autoudfyldes"
|
||||
},
|
||||
"autofillSelectInfoWithCommand": {
|
||||
"message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.",
|
||||
"message": "Vælg et emne fra denne skærm, brug genvejen $COMMAND$ eller udforsk andre valgmuligheder i Indstillinger.",
|
||||
"placeholders": {
|
||||
"command": {
|
||||
"content": "$1",
|
||||
@@ -2323,7 +2323,7 @@
|
||||
}
|
||||
},
|
||||
"autofillSelectInfoWithoutCommand": {
|
||||
"message": "Select an item from this screen, or explore other options in settings."
|
||||
"message": "Vælg et emne fra denne skærm eller udforsk andre valgmuligheder i Indstillinger."
|
||||
},
|
||||
"gotIt": {
|
||||
"message": "Forstået"
|
||||
@@ -2700,7 +2700,7 @@
|
||||
}
|
||||
},
|
||||
"launchDuoAndFollowStepsToFinishLoggingIn": {
|
||||
"message": "Start DUO og følg trinene for at fuldføre indlogningen."
|
||||
"message": "Start Duo og følg trinnene for at fuldføre indlogningen."
|
||||
},
|
||||
"duoRequiredForAccount": {
|
||||
"message": "Duo-totrinsindlogning kræves for kontoen."
|
||||
@@ -2712,7 +2712,7 @@
|
||||
"message": "Pop ud-udvidelse"
|
||||
},
|
||||
"launchDuo": {
|
||||
"message": "Start DUO"
|
||||
"message": "Start Duo"
|
||||
},
|
||||
"importFormatError": {
|
||||
"message": "Data er ikke korrekt formateret. Tjek importfilen og forsøg igen."
|
||||
|
||||
@@ -2999,5 +2999,11 @@
|
||||
"saveCipherAttemptFailed": {
|
||||
"message": "Error saving credentials. Check console for details.",
|
||||
"description": "Notification message for when saving credentials has failed."
|
||||
},
|
||||
"removePasskey": {
|
||||
"message": "Remove passkey"
|
||||
},
|
||||
"passkeyRemoved": {
|
||||
"message": "Passkey removed"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"message": "Masuk"
|
||||
},
|
||||
"enterpriseSingleSignOn": {
|
||||
"message": "Sistem Masuk Tunggal Perusahaan"
|
||||
"message": "SSO Perusahaan"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Batal"
|
||||
|
||||
@@ -2913,7 +2913,7 @@
|
||||
"message": "Mudar de conta"
|
||||
},
|
||||
"switchAccounts": {
|
||||
"message": "Mudar de contas"
|
||||
"message": "Mudar de conta"
|
||||
},
|
||||
"switchToAccount": {
|
||||
"message": "Mudar para conta"
|
||||
|
||||
@@ -1500,7 +1500,7 @@
|
||||
"message": "无效 PIN 码。"
|
||||
},
|
||||
"tooManyInvalidPinEntryAttemptsLoggingOut": {
|
||||
"message": "无效的 PIN 输入尝试次数过多,正在退出登录。"
|
||||
"message": "无效的 PIN 输入尝试次数过多,正在注销。"
|
||||
},
|
||||
"unlockWithBiometrics": {
|
||||
"message": "使用生物识别解锁"
|
||||
@@ -1742,7 +1742,7 @@
|
||||
"message": "Bitwarden 将不会询问是否为这些域名保存登录信息。您必须刷新页面才能使更改生效。"
|
||||
},
|
||||
"excludedDomainsDescAlt": {
|
||||
"message": "Bitwarden 不会询问保存所有已登录的账户的这些域名的登录信息。您必须刷新页面才能使更改生效。"
|
||||
"message": "Bitwarden 不会询问保存所有已登录的账户的这些域名的登录信息。必须刷新页面才能使更改生效。"
|
||||
},
|
||||
"excludedDomainsInvalidDomain": {
|
||||
"message": "$DOMAIN$ 不是一个有效的域名",
|
||||
@@ -2314,7 +2314,7 @@
|
||||
"message": "如何自动填充"
|
||||
},
|
||||
"autofillSelectInfoWithCommand": {
|
||||
"message": "从此界面选择一个项目,使用快捷方式 $COMMAND$,或探索设置中的其他选项。",
|
||||
"message": "从此界面选择一个项目,使用快捷键 $COMMAND$,或探索设置中的其他选项。",
|
||||
"placeholders": {
|
||||
"command": {
|
||||
"content": "$1",
|
||||
@@ -2335,10 +2335,10 @@
|
||||
"message": "自动填充键盘快捷键"
|
||||
},
|
||||
"autofillShortcutNotSet": {
|
||||
"message": "未设置自动填充快捷方式。请在浏览器设置中更改此设置。"
|
||||
"message": "未设置自动填充快捷键。可在浏览器的设置中更改它。"
|
||||
},
|
||||
"autofillShortcutText": {
|
||||
"message": "自动填充快捷方式为: $COMMAND$。在浏览器设置中更改此项。",
|
||||
"message": "自动填充快捷键为:$COMMAND$。可在浏览器的设置中更改它。",
|
||||
"placeholders": {
|
||||
"command": {
|
||||
"content": "$1",
|
||||
@@ -2928,7 +2928,7 @@
|
||||
"message": "已达到账户上限。请注销一个账户后再添加其他账户。"
|
||||
},
|
||||
"active": {
|
||||
"message": "已生效"
|
||||
"message": "活动的"
|
||||
},
|
||||
"locked": {
|
||||
"message": "已锁定"
|
||||
@@ -2961,7 +2961,7 @@
|
||||
}
|
||||
},
|
||||
"commonImportFormats": {
|
||||
"message": "通用格式",
|
||||
"message": "常规格式",
|
||||
"description": "Label indicating the most common import formats"
|
||||
},
|
||||
"overrideDefaultBrowserAutofillTitle": {
|
||||
@@ -2969,7 +2969,7 @@
|
||||
"description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior"
|
||||
},
|
||||
"overrideDefaultBrowserAutofillDescription": {
|
||||
"message": "忽略此设置可能会导致 Bitwarden 自动填充菜单与浏览器自带功能产生冲突。",
|
||||
"message": "忽略此选项可能会导致 Bitwarden 自动填充菜单与浏览器自带功能产生冲突。",
|
||||
"description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior"
|
||||
},
|
||||
"overrideDefaultBrowserAutoFillSettings": {
|
||||
|
||||
@@ -23,13 +23,18 @@ import {
|
||||
stateServiceFactory,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
|
||||
import { AccountServiceInitOptions, accountServiceFactory } from "./account-service.factory";
|
||||
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
|
||||
|
||||
type AuthServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type AuthServiceInitOptions = AuthServiceFactoryOptions &
|
||||
AccountServiceInitOptions &
|
||||
MessagingServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
StateServiceInitOptions;
|
||||
StateServiceInitOptions &
|
||||
TokenServiceInitOptions;
|
||||
|
||||
export function authServiceFactory(
|
||||
cache: { authService?: AbstractAuthService } & CachedServices,
|
||||
@@ -41,10 +46,12 @@ export function authServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new AuthService(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await messagingServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,9 +39,13 @@ import {
|
||||
platformUtilsServiceFactory,
|
||||
} from "../../../platform/background/service-factories/platform-utils-service.factory";
|
||||
import {
|
||||
StateServiceInitOptions,
|
||||
stateServiceFactory,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
StateProviderInitOptions,
|
||||
stateProviderFactory,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
import {
|
||||
SecureStorageServiceInitOptions,
|
||||
secureStorageServiceFactory,
|
||||
} from "../../../platform/background/service-factories/storage-service.factory";
|
||||
|
||||
import {
|
||||
UserDecryptionOptionsServiceInitOptions,
|
||||
@@ -55,11 +59,12 @@ export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactor
|
||||
CryptoFunctionServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
EncryptServiceInitOptions &
|
||||
StateServiceInitOptions &
|
||||
AppIdServiceInitOptions &
|
||||
DevicesApiServiceInitOptions &
|
||||
I18nServiceInitOptions &
|
||||
PlatformUtilsServiceInitOptions &
|
||||
StateProviderInitOptions &
|
||||
SecureStorageServiceInitOptions &
|
||||
UserDecryptionOptionsServiceInitOptions;
|
||||
|
||||
export function deviceTrustCryptoServiceFactory(
|
||||
@@ -76,11 +81,12 @@ export function deviceTrustCryptoServiceFactory(
|
||||
await cryptoFunctionServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await encryptServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
await appIdServiceFactory(cache, opts),
|
||||
await devicesApiServiceFactory(cache, opts),
|
||||
await i18nServiceFactory(cache, opts),
|
||||
await platformUtilsServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
await secureStorageServiceFactory(cache, opts),
|
||||
await userDecryptionOptionsServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -27,9 +27,9 @@ import {
|
||||
LogServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/log-service.factory";
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-service.factory";
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
import { TokenServiceInitOptions, tokenServiceFactory } from "./token-service.factory";
|
||||
|
||||
@@ -40,13 +40,13 @@ type KeyConnectorServiceFactoryOptions = FactoryOptions & {
|
||||
};
|
||||
|
||||
export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
|
||||
StateServiceInitOptions &
|
||||
CryptoServiceInitOptions &
|
||||
ApiServiceInitOptions &
|
||||
TokenServiceInitOptions &
|
||||
LogServiceInitOptions &
|
||||
OrganizationServiceInitOptions &
|
||||
KeyGenerationServiceInitOptions;
|
||||
KeyGenerationServiceInitOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function keyConnectorServiceFactory(
|
||||
cache: { keyConnectorService?: AbstractKeyConnectorService } & CachedServices,
|
||||
@@ -58,7 +58,6 @@ export function keyConnectorServiceFactory(
|
||||
opts,
|
||||
async () =>
|
||||
new KeyConnectorService(
|
||||
await stateServiceFactory(cache, opts),
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await apiServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
@@ -66,6 +65,7 @@ export function keyConnectorServiceFactory(
|
||||
await organizationServiceFactory(cache, opts),
|
||||
await keyGenerationServiceFactory(cache, opts),
|
||||
opts.keyConnectorServiceOptions.logoutCallback,
|
||||
await stateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { LoginEmailServiceAbstraction, LoginEmailService } from "@bitwarden/auth/common";
|
||||
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
FactoryOptions,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type LoginEmailServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type LoginEmailServiceInitOptions = LoginEmailServiceFactoryOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function loginEmailServiceFactory(
|
||||
cache: { loginEmailService?: LoginEmailServiceAbstraction } & CachedServices,
|
||||
opts: LoginEmailServiceInitOptions,
|
||||
): Promise<LoginEmailServiceAbstraction> {
|
||||
return factory(
|
||||
cache,
|
||||
"loginEmailService",
|
||||
opts,
|
||||
async () => new LoginEmailService(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import { TokenService as AbstractTokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||
|
||||
import {
|
||||
EncryptServiceInitOptions,
|
||||
encryptServiceFactory,
|
||||
} from "../../../platform/background/service-factories/encrypt-service.factory";
|
||||
import {
|
||||
FactoryOptions,
|
||||
CachedServices,
|
||||
@@ -10,6 +14,14 @@ import {
|
||||
GlobalStateProviderInitOptions,
|
||||
globalStateProviderFactory,
|
||||
} from "../../../platform/background/service-factories/global-state-provider.factory";
|
||||
import {
|
||||
KeyGenerationServiceInitOptions,
|
||||
keyGenerationServiceFactory,
|
||||
} from "../../../platform/background/service-factories/key-generation-service.factory";
|
||||
import {
|
||||
LogServiceInitOptions,
|
||||
logServiceFactory,
|
||||
} from "../../../platform/background/service-factories/log-service.factory";
|
||||
import {
|
||||
PlatformUtilsServiceInitOptions,
|
||||
platformUtilsServiceFactory,
|
||||
@@ -29,7 +41,10 @@ export type TokenServiceInitOptions = TokenServiceFactoryOptions &
|
||||
SingleUserStateProviderInitOptions &
|
||||
GlobalStateProviderInitOptions &
|
||||
PlatformUtilsServiceInitOptions &
|
||||
SecureStorageServiceInitOptions;
|
||||
SecureStorageServiceInitOptions &
|
||||
KeyGenerationServiceInitOptions &
|
||||
EncryptServiceInitOptions &
|
||||
LogServiceInitOptions;
|
||||
|
||||
export function tokenServiceFactory(
|
||||
cache: { tokenService?: AbstractTokenService } & CachedServices,
|
||||
@@ -45,6 +60,9 @@ export function tokenServiceFactory(
|
||||
await globalStateProviderFactory(cache, opts),
|
||||
(await platformUtilsServiceFactory(cache, opts)).supportsSecureStorage(),
|
||||
await secureStorageServiceFactory(cache, opts),
|
||||
await keyGenerationServiceFactory(cache, opts),
|
||||
await encryptServiceFactory(cache, opts),
|
||||
await logServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
|
||||
import { HintComponent as BaseHintComponent } from "@bitwarden/angular/auth/components/hint.component";
|
||||
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -20,9 +20,9 @@ export class HintComponent extends BaseHintComponent {
|
||||
apiService: ApiService,
|
||||
logService: LogService,
|
||||
private route: ActivatedRoute,
|
||||
loginService: LoginService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
) {
|
||||
super(router, i18nService, apiService, platformUtilsService, logService, loginService);
|
||||
super(router, i18nService, apiService, platformUtilsService, logService, loginEmailService);
|
||||
|
||||
super.onSuccessfulSubmit = async () => {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</form>
|
||||
<p class="createAccountLink">
|
||||
{{ "newAroundHere" | i18n }}
|
||||
<a routerLink="/register" (click)="setFormValues()">{{ "createAccount" | i18n }}</a>
|
||||
<a routerLink="/register" (click)="setLoginEmailValues()">{{ "createAccount" | i18n }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
||||
|
||||
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
|
||||
import { AccountSwitcherService } from "./account-switching/services/account-switcher.service";
|
||||
|
||||
@@ -29,38 +28,32 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
private stateService: StateService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private i18nService: I18nService,
|
||||
private environmentService: EnvironmentService,
|
||||
private loginService: LoginService,
|
||||
private loginEmailService: LoginEmailServiceAbstraction,
|
||||
private accountSwitcherService: AccountSwitcherService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
let savedEmail = this.loginService.getEmail();
|
||||
const rememberEmail = this.loginService.getRememberEmail();
|
||||
const email = this.loginEmailService.getEmail();
|
||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||
|
||||
if (savedEmail != null) {
|
||||
this.formGroup.patchValue({
|
||||
email: savedEmail,
|
||||
rememberEmail: rememberEmail,
|
||||
});
|
||||
if (email != null) {
|
||||
this.formGroup.patchValue({ email, rememberEmail });
|
||||
} else {
|
||||
savedEmail = await this.stateService.getRememberedEmail();
|
||||
if (savedEmail != null) {
|
||||
this.formGroup.patchValue({
|
||||
email: savedEmail,
|
||||
rememberEmail: true,
|
||||
});
|
||||
const storedEmail = await firstValueFrom(this.loginEmailService.storedEmail$);
|
||||
|
||||
if (storedEmail != null) {
|
||||
this.formGroup.patchValue({ email: storedEmail, rememberEmail: true });
|
||||
}
|
||||
}
|
||||
|
||||
this.environmentSelector.onOpenSelfHostedSettings
|
||||
.pipe(takeUntil(this.destroyed$))
|
||||
.subscribe(() => {
|
||||
this.setFormValues();
|
||||
this.setLoginEmailValues();
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["environment"]);
|
||||
@@ -76,8 +69,9 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
return this.accountSwitcherService.availableAccounts$;
|
||||
}
|
||||
|
||||
submit() {
|
||||
async submit() {
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
@@ -87,15 +81,12 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loginService.setEmail(this.formGroup.value.email);
|
||||
this.loginService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["login"], { queryParams: { email: this.formGroup.value.email } });
|
||||
this.setLoginEmailValues();
|
||||
await this.router.navigate(["login"], { queryParams: { email: this.formGroup.value.email } });
|
||||
}
|
||||
|
||||
setFormValues() {
|
||||
this.loginService.setEmail(this.formGroup.value.email);
|
||||
this.loginService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||
setLoginEmailValues() {
|
||||
this.loginEmailService.setEmail(this.formGroup.value.email);
|
||||
this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaul
|
||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
@@ -62,6 +63,7 @@ export class LockComponent extends BaseLockComponent {
|
||||
pinCryptoService: PinCryptoServiceAbstraction,
|
||||
private routerService: BrowserRouterService,
|
||||
biometricStateService: BiometricStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
@@ -84,6 +86,7 @@ export class LockComponent extends BaseLockComponent {
|
||||
userVerificationService,
|
||||
pinCryptoService,
|
||||
biometricStateService,
|
||||
accountService,
|
||||
);
|
||||
this.successRoute = "/tabs/current";
|
||||
this.isInitialLockScreen = (window as any).previousPopupUrl == null;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Location } from "@angular/common";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { LoginViaAuthRequestComponent as BaseLoginWithDeviceComponent } from "@bitwarden/angular/auth/components/login-via-auth-request.component";
|
||||
import {
|
||||
AuthRequestServiceAbstraction,
|
||||
LoginStrategyServiceAbstraction,
|
||||
LoginEmailServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
@@ -28,10 +29,7 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
|
||||
selector: "app-login-via-auth-request",
|
||||
templateUrl: "login-via-auth-request.component.html",
|
||||
})
|
||||
export class LoginViaAuthRequestComponent
|
||||
extends BaseLoginWithDeviceComponent
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
|
||||
constructor(
|
||||
router: Router,
|
||||
cryptoService: CryptoService,
|
||||
@@ -47,11 +45,12 @@ export class LoginViaAuthRequestComponent
|
||||
anonymousHubService: AnonymousHubService,
|
||||
validationService: ValidationService,
|
||||
stateService: StateService,
|
||||
loginService: LoginService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
syncService: SyncService,
|
||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
||||
authRequestService: AuthRequestServiceAbstraction,
|
||||
loginStrategyService: LoginStrategyServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
private location: Location,
|
||||
) {
|
||||
super(
|
||||
@@ -69,10 +68,11 @@ export class LoginViaAuthRequestComponent
|
||||
anonymousHubService,
|
||||
validationService,
|
||||
stateService,
|
||||
loginService,
|
||||
loginEmailService,
|
||||
deviceTrustCryptoService,
|
||||
authRequestService,
|
||||
loginStrategyService,
|
||||
accountService,
|
||||
);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="button" class="btn link" routerLink="/hint" (click)="setFormValues()">
|
||||
<button type="button" class="btn link" routerLink="/hint" (click)="setLoginEmailValues()">
|
||||
<b>{{ "getMasterPasswordHint" | i18n }}</b>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,9 +5,11 @@ import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component";
|
||||
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
|
||||
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import {
|
||||
LoginStrategyServiceAbstraction,
|
||||
LoginEmailServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
@@ -46,7 +48,7 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
formBuilder: FormBuilder,
|
||||
formValidationErrorService: FormValidationErrorsService,
|
||||
route: ActivatedRoute,
|
||||
loginService: LoginService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
||||
) {
|
||||
@@ -66,7 +68,7 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
formBuilder,
|
||||
formValidationErrorService,
|
||||
route,
|
||||
loginService,
|
||||
loginEmailService,
|
||||
ssoLoginService,
|
||||
webAuthnLoginService,
|
||||
);
|
||||
@@ -77,8 +79,8 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
this.showPasswordless = flagEnabled("showPasswordless");
|
||||
|
||||
if (this.showPasswordless) {
|
||||
this.formGroup.controls.email.setValue(this.loginService.getEmail());
|
||||
this.formGroup.controls.rememberEmail.setValue(this.loginService.getRememberEmail());
|
||||
this.formGroup.controls.email.setValue(this.loginEmailService.getEmail());
|
||||
this.formGroup.controls.rememberEmail.setValue(this.loginEmailService.getRememberEmail());
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.validateEmail();
|
||||
@@ -94,7 +96,7 @@ export class LoginComponent extends BaseLoginComponent {
|
||||
async launchSsoBrowser() {
|
||||
// Save off email for SSO
|
||||
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
|
||||
await this.loginService.saveEmailSettings();
|
||||
await this.loginEmailService.saveEmailSettings();
|
||||
// Generate necessary sso params
|
||||
const passwordOptions: any = {
|
||||
type: "password",
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
|
||||
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
|
||||
|
||||
@Injectable()
|
||||
export class UnauthGuardService extends BaseUnauthGuardService {
|
||||
protected homepage = "tabs/current";
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -44,7 +44,7 @@ export class SsoComponent extends BaseSsoComponent {
|
||||
environmentService: EnvironmentService,
|
||||
logService: LogService,
|
||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||
configService: ConfigServiceAbstraction,
|
||||
configService: ConfigService,
|
||||
protected authService: AuthService,
|
||||
@Inject(WINDOW) private win: Window,
|
||||
) {
|
||||
|
||||
@@ -7,16 +7,16 @@ import { TwoFactorComponent as BaseTwoFactorComponent } from "@bitwarden/angular
|
||||
import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
|
||||
import {
|
||||
LoginStrategyServiceAbstraction,
|
||||
LoginEmailServiceAbstraction,
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@@ -57,9 +57,9 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
logService: LogService,
|
||||
twoFactorService: TwoFactorService,
|
||||
appIdService: AppIdService,
|
||||
loginService: LoginService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||
configService: ConfigServiceAbstraction,
|
||||
configService: ConfigService,
|
||||
ssoLoginService: SsoLoginServiceAbstraction,
|
||||
private dialogService: DialogService,
|
||||
@Inject(WINDOW) protected win: Window,
|
||||
@@ -78,7 +78,7 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
|
||||
logService,
|
||||
twoFactorService,
|
||||
appIdService,
|
||||
loginService,
|
||||
loginEmailService,
|
||||
userDecryptionOptionsService,
|
||||
ssoLoginService,
|
||||
configService,
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class WebRequestBackground {
|
||||
private authService: AuthService,
|
||||
) {
|
||||
if (BrowserApi.isManifestVersion(2)) {
|
||||
this.webRequest = (window as any).chrome.webRequest;
|
||||
this.webRequest = chrome.webRequest;
|
||||
}
|
||||
this.isFirefox = platformUtilsService.isFirefox();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
authServiceFactory,
|
||||
AuthServiceInitOptions,
|
||||
} from "../../auth/background/service-factories/auth-service.factory";
|
||||
import { KeyConnectorServiceInitOptions } from "../../auth/background/service-factories/key-connector-service.factory";
|
||||
import { userVerificationServiceFactory } from "../../auth/background/service-factories/user-verification-service.factory";
|
||||
import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
|
||||
import { autofillSettingsServiceFactory } from "../../autofill/background/service_factories/autofill-settings-service.factory";
|
||||
@@ -78,7 +79,9 @@ export class ContextMenuClickedHandler {
|
||||
|
||||
static async mv3Create(cachedServices: CachedServices) {
|
||||
const stateFactory = new StateFactory(GlobalState, Account);
|
||||
const serviceOptions: AuthServiceInitOptions & CipherServiceInitOptions = {
|
||||
const serviceOptions: AuthServiceInitOptions &
|
||||
CipherServiceInitOptions &
|
||||
KeyConnectorServiceInitOptions = {
|
||||
apiServiceOptions: {
|
||||
logoutCallback: NOT_IMPLEMENTED,
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ function loadAutofiller() {
|
||||
let pageHref: string = null;
|
||||
let filledThisHref = false;
|
||||
let delayFillTimeout: number;
|
||||
let doFillInterval: NodeJS.Timeout;
|
||||
let doFillInterval: number | NodeJS.Timeout;
|
||||
const handleExtensionDisconnect = () => {
|
||||
clearDoFillInterval();
|
||||
clearDelayFillTimeout();
|
||||
|
||||
@@ -139,7 +139,7 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", adjustHeight);
|
||||
globalThis.addEventListener("resize", adjustHeight);
|
||||
adjustHeight();
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ function setupLogoLink(i18n: Record<string, string>) {
|
||||
function setNotificationBarTheme() {
|
||||
let theme = notificationBarIframeInitData.theme;
|
||||
if (theme === ThemeType.System) {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
theme = globalThis.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ThemeType.Dark
|
||||
: ThemeType.Light;
|
||||
}
|
||||
@@ -393,5 +393,5 @@ function setNotificationBarTheme() {
|
||||
}
|
||||
|
||||
function postMessageToParent(message: NotificationBarWindowMessage) {
|
||||
window.parent.postMessage(message, windowMessageOrigin || "*");
|
||||
globalThis.parent.postMessage(message, windowMessageOrigin || "*");
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf
|
||||
let borderColor: string;
|
||||
let verifiedTheme = theme;
|
||||
if (verifiedTheme === ThemeType.System) {
|
||||
verifiedTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
verifiedTheme = globalThis.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ThemeType.Dark
|
||||
: ThemeType.Light;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
||||
private ciphers: OverlayCipherData[] = [];
|
||||
private ciphersList: HTMLUListElement;
|
||||
private cipherListScrollIsDebounced = false;
|
||||
private cipherListScrollDebounceTimeout: NodeJS.Timeout;
|
||||
private cipherListScrollDebounceTimeout: number | NodeJS.Timeout;
|
||||
private currentCipherIndex = 0;
|
||||
private readonly showCiphersPerPage = 6;
|
||||
private readonly overlayListWindowMessageHandlers: OverlayListWindowMessageHandlers = {
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface PageDetail {
|
||||
export interface AutoFillOptions {
|
||||
cipher: CipherView;
|
||||
pageDetails: PageDetail[];
|
||||
doc?: typeof window.document;
|
||||
doc?: typeof self.document;
|
||||
tab: chrome.tabs.Tab;
|
||||
skipUsernameOnlyFill?: boolean;
|
||||
onlyEmptyFields?: boolean;
|
||||
|
||||
@@ -33,7 +33,7 @@ describe("a placeholder", () => {
|
||||
//
|
||||
// const defaultWindowReadyState = document.readyState;
|
||||
// const defaultDocumentVisibilityState = document.visibilityState;
|
||||
// describe("AutofillOverlayContentService", () => {
|
||||
// describe.skip("AutofillOverlayContentService", () => {
|
||||
// let autofillOverlayContentService: AutofillOverlayContentService;
|
||||
// let sendExtensionMessageSpy: jest.SpyInstance;
|
||||
//
|
||||
@@ -177,12 +177,10 @@ describe("a placeholder", () => {
|
||||
// autofillFieldData = mock<AutofillField>();
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that are readonly", () => {
|
||||
// it("ignores fields that are readonly", async () => {
|
||||
// autofillFieldData.readonly = true;
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -190,12 +188,10 @@ describe("a placeholder", () => {
|
||||
// expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that contain a disabled attribute", () => {
|
||||
// it("ignores fields that contain a disabled attribute", async () => {
|
||||
// autofillFieldData.disabled = true;
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -203,12 +199,10 @@ describe("a placeholder", () => {
|
||||
// expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that are not viewable", () => {
|
||||
// it("ignores fields that are not viewable", async () => {
|
||||
// autofillFieldData.viewable = false;
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -217,12 +211,10 @@ describe("a placeholder", () => {
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that are part of the ExcludedOverlayTypes", () => {
|
||||
// AutoFillConstants.ExcludedOverlayTypes.forEach((excludedType) => {
|
||||
// AutoFillConstants.ExcludedOverlayTypes.forEach(async (excludedType) => {
|
||||
// autofillFieldData.type = excludedType;
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -231,12 +223,10 @@ describe("a placeholder", () => {
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that contain the keyword `search`", () => {
|
||||
// it("ignores fields that contain the keyword `search`", async () => {
|
||||
// autofillFieldData.placeholder = "search";
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -244,12 +234,10 @@ describe("a placeholder", () => {
|
||||
// expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that contain the keyword `captcha` ", () => {
|
||||
// it("ignores fields that contain the keyword `captcha` ", async () => {
|
||||
// autofillFieldData.placeholder = "captcha";
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -257,12 +245,10 @@ describe("a placeholder", () => {
|
||||
// expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// it("ignores fields that do not appear as a login field", () => {
|
||||
// it("ignores fields that do not appear as a login field", async () => {
|
||||
// autofillFieldData.placeholder = "not-a-login-field";
|
||||
//
|
||||
// // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// // eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
// autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
@@ -271,6 +257,17 @@ describe("a placeholder", () => {
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// it("skips setup on fields that have been previously set up", async () => {
|
||||
// autofillOverlayContentService["formFieldElements"].add(autofillFieldElement);
|
||||
//
|
||||
// await autofillOverlayContentService.setupAutofillOverlayListenerOnField(
|
||||
// autofillFieldElement,
|
||||
// autofillFieldData,
|
||||
// );
|
||||
//
|
||||
// expect(autofillFieldElement.addEventListener).not.toHaveBeenCalled();
|
||||
// });
|
||||
//
|
||||
// describe("identifies the overlay visibility setting", () => {
|
||||
// it("defaults the overlay visibility setting to `OnFieldFocus` if a value is not set", async () => {
|
||||
// sendExtensionMessageSpy.mockResolvedValueOnce(undefined);
|
||||
|
||||
@@ -63,7 +63,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
autofillFieldData: AutofillField,
|
||||
) {
|
||||
if (this.isIgnoredField(autofillFieldData)) {
|
||||
if (this.isIgnoredField(autofillFieldData) || this.formFieldElements.has(formFieldElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -620,7 +620,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
private async getBoundingClientRectFromIntersectionObserver(
|
||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||
): Promise<DOMRectReadOnly | null> {
|
||||
if (!("IntersectionObserver" in window) && !("IntersectionObserverEntry" in window)) {
|
||||
if (!("IntersectionObserver" in globalThis) && !("IntersectionObserverEntry" in globalThis)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
||||
|
||||
if (
|
||||
this.focusedFieldData.focusedFieldRects?.top > 0 &&
|
||||
this.focusedFieldData.focusedFieldRects?.top < window.innerHeight + window.scrollY
|
||||
this.focusedFieldData.focusedFieldRects?.top < globalThis.innerHeight + globalThis.scrollY
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
|
||||
export default class AutofillService implements AutofillServiceInterface {
|
||||
private openVaultItemPasswordRepromptPopout = openVaultItemPasswordRepromptPopout;
|
||||
private openPasswordRepromptPopoutDebounce: NodeJS.Timeout;
|
||||
private openPasswordRepromptPopoutDebounce: number | NodeJS.Timeout;
|
||||
private currentlyOpeningPasswordRepromptPopout = false;
|
||||
private autofillScriptPortsSet = new Set<chrome.runtime.Port>();
|
||||
static searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
||||
|
||||
@@ -27,6 +27,7 @@ describe("CollectAutofillContentService", () => {
|
||||
const domElementVisibilityService = new DomElementVisibilityService();
|
||||
const autofillOverlayContentService = new AutofillOverlayContentService();
|
||||
let collectAutofillContentService: CollectAutofillContentService;
|
||||
const mockIntersectionObserver = mock<IntersectionObserver>();
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = mockLoginForm;
|
||||
@@ -34,6 +35,7 @@ describe("CollectAutofillContentService", () => {
|
||||
domElementVisibilityService,
|
||||
autofillOverlayContentService,
|
||||
);
|
||||
window.IntersectionObserver = jest.fn(() => mockIntersectionObserver);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -2527,10 +2529,10 @@ describe("CollectAutofillContentService", () => {
|
||||
});
|
||||
|
||||
updatedAttributes.forEach((attribute) => {
|
||||
it(`will update the ${attribute} value for the field element`, async () => {
|
||||
it(`will update the ${attribute} value for the field element`, () => {
|
||||
jest.spyOn(collectAutofillContentService["autofillFieldElements"], "set");
|
||||
|
||||
await collectAutofillContentService["updateAutofillFieldElementData"](
|
||||
collectAutofillContentService["updateAutofillFieldElementData"](
|
||||
attribute,
|
||||
fieldElement,
|
||||
autofillField,
|
||||
@@ -2543,10 +2545,10 @@ describe("CollectAutofillContentService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("will not update an attribute value if it is not present in the updateActions object", async () => {
|
||||
it("will not update an attribute value if it is not present in the updateActions object", () => {
|
||||
jest.spyOn(collectAutofillContentService["autofillFieldElements"], "set");
|
||||
|
||||
await collectAutofillContentService["updateAutofillFieldElementData"](
|
||||
collectAutofillContentService["updateAutofillFieldElementData"](
|
||||
"random-attribute",
|
||||
fieldElement,
|
||||
autofillField,
|
||||
@@ -2555,4 +2557,67 @@ describe("CollectAutofillContentService", () => {
|
||||
expect(collectAutofillContentService["autofillFieldElements"].set).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleFormElementIntersection", () => {
|
||||
let isFormFieldViewableSpy: jest.SpyInstance;
|
||||
let setupAutofillOverlayListenerOnFieldSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
isFormFieldViewableSpy = jest.spyOn(
|
||||
collectAutofillContentService["domElementVisibilityService"],
|
||||
"isFormFieldViewable",
|
||||
);
|
||||
setupAutofillOverlayListenerOnFieldSpy = jest.spyOn(
|
||||
collectAutofillContentService["autofillOverlayContentService"],
|
||||
"setupAutofillOverlayListenerOnField",
|
||||
);
|
||||
});
|
||||
|
||||
it("skips the initial intersection event for an observed element", async () => {
|
||||
const formFieldElement = document.createElement("input") as ElementWithOpId<FormFieldElement>;
|
||||
collectAutofillContentService["elementInitializingIntersectionObserver"].add(
|
||||
formFieldElement,
|
||||
);
|
||||
const entries = [
|
||||
{ target: formFieldElement, isIntersecting: true },
|
||||
] as unknown as IntersectionObserverEntry[];
|
||||
|
||||
await collectAutofillContentService["handleFormElementIntersection"](entries);
|
||||
|
||||
expect(isFormFieldViewableSpy).not.toHaveBeenCalled();
|
||||
expect(setupAutofillOverlayListenerOnFieldSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips setting up the overlay listeners on a field that is not viewable", async () => {
|
||||
const formFieldElement = document.createElement("input") as ElementWithOpId<FormFieldElement>;
|
||||
const entries = [
|
||||
{ target: formFieldElement, isIntersecting: true },
|
||||
] as unknown as IntersectionObserverEntry[];
|
||||
isFormFieldViewableSpy.mockReturnValueOnce(false);
|
||||
|
||||
await collectAutofillContentService["handleFormElementIntersection"](entries);
|
||||
|
||||
expect(isFormFieldViewableSpy).toHaveBeenCalledWith(formFieldElement);
|
||||
expect(setupAutofillOverlayListenerOnFieldSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets up the overlay listeners on a viewable field", async () => {
|
||||
const formFieldElement = document.createElement("input") as ElementWithOpId<FormFieldElement>;
|
||||
const autofillField = mock<AutofillField>();
|
||||
const entries = [
|
||||
{ target: formFieldElement, isIntersecting: true },
|
||||
] as unknown as IntersectionObserverEntry[];
|
||||
isFormFieldViewableSpy.mockReturnValueOnce(true);
|
||||
collectAutofillContentService["autofillFieldElements"].set(formFieldElement, autofillField);
|
||||
collectAutofillContentService["intersectionObserver"] = mockIntersectionObserver;
|
||||
|
||||
await collectAutofillContentService["handleFormElementIntersection"](entries);
|
||||
|
||||
expect(isFormFieldViewableSpy).toHaveBeenCalledWith(formFieldElement);
|
||||
expect(setupAutofillOverlayListenerOnFieldSpy).toHaveBeenCalledWith(
|
||||
formFieldElement,
|
||||
autofillField,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,8 +38,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
private autofillFormElements: AutofillFormElements = new Map();
|
||||
private autofillFieldElements: AutofillFieldElements = new Map();
|
||||
private currentLocationHref = "";
|
||||
private intersectionObserver: IntersectionObserver;
|
||||
private elementInitializingIntersectionObserver: Set<Element> = new Set();
|
||||
private mutationObserver: MutationObserver;
|
||||
private updateAutofillElementsAfterMutationTimeout: NodeJS.Timeout;
|
||||
private updateAutofillElementsAfterMutationTimeout: number | NodeJS.Timeout;
|
||||
private readonly updateAfterMutationTimeoutDelay = 1000;
|
||||
private readonly ignoredInputTypes = new Set([
|
||||
"hidden",
|
||||
@@ -70,6 +72,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
this.setupMutationObserver();
|
||||
}
|
||||
|
||||
if (!this.intersectionObserver) {
|
||||
this.setupIntersectionObserver();
|
||||
}
|
||||
|
||||
if (!this.domRecentlyMutated && this.noFieldsFound) {
|
||||
return this.getFormattedPageDetails({}, []);
|
||||
}
|
||||
@@ -180,7 +186,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
): AutofillPageDetails {
|
||||
return {
|
||||
title: document.title,
|
||||
url: (document.defaultView || window).location.href,
|
||||
url: (document.defaultView || globalThis).location.href,
|
||||
documentUrl: document.location.href,
|
||||
forms: autofillFormsData,
|
||||
fields: autofillFieldsData,
|
||||
@@ -240,7 +246,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
* @private
|
||||
*/
|
||||
private getFormActionAttribute(element: ElementWithOpId<HTMLFormElement>): string {
|
||||
return new URL(this.getPropertyOrAttribute(element, "action"), window.location.href).href;
|
||||
return new URL(this.getPropertyOrAttribute(element, "action"), globalThis.location.href).href;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,11 +366,14 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
tagName: this.getAttributeLowerCase(element, "tagName"),
|
||||
};
|
||||
|
||||
if (!autofillFieldBase.viewable) {
|
||||
this.elementInitializingIntersectionObserver.add(element);
|
||||
this.intersectionObserver.observe(element);
|
||||
}
|
||||
|
||||
if (elementIsSpanElement(element)) {
|
||||
this.cacheAutofillFieldElement(index, element, autofillFieldBase);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
||||
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
||||
element,
|
||||
autofillFieldBase,
|
||||
);
|
||||
@@ -407,9 +416,10 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
};
|
||||
|
||||
this.cacheAutofillFieldElement(index, element, autofillField);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(element, autofillField);
|
||||
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
||||
element,
|
||||
autofillField,
|
||||
);
|
||||
return autofillField;
|
||||
};
|
||||
|
||||
@@ -1189,8 +1199,6 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.updateAutofillFieldElementData(
|
||||
attributeName,
|
||||
targetElement as ElementWithOpId<FormFieldElement>,
|
||||
@@ -1232,13 +1240,12 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
|
||||
/**
|
||||
* Updates the autofill field element data based on the passed attribute name.
|
||||
*
|
||||
* @param {string} attributeName
|
||||
* @param {ElementWithOpId<FormFieldElement>} element
|
||||
* @param {AutofillField} dataTarget
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
private async updateAutofillFieldElementData(
|
||||
private updateAutofillFieldElementData(
|
||||
attributeName: string,
|
||||
element: ElementWithOpId<FormFieldElement>,
|
||||
dataTarget: AutofillField,
|
||||
@@ -1304,6 +1311,52 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
return attributeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an IntersectionObserver to observe found form
|
||||
* field elements that are not viewable in the viewport.
|
||||
*/
|
||||
private setupIntersectionObserver() {
|
||||
this.intersectionObserver = new IntersectionObserver(this.handleFormElementIntersection, {
|
||||
root: null,
|
||||
rootMargin: "0px",
|
||||
threshold: 1.0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles observed form field elements that are not viewable in the viewport.
|
||||
* Will re-evaluate the visibility of the element and set up the autofill
|
||||
* overlay listeners on the field if it is viewable.
|
||||
*
|
||||
* @param entries - The entries observed by the IntersectionObserver
|
||||
*/
|
||||
private handleFormElementIntersection = async (entries: IntersectionObserverEntry[]) => {
|
||||
for (let entryIndex = 0; entryIndex < entries.length; entryIndex++) {
|
||||
const entry = entries[entryIndex];
|
||||
const formFieldElement = entry.target as ElementWithOpId<FormFieldElement>;
|
||||
if (this.elementInitializingIntersectionObserver.has(formFieldElement)) {
|
||||
this.elementInitializingIntersectionObserver.delete(formFieldElement);
|
||||
continue;
|
||||
}
|
||||
|
||||
const isViewable =
|
||||
await this.domElementVisibilityService.isFormFieldViewable(formFieldElement);
|
||||
if (!isViewable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const cachedAutofillFieldElement = this.autofillFieldElements.get(formFieldElement);
|
||||
cachedAutofillFieldElement.viewable = true;
|
||||
|
||||
void this.autofillOverlayContentService?.setupAutofillOverlayListenerOnField(
|
||||
formFieldElement,
|
||||
cachedAutofillFieldElement,
|
||||
);
|
||||
|
||||
this.intersectionObserver.unobserve(entry.target);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroys the CollectAutofillContentService. Clears all
|
||||
* timeouts and disconnects the mutation observer.
|
||||
@@ -1313,6 +1366,7 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
clearTimeout(this.updateAutofillElementsAfterMutationTimeout);
|
||||
}
|
||||
this.mutationObserver?.disconnect();
|
||||
this.intersectionObserver?.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class DomElementVisibilityService implements domElementVisibilityServiceInterfac
|
||||
*/
|
||||
private getElementStyle(element: HTMLElement, styleProperty: string): string {
|
||||
if (!this.cachedComputedStyle) {
|
||||
this.cachedComputedStyle = (element.ownerDocument.defaultView || window).getComputedStyle(
|
||||
this.cachedComputedStyle = (element.ownerDocument.defaultView || globalThis).getComputedStyle(
|
||||
element,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ const initEventCount = Object.freeze(
|
||||
);
|
||||
|
||||
let confirmSpy: jest.SpyInstance<boolean, [message?: string]>;
|
||||
let windowSpy: jest.SpyInstance<any>;
|
||||
let windowLocationSpy: jest.SpyInstance<any>;
|
||||
let savedURLs: string[] | null = ["https://bitwarden.com"];
|
||||
function setMockWindowLocation({
|
||||
protocol,
|
||||
@@ -56,11 +56,9 @@ function setMockWindowLocation({
|
||||
protocol: "http:" | "https:";
|
||||
hostname: string;
|
||||
}) {
|
||||
windowSpy.mockImplementation(() => ({
|
||||
location: {
|
||||
windowLocationSpy.mockImplementation(() => ({
|
||||
protocol,
|
||||
hostname,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -76,8 +74,8 @@ describe("InsertAutofillContentService", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = mockLoginForm;
|
||||
confirmSpy = jest.spyOn(window, "confirm");
|
||||
windowSpy = jest.spyOn(window, "window", "get");
|
||||
confirmSpy = jest.spyOn(globalThis, "confirm");
|
||||
windowLocationSpy = jest.spyOn(globalThis, "location", "get");
|
||||
insertAutofillContentService = new InsertAutofillContentService(
|
||||
domElementVisibilityService,
|
||||
collectAutofillContentService,
|
||||
@@ -101,7 +99,7 @@ describe("InsertAutofillContentService", () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
windowSpy.mockRestore();
|
||||
windowLocationSpy.mockRestore();
|
||||
confirmSpy.mockRestore();
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
@@ -245,8 +243,8 @@ describe("InsertAutofillContentService", () => {
|
||||
});
|
||||
|
||||
it("returns true if the frameElement has a sandbox attribute", () => {
|
||||
Object.defineProperty(globalThis, "window", {
|
||||
value: { frameElement: { hasAttribute: jest.fn(() => true) } },
|
||||
Object.defineProperty(globalThis, "frameElement", {
|
||||
value: { hasAttribute: jest.fn(() => true) },
|
||||
writable: true,
|
||||
});
|
||||
|
||||
@@ -991,11 +989,11 @@ describe("InsertAutofillContentService", () => {
|
||||
const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement;
|
||||
inputElement.value = "test";
|
||||
jest.spyOn(inputElement, "focus");
|
||||
jest.spyOn(window, "String");
|
||||
jest.spyOn(globalThis, "String");
|
||||
|
||||
insertAutofillContentService["triggerFocusOnElement"](inputElement, true);
|
||||
|
||||
expect(window.String).toHaveBeenCalledWith(value);
|
||||
expect(globalThis.String).toHaveBeenCalledWith(value);
|
||||
expect(inputElement.focus).toHaveBeenCalled();
|
||||
expect(inputElement.value).toEqual(value);
|
||||
});
|
||||
@@ -1005,11 +1003,11 @@ describe("InsertAutofillContentService", () => {
|
||||
const inputElement = document.querySelector('input[type="text"]') as HTMLInputElement;
|
||||
inputElement.value = "test";
|
||||
jest.spyOn(inputElement, "focus");
|
||||
jest.spyOn(window, "String");
|
||||
jest.spyOn(globalThis, "String");
|
||||
|
||||
insertAutofillContentService["triggerFocusOnElement"](inputElement, false);
|
||||
|
||||
expect(window.String).not.toHaveBeenCalledWith();
|
||||
expect(globalThis.String).not.toHaveBeenCalledWith();
|
||||
expect(inputElement.focus).toHaveBeenCalled();
|
||||
expect(inputElement.value).toEqual(value);
|
||||
});
|
||||
|
||||
@@ -65,8 +65,8 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
private fillingWithinSandboxedIframe() {
|
||||
return (
|
||||
String(self.origin).toLowerCase() === "null" ||
|
||||
window.frameElement?.hasAttribute("sandbox") ||
|
||||
window.location.hostname === ""
|
||||
globalThis.frameElement?.hasAttribute("sandbox") ||
|
||||
globalThis.location.hostname === ""
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
*/
|
||||
private userCancelledInsecureUrlAutofill(savedUrls?: string[] | null): boolean {
|
||||
if (
|
||||
!savedUrls?.some((url) => url.startsWith(`https://${window.location.hostname}`)) ||
|
||||
window.location.protocol !== "http:" ||
|
||||
!savedUrls?.some((url) => url.startsWith(`https://${globalThis.location.hostname}`)) ||
|
||||
globalThis.location.protocol !== "http:" ||
|
||||
!this.isPasswordFieldWithinDocument()
|
||||
) {
|
||||
return false;
|
||||
@@ -88,10 +88,10 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
|
||||
const confirmationWarning = [
|
||||
chrome.i18n.getMessage("insecurePageWarning"),
|
||||
chrome.i18n.getMessage("insecurePageWarningFillPrompt", [window.location.hostname]),
|
||||
chrome.i18n.getMessage("insecurePageWarningFillPrompt", [globalThis.location.hostname]),
|
||||
].join("\n\n");
|
||||
|
||||
return !confirm(confirmationWarning);
|
||||
return !globalThis.confirm(confirmationWarning);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,10 +129,10 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
|
||||
|
||||
const confirmationWarning = [
|
||||
chrome.i18n.getMessage("autofillIframeWarning"),
|
||||
chrome.i18n.getMessage("autofillIframeWarningTip", [window.location.hostname]),
|
||||
chrome.i18n.getMessage("autofillIframeWarningTip", [globalThis.location.hostname]),
|
||||
].join("\n\n");
|
||||
|
||||
return !confirm(confirmationWarning);
|
||||
return !globalThis.confirm(confirmationWarning);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ const IdleInterval = 60 * 5; // 5 minutes
|
||||
|
||||
export default class IdleBackground {
|
||||
private idle: typeof chrome.idle | typeof browser.idle | null;
|
||||
private idleTimer: number = null;
|
||||
private idleTimer: number | NodeJS.Timeout = null;
|
||||
private idleState = "active";
|
||||
|
||||
constructor(
|
||||
@@ -73,7 +73,7 @@ export default class IdleBackground {
|
||||
|
||||
private pollIdle(handler: (newState: string) => void) {
|
||||
if (this.idleTimer != null) {
|
||||
window.clearTimeout(this.idleTimer);
|
||||
globalThis.clearTimeout(this.idleTimer);
|
||||
this.idleTimer = null;
|
||||
}
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
@@ -83,7 +83,7 @@ export default class IdleBackground {
|
||||
this.idleState = state;
|
||||
handler(state);
|
||||
}
|
||||
this.idleTimer = window.setTimeout(() => this.pollIdle(handler), 5000);
|
||||
this.idleTimer = globalThis.setTimeout(() => this.pollIdle(handler), 5000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
UserDecryptionOptionsService,
|
||||
AuthRequestServiceAbstraction,
|
||||
AuthRequestService,
|
||||
LoginEmailServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
@@ -70,6 +71,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
@@ -93,6 +95,7 @@ import { StateFactory } from "@bitwarden/common/platform/factories/state-factory
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
|
||||
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
|
||||
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
@@ -143,6 +146,8 @@ import {
|
||||
} from "@bitwarden/common/tools/password-strength";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
|
||||
import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
|
||||
import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@@ -200,10 +205,10 @@ import { BrowserApi } from "../platform/browser/browser-api";
|
||||
import { flagEnabled } from "../platform/flags";
|
||||
import { UpdateBadge } from "../platform/listeners/update-badge";
|
||||
import { BrowserStateService as StateServiceAbstraction } from "../platform/services/abstractions/browser-state.service";
|
||||
import { BrowserConfigService } from "../platform/services/browser-config.service";
|
||||
import { BrowserCryptoService } from "../platform/services/browser-crypto.service";
|
||||
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
|
||||
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
|
||||
import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service";
|
||||
import BrowserMessagingPrivateModeBackgroundService from "../platform/services/browser-messaging-private-mode-background.service";
|
||||
import BrowserMessagingService from "../platform/services/browser-messaging.service";
|
||||
import { BrowserStateService } from "../platform/services/browser-state.service";
|
||||
@@ -213,7 +218,6 @@ import { BackgroundPlatformUtilsService } from "../platform/services/platform-ut
|
||||
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
|
||||
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
|
||||
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
|
||||
import { BrowserSendService } from "../services/browser-send.service";
|
||||
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
|
||||
import FilelessImporterBackground from "../tools/background/fileless-importer.background";
|
||||
import { BrowserFido2UserInterfaceService } from "../vault/fido2/browser-fido2-user-interface.service";
|
||||
@@ -228,7 +232,7 @@ import RuntimeBackground from "./runtime.background";
|
||||
|
||||
export default class MainBackground {
|
||||
messagingService: MessagingServiceAbstraction;
|
||||
storageService: AbstractStorageService;
|
||||
storageService: AbstractStorageService & ObservableStorageService;
|
||||
secureStorageService: AbstractStorageService;
|
||||
memoryStorageService: AbstractMemoryStorageService;
|
||||
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
|
||||
@@ -257,6 +261,7 @@ export default class MainBackground {
|
||||
auditService: AuditServiceAbstraction;
|
||||
authService: AuthServiceAbstraction;
|
||||
loginStrategyService: LoginStrategyServiceAbstraction;
|
||||
loginEmailService: LoginEmailServiceAbstraction;
|
||||
importApiService: ImportApiServiceAbstraction;
|
||||
importService: ImportServiceAbstraction;
|
||||
exportService: VaultExportServiceAbstraction;
|
||||
@@ -272,6 +277,7 @@ export default class MainBackground {
|
||||
eventUploadService: EventUploadServiceAbstraction;
|
||||
policyService: InternalPolicyServiceAbstraction;
|
||||
sendService: InternalSendServiceAbstraction;
|
||||
sendStateProvider: SendStateProvider;
|
||||
fileUploadService: FileUploadServiceAbstraction;
|
||||
cipherFileUploadService: CipherFileUploadServiceAbstraction;
|
||||
organizationService: InternalOrganizationServiceAbstraction;
|
||||
@@ -293,7 +299,7 @@ export default class MainBackground {
|
||||
avatarService: AvatarServiceAbstraction;
|
||||
mainContextMenuHandler: MainContextMenuHandler;
|
||||
cipherContextMenuHandler: CipherContextMenuHandler;
|
||||
configService: BrowserConfigService;
|
||||
configService: ConfigService;
|
||||
configApiService: ConfigApiServiceAbstraction;
|
||||
devicesApiService: DevicesApiServiceAbstraction;
|
||||
devicesService: DevicesServiceAbstraction;
|
||||
@@ -362,22 +368,28 @@ export default class MainBackground {
|
||||
this.cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
|
||||
this.storageService = new BrowserLocalStorageService();
|
||||
|
||||
const mv3MemoryStorageCreator = (partitionName: string) => {
|
||||
// TODO: Consider using multithreaded encrypt service in popup only context
|
||||
return new LocalBackedSessionStorageService(
|
||||
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
|
||||
this.keyGenerationService,
|
||||
new BrowserLocalStorageService(),
|
||||
new BrowserMemoryStorageService(),
|
||||
partitionName,
|
||||
);
|
||||
};
|
||||
|
||||
this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used
|
||||
this.memoryStorageService = BrowserApi.isManifestVersion(3)
|
||||
? new LocalBackedSessionStorageService(
|
||||
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
|
||||
this.keyGenerationService,
|
||||
)
|
||||
? mv3MemoryStorageCreator("stateService")
|
||||
: new MemoryStorageService();
|
||||
this.memoryStorageForStateProviders = BrowserApi.isManifestVersion(3)
|
||||
? new LocalBackedSessionStorageService(
|
||||
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
|
||||
this.keyGenerationService,
|
||||
)
|
||||
? mv3MemoryStorageCreator("stateProviders")
|
||||
: new BackgroundMemoryStorageService();
|
||||
|
||||
const storageServiceProvider = new StorageServiceProvider(
|
||||
this.storageService as BrowserLocalStorageService,
|
||||
this.storageService,
|
||||
this.memoryStorageForStateProviders,
|
||||
);
|
||||
|
||||
@@ -443,6 +455,9 @@ export default class MainBackground {
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService.supportsSecureStorage(),
|
||||
this.secureStorageService,
|
||||
this.keyGenerationService,
|
||||
this.encryptService,
|
||||
this.logService,
|
||||
);
|
||||
|
||||
const migrationRunner = new MigrationRunner(
|
||||
@@ -510,7 +525,6 @@ export default class MainBackground {
|
||||
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
|
||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
||||
this.keyConnectorService = new KeyConnectorService(
|
||||
this.stateService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
@@ -518,6 +532,7 @@ export default class MainBackground {
|
||||
this.organizationService,
|
||||
this.keyGenerationService,
|
||||
logoutCallback,
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.passwordStrengthService = new PasswordStrengthService();
|
||||
@@ -550,11 +565,12 @@ export default class MainBackground {
|
||||
this.cryptoFunctionService,
|
||||
this.cryptoService,
|
||||
this.encryptService,
|
||||
this.stateService,
|
||||
this.appIdService,
|
||||
this.devicesApiService,
|
||||
this.i18nService,
|
||||
this.platformUtilsService,
|
||||
this.stateProvider,
|
||||
this.secureStorageService,
|
||||
this.userDecryptionOptionsService,
|
||||
);
|
||||
|
||||
@@ -568,14 +584,16 @@ export default class MainBackground {
|
||||
);
|
||||
|
||||
this.authService = new AuthService(
|
||||
this.accountService,
|
||||
backgroundMessagingService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.stateService,
|
||||
this.tokenService,
|
||||
);
|
||||
|
||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||
this.activeUserStateProvider,
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.loginStrategyService = new LoginStrategyService(
|
||||
@@ -605,16 +623,13 @@ export default class MainBackground {
|
||||
|
||||
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
|
||||
|
||||
this.configApiService = new ConfigApiService(this.apiService, this.authService);
|
||||
this.configApiService = new ConfigApiService(this.apiService, this.tokenService);
|
||||
|
||||
this.configService = new BrowserConfigService(
|
||||
this.stateService,
|
||||
this.configService = new DefaultConfigService(
|
||||
this.configApiService,
|
||||
this.authService,
|
||||
this.environmentService,
|
||||
this.logService,
|
||||
this.stateProvider,
|
||||
true,
|
||||
);
|
||||
|
||||
this.cipherService = new CipherService(
|
||||
@@ -694,11 +709,14 @@ export default class MainBackground {
|
||||
logoutCallback,
|
||||
);
|
||||
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
|
||||
this.sendService = new BrowserSendService(
|
||||
|
||||
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
||||
this.sendService = new SendService(
|
||||
this.cryptoService,
|
||||
this.i18nService,
|
||||
this.keyGenerationService,
|
||||
this.stateService,
|
||||
this.sendStateProvider,
|
||||
this.encryptService,
|
||||
);
|
||||
this.sendApiService = new SendApiService(
|
||||
this.apiService,
|
||||
@@ -989,7 +1007,7 @@ export default class MainBackground {
|
||||
}
|
||||
|
||||
async bootstrap() {
|
||||
this.containerService.attachToGlobal(window);
|
||||
this.containerService.attachToGlobal(self);
|
||||
|
||||
await this.stateService.init();
|
||||
|
||||
@@ -1001,7 +1019,6 @@ export default class MainBackground {
|
||||
this.filelessImporterBackground.init();
|
||||
await this.commandsBackground.init();
|
||||
|
||||
this.configService.init();
|
||||
this.twoFactorService.init();
|
||||
|
||||
await this.overlayBackground.init();
|
||||
@@ -1079,7 +1096,9 @@ export default class MainBackground {
|
||||
await this.stateService.setActiveUser(userId);
|
||||
|
||||
if (userId == null) {
|
||||
await this.stateService.setRememberedEmail(null);
|
||||
this.loginEmailService.setRememberEmail(false);
|
||||
await this.loginEmailService.saveEmailSettings();
|
||||
|
||||
await this.refreshBadge();
|
||||
await this.refreshMenu();
|
||||
await this.overlayBackground.updateOverlayCiphers();
|
||||
@@ -1124,7 +1143,6 @@ export default class MainBackground {
|
||||
this.policyService.clear(userId),
|
||||
this.passwordGenerationService.clear(userId),
|
||||
this.vaultTimeoutSettingsService.clear(userId),
|
||||
this.keyConnectorService.clear(),
|
||||
this.vaultFilterService.clear(),
|
||||
this.biometricStateService.logout(userId),
|
||||
this.providerService.save(null, userId),
|
||||
|
||||
@@ -3,7 +3,7 @@ import { firstValueFrom } from "rxjs";
|
||||
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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
@@ -46,7 +46,7 @@ export default class RuntimeBackground {
|
||||
private environmentService: BrowserEnvironmentService,
|
||||
private messagingService: MessagingService,
|
||||
private logService: LogService,
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private fido2Service: Fido2Service,
|
||||
) {
|
||||
// onInstalled listener must be wired up before anything else, so we do it in the ctor
|
||||
@@ -89,7 +89,7 @@ export default class RuntimeBackground {
|
||||
|
||||
BrowserApi.messageListener("runtime.background", backgroundMessageListener);
|
||||
if (this.main.popupOnlyContext) {
|
||||
(window as any).bitwardenBackgroundMessageListener = backgroundMessageListener;
|
||||
(self as any).bitwardenBackgroundMessageListener = backgroundMessageListener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export default class RuntimeBackground {
|
||||
await this.main.refreshBadge();
|
||||
await this.main.refreshMenu();
|
||||
}, 2000);
|
||||
this.configService.triggerServerConfigFetch();
|
||||
await this.configService.ensureConfigFetched();
|
||||
}
|
||||
break;
|
||||
case "openPopup":
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
|
||||
import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
|
||||
import {
|
||||
CryptoServiceInitOptions,
|
||||
cryptoServiceFactory,
|
||||
} from "../../platform/background/service-factories/crypto-service.factory";
|
||||
import {
|
||||
EncryptServiceInitOptions,
|
||||
encryptServiceFactory,
|
||||
} from "../../platform/background/service-factories/encrypt-service.factory";
|
||||
import {
|
||||
FactoryOptions,
|
||||
CachedServices,
|
||||
@@ -17,11 +22,11 @@ import {
|
||||
KeyGenerationServiceInitOptions,
|
||||
keyGenerationServiceFactory,
|
||||
} from "../../platform/background/service-factories/key-generation-service.factory";
|
||||
|
||||
import {
|
||||
stateServiceFactory,
|
||||
StateServiceInitOptions,
|
||||
} from "../../platform/background/service-factories/state-service.factory";
|
||||
import { BrowserSendService } from "../../services/browser-send.service";
|
||||
SendStateProviderInitOptions,
|
||||
sendStateProviderFactory,
|
||||
} from "./send-state-provider.factory";
|
||||
|
||||
type SendServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
@@ -29,7 +34,8 @@ export type SendServiceInitOptions = SendServiceFactoryOptions &
|
||||
CryptoServiceInitOptions &
|
||||
I18nServiceInitOptions &
|
||||
KeyGenerationServiceInitOptions &
|
||||
StateServiceInitOptions;
|
||||
SendStateProviderInitOptions &
|
||||
EncryptServiceInitOptions;
|
||||
|
||||
export function sendServiceFactory(
|
||||
cache: { sendService?: InternalSendService } & CachedServices,
|
||||
@@ -40,11 +46,12 @@ export function sendServiceFactory(
|
||||
"sendService",
|
||||
opts,
|
||||
async () =>
|
||||
new BrowserSendService(
|
||||
new SendService(
|
||||
await cryptoServiceFactory(cache, opts),
|
||||
await i18nServiceFactory(cache, opts),
|
||||
await keyGenerationServiceFactory(cache, opts),
|
||||
await stateServiceFactory(cache, opts),
|
||||
await sendStateProviderFactory(cache, opts),
|
||||
await encryptServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider";
|
||||
|
||||
import {
|
||||
CachedServices,
|
||||
FactoryOptions,
|
||||
factory,
|
||||
} from "../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
StateProviderInitOptions,
|
||||
stateProviderFactory,
|
||||
} from "../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type SendStateProviderFactoryOptions = FactoryOptions;
|
||||
|
||||
export type SendStateProviderInitOptions = SendStateProviderFactoryOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function sendStateProviderFactory(
|
||||
cache: { sendStateProvider?: SendStateProvider } & CachedServices,
|
||||
opts: SendStateProviderInitOptions,
|
||||
): Promise<SendStateProvider> {
|
||||
return factory(
|
||||
cache,
|
||||
"sendStateProvider",
|
||||
opts,
|
||||
async () => new SendStateProvider(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.3.0",
|
||||
"version": "2024.3.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"minimum_chrome_version": "102.0",
|
||||
"name": "__MSG_extName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "2024.3.0",
|
||||
"version": "2024.3.1",
|
||||
"description": "__MSG_extDesc__",
|
||||
"default_locale": "en",
|
||||
"author": "Bitwarden Inc.",
|
||||
|
||||
@@ -1,42 +1,35 @@
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
|
||||
import MainBackground from "../background/main.background";
|
||||
|
||||
import { onAlarmListener } from "./alarms/on-alarm-listener";
|
||||
import { registerAlarms } from "./alarms/register-alarms";
|
||||
import { BrowserApi } from "./browser/browser-api";
|
||||
import {
|
||||
contextMenusClickedListener,
|
||||
onCommandListener,
|
||||
onInstallListener,
|
||||
runtimeMessageListener,
|
||||
windowsOnFocusChangedListener,
|
||||
tabsOnActivatedListener,
|
||||
tabsOnReplacedListener,
|
||||
tabsOnUpdatedListener,
|
||||
} from "./listeners";
|
||||
|
||||
if (BrowserApi.isManifestVersion(3)) {
|
||||
chrome.commands.onCommand.addListener(onCommandListener);
|
||||
chrome.runtime.onInstalled.addListener(onInstallListener);
|
||||
chrome.alarms.onAlarm.addListener(onAlarmListener);
|
||||
registerAlarms();
|
||||
chrome.windows.onFocusChanged.addListener(windowsOnFocusChangedListener);
|
||||
chrome.tabs.onActivated.addListener(tabsOnActivatedListener);
|
||||
chrome.tabs.onReplaced.addListener(tabsOnReplacedListener);
|
||||
chrome.tabs.onUpdated.addListener(tabsOnUpdatedListener);
|
||||
chrome.contextMenus.onClicked.addListener(contextMenusClickedListener);
|
||||
BrowserApi.messageListener(
|
||||
"runtime.background",
|
||||
(message: { command: string }, sender, sendResponse) => {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
runtimeMessageListener(message, sender);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const bitwardenMain = ((window as any).bitwardenMain = new MainBackground());
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
bitwardenMain.bootstrap().then(() => {
|
||||
const logService = new ConsoleLogService(false);
|
||||
const bitwardenMain = ((self as any).bitwardenMain = new MainBackground());
|
||||
bitwardenMain
|
||||
.bootstrap()
|
||||
.then(() => {
|
||||
// Finished bootstrapping
|
||||
});
|
||||
if (BrowserApi.isManifestVersion(3)) {
|
||||
startHeartbeat().catch((error) => logService.error(error));
|
||||
}
|
||||
})
|
||||
.catch((error) => logService.error(error));
|
||||
|
||||
/**
|
||||
* Tracks when a service worker was last alive and extends the service worker
|
||||
* lifetime by writing the current time to extension storage every 20 seconds.
|
||||
*/
|
||||
async function runHeartbeat() {
|
||||
await chrome.storage.local.set({ "last-heartbeat": new Date().getTime() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the heartbeat interval which keeps the service worker alive.
|
||||
*/
|
||||
async function startHeartbeat() {
|
||||
// Run the heartbeat once at service worker startup, then again every 20 seconds.
|
||||
runHeartbeat()
|
||||
.then(() => setInterval(runHeartbeat, 20 * 1000))
|
||||
.catch((error) => logService.error(error));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
|
||||
import { activeUserStateProviderFactory } from "./active-user-state-provider.factory";
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
import { StateProviderInitOptions } from "./state-provider.factory";
|
||||
import { StateProviderInitOptions, stateProviderFactory } from "./state-provider.factory";
|
||||
|
||||
type BillingAccountProfileStateServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
@@ -21,8 +20,6 @@ export function billingAccountProfileStateServiceFactory(
|
||||
"billingAccountProfileStateService",
|
||||
opts,
|
||||
async () =>
|
||||
new DefaultBillingAccountProfileStateService(
|
||||
await activeUserStateProviderFactory(cache, opts),
|
||||
),
|
||||
new DefaultBillingAccountProfileStateService(await stateProviderFactory(cache, opts)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstract
|
||||
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
|
||||
|
||||
import {
|
||||
authServiceFactory,
|
||||
AuthServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/auth-service.factory";
|
||||
tokenServiceFactory,
|
||||
TokenServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/token-service.factory";
|
||||
|
||||
import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory";
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
@@ -13,7 +13,7 @@ type ConfigApiServiceFactoyOptions = FactoryOptions;
|
||||
|
||||
export type ConfigApiServiceInitOptions = ConfigApiServiceFactoyOptions &
|
||||
ApiServiceInitOptions &
|
||||
AuthServiceInitOptions;
|
||||
TokenServiceInitOptions;
|
||||
|
||||
export function configApiServiceFactory(
|
||||
cache: { configApiService?: ConfigApiServiceAbstraction } & CachedServices,
|
||||
@@ -26,7 +26,7 @@ export function configApiServiceFactory(
|
||||
async () =>
|
||||
new ConfigApiService(
|
||||
await apiServiceFactory(cache, opts),
|
||||
await authServiceFactory(cache, opts),
|
||||
await tokenServiceFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
|
||||
import {
|
||||
authServiceFactory,
|
||||
AuthServiceInitOptions,
|
||||
} from "../../../auth/background/service-factories/auth-service.factory";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
|
||||
|
||||
import { configApiServiceFactory, ConfigApiServiceInitOptions } from "./config-api.service.factory";
|
||||
import {
|
||||
@@ -13,39 +8,30 @@ import {
|
||||
} from "./environment-service.factory";
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory";
|
||||
import { stateProviderFactory } from "./state-provider.factory";
|
||||
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
|
||||
import { stateProviderFactory, StateProviderInitOptions } from "./state-provider.factory";
|
||||
|
||||
type ConfigServiceFactoryOptions = FactoryOptions & {
|
||||
configServiceOptions?: {
|
||||
subscribe?: boolean;
|
||||
};
|
||||
};
|
||||
type ConfigServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type ConfigServiceInitOptions = ConfigServiceFactoryOptions &
|
||||
StateServiceInitOptions &
|
||||
ConfigApiServiceInitOptions &
|
||||
AuthServiceInitOptions &
|
||||
EnvironmentServiceInitOptions &
|
||||
LogServiceInitOptions;
|
||||
LogServiceInitOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function configServiceFactory(
|
||||
cache: { configService?: ConfigServiceAbstraction } & CachedServices,
|
||||
cache: { configService?: ConfigService } & CachedServices,
|
||||
opts: ConfigServiceInitOptions,
|
||||
): Promise<ConfigServiceAbstraction> {
|
||||
): Promise<ConfigService> {
|
||||
return factory(
|
||||
cache,
|
||||
"configService",
|
||||
opts,
|
||||
async () =>
|
||||
new ConfigService(
|
||||
await stateServiceFactory(cache, opts),
|
||||
new DefaultConfigService(
|
||||
await configApiServiceFactory(cache, opts),
|
||||
await authServiceFactory(cache, opts),
|
||||
await environmentServiceFactory(cache, opts),
|
||||
await logServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
opts.configServiceOptions?.subscribe ?? true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory
|
||||
|
||||
import { BrowserApi } from "../../browser/browser-api";
|
||||
import BrowserLocalStorageService from "../../services/browser-local-storage.service";
|
||||
import BrowserMemoryStorageService from "../../services/browser-memory-storage.service";
|
||||
import { LocalBackedSessionStorageService } from "../../services/local-backed-session-storage.service";
|
||||
import { BackgroundMemoryStorageService } from "../../storage/background-memory-storage.service";
|
||||
|
||||
@@ -17,13 +18,14 @@ import {
|
||||
keyGenerationServiceFactory,
|
||||
} from "./key-generation-service.factory";
|
||||
|
||||
type StorageServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type DiskStorageServiceInitOptions = StorageServiceFactoryOptions;
|
||||
export type SecureStorageServiceInitOptions = StorageServiceFactoryOptions;
|
||||
export type MemoryStorageServiceInitOptions = StorageServiceFactoryOptions &
|
||||
export type DiskStorageServiceInitOptions = FactoryOptions;
|
||||
export type SecureStorageServiceInitOptions = FactoryOptions;
|
||||
export type SessionStorageServiceInitOptions = FactoryOptions;
|
||||
export type MemoryStorageServiceInitOptions = FactoryOptions &
|
||||
EncryptServiceInitOptions &
|
||||
KeyGenerationServiceInitOptions;
|
||||
KeyGenerationServiceInitOptions &
|
||||
DiskStorageServiceInitOptions &
|
||||
SessionStorageServiceInitOptions;
|
||||
|
||||
export function diskStorageServiceFactory(
|
||||
cache: { diskStorageService?: AbstractStorageService } & CachedServices,
|
||||
@@ -47,6 +49,13 @@ export function secureStorageServiceFactory(
|
||||
return factory(cache, "secureStorageService", opts, () => new BrowserLocalStorageService());
|
||||
}
|
||||
|
||||
export function sessionStorageServiceFactory(
|
||||
cache: { sessionStorageService?: AbstractStorageService } & CachedServices,
|
||||
opts: SessionStorageServiceInitOptions,
|
||||
): Promise<AbstractStorageService> {
|
||||
return factory(cache, "sessionStorageService", opts, () => new BrowserMemoryStorageService());
|
||||
}
|
||||
|
||||
export function memoryStorageServiceFactory(
|
||||
cache: { memoryStorageService?: AbstractMemoryStorageService } & CachedServices,
|
||||
opts: MemoryStorageServiceInitOptions,
|
||||
@@ -56,6 +65,9 @@ export function memoryStorageServiceFactory(
|
||||
return new LocalBackedSessionStorageService(
|
||||
await encryptServiceFactory(cache, opts),
|
||||
await keyGenerationServiceFactory(cache, opts),
|
||||
await diskStorageServiceFactory(cache, opts),
|
||||
await sessionStorageServiceFactory(cache, opts),
|
||||
"serviceFactories",
|
||||
);
|
||||
}
|
||||
return new MemoryStorageService();
|
||||
|
||||
@@ -357,11 +357,11 @@ export class BrowserApi {
|
||||
private static setupUnloadListeners() {
|
||||
// The MDN recommend using 'visibilitychange' but that event is fired any time the popup window is obscured as well
|
||||
// 'pagehide' works just like 'unload' but is compatible with the back/forward cache, so we prefer using that one
|
||||
window.onpagehide = () => {
|
||||
self.addEventListener("pagehide", () => {
|
||||
for (const [event, callback] of BrowserApi.trackedChromeEventListeners) {
|
||||
event.removeListener(callback);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static sendMessage(subscriber: string, arg: any = {}) {
|
||||
@@ -429,7 +429,7 @@ export class BrowserApi {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentHref = window.location.href;
|
||||
const currentHref = self.location.href;
|
||||
views
|
||||
.filter((w) => w.location.href != null && !w.location.href.includes("background.html"))
|
||||
.filter((w) => !exemptCurrentHref || w.location.href !== currentHref)
|
||||
|
||||
@@ -29,14 +29,14 @@ class OffscreenDocument implements OffscreenDocumentInterface {
|
||||
* @param message - The extension message containing the text to copy
|
||||
*/
|
||||
private async handleOffscreenCopyToClipboard(message: OffscreenDocumentExtensionMessage) {
|
||||
await BrowserClipboardService.copy(window, message.text);
|
||||
await BrowserClipboardService.copy(self, message.text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the user's clipboard and returns the text.
|
||||
*/
|
||||
private async handleOffscreenReadFromClipboard() {
|
||||
return await BrowserClipboardService.read(window);
|
||||
return await BrowserClipboardService.read(self);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@ import { FileDownloadRequest } from "@bitwarden/common/platform/abstractions/fil
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { SafariApp } from "../../browser/safariApp";
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import { SafariApp } from "../../../browser/safariApp";
|
||||
import { BrowserApi } from "../../browser/browser-api";
|
||||
|
||||
@Injectable()
|
||||
export class BrowserFileDownloadService implements FileDownloadService {
|
||||
@@ -3,22 +3,9 @@ import { StorageOptions } from "@bitwarden/common/platform/models/domain/storage
|
||||
|
||||
import { Account } from "../../../models/account";
|
||||
import { BrowserComponentState } from "../../../models/browserComponentState";
|
||||
import { BrowserGroupingsComponentState } from "../../../models/browserGroupingsComponentState";
|
||||
import { BrowserSendComponentState } from "../../../models/browserSendComponentState";
|
||||
|
||||
export abstract class BrowserStateService extends BaseStateServiceAbstraction<Account> {
|
||||
getBrowserGroupingComponentState: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<BrowserGroupingsComponentState>;
|
||||
setBrowserGroupingComponentState: (
|
||||
value: BrowserGroupingsComponentState,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getBrowserVaultItemsComponentState: (options?: StorageOptions) => Promise<BrowserComponentState>;
|
||||
setBrowserVaultItemsComponentState: (
|
||||
value: BrowserComponentState,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getBrowserSendComponentState: (options?: StorageOptions) => Promise<BrowserSendComponentState>;
|
||||
setBrowserSendComponentState: (
|
||||
value: BrowserSendComponentState,
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { ReplaySubject } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
|
||||
import { browserSession, sessionSync } from "../decorators/session-sync-observable";
|
||||
|
||||
@browserSession
|
||||
export class BrowserConfigService extends ConfigService {
|
||||
@sessionSync<ServerConfig>({ initializer: ServerConfig.fromJSON })
|
||||
protected _serverConfig: ReplaySubject<ServerConfig | null>;
|
||||
|
||||
constructor(
|
||||
stateService: StateService,
|
||||
configApiService: ConfigApiServiceAbstraction,
|
||||
authService: AuthService,
|
||||
environmentService: EnvironmentService,
|
||||
logService: LogService,
|
||||
stateProvider: StateProvider,
|
||||
subscribe = false,
|
||||
) {
|
||||
super(
|
||||
stateService,
|
||||
configApiService,
|
||||
authService,
|
||||
environmentService,
|
||||
logService,
|
||||
stateProvider,
|
||||
subscribe,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
export default class BrowserMessagingPrivateModeBackgroundService implements MessagingService {
|
||||
send(subscriber: string, arg: any = {}) {
|
||||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
(window as any).bitwardenPopupMainMessageListener(message);
|
||||
(self as any).bitwardenPopupMainMessageListener(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
export default class BrowserMessagingPrivateModePopupService implements MessagingService {
|
||||
send(subscriber: string, arg: any = {}) {
|
||||
const message = Object.assign({}, { command: subscriber }, arg);
|
||||
(window as any).bitwardenBackgroundMessageListener(message);
|
||||
(self as any).bitwardenBackgroundMessageListener(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { Account } from "../../models/account";
|
||||
import { BrowserComponentState } from "../../models/browserComponentState";
|
||||
import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState";
|
||||
import { BrowserSendComponentState } from "../../models/browserSendComponentState";
|
||||
|
||||
import { BrowserStateService } from "./browser-state.service";
|
||||
@@ -86,27 +85,6 @@ describe("Browser State Service", () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe("getBrowserGroupingComponentState", () => {
|
||||
it("should return a BrowserGroupingsComponentState", async () => {
|
||||
state.accounts[userId].groupings = new BrowserGroupingsComponentState();
|
||||
|
||||
const actual = await sut.getBrowserGroupingComponentState();
|
||||
expect(actual).toBeInstanceOf(BrowserGroupingsComponentState);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBrowserVaultItemsComponentState", () => {
|
||||
it("should return a BrowserComponentState", async () => {
|
||||
const componentState = new BrowserComponentState();
|
||||
componentState.scrollY = 0;
|
||||
componentState.searchText = "test";
|
||||
state.accounts[userId].ciphers = componentState;
|
||||
|
||||
const actual = await sut.getBrowserVaultItemsComponentState();
|
||||
expect(actual).toStrictEqual(componentState);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBrowserSendComponentState", () => {
|
||||
it("should return a BrowserSendComponentState", async () => {
|
||||
const sendState = new BrowserSendComponentState();
|
||||
|
||||
@@ -16,7 +16,6 @@ import { StateService as BaseStateService } from "@bitwarden/common/platform/ser
|
||||
|
||||
import { Account } from "../../models/account";
|
||||
import { BrowserComponentState } from "../../models/browserComponentState";
|
||||
import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState";
|
||||
import { BrowserSendComponentState } from "../../models/browserSendComponentState";
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import { browserSession, sessionSync } from "../decorators/session-sync-observable";
|
||||
@@ -116,50 +115,6 @@ export class BrowserStateService
|
||||
);
|
||||
}
|
||||
|
||||
async getBrowserGroupingComponentState(
|
||||
options?: StorageOptions,
|
||||
): Promise<BrowserGroupingsComponentState> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.groupings;
|
||||
}
|
||||
|
||||
async setBrowserGroupingComponentState(
|
||||
value: BrowserGroupingsComponentState,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.groupings = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getBrowserVaultItemsComponentState(
|
||||
options?: StorageOptions,
|
||||
): Promise<BrowserComponentState> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.ciphers;
|
||||
}
|
||||
|
||||
async setBrowserVaultItemsComponentState(
|
||||
value: BrowserComponentState,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.ciphers = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getBrowserSendComponentState(options?: StorageOptions): Promise<BrowserSendComponentState> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
|
||||
@@ -25,6 +25,7 @@ export default class I18nService extends BaseI18nService {
|
||||
"bs",
|
||||
"ca",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"el",
|
||||
@@ -37,6 +38,7 @@ export default class I18nService extends BaseI18nService {
|
||||
"fi",
|
||||
"fil",
|
||||
"fr",
|
||||
"gl",
|
||||
"he",
|
||||
"hi",
|
||||
"hr",
|
||||
@@ -51,9 +53,13 @@ export default class I18nService extends BaseI18nService {
|
||||
"lt",
|
||||
"lv",
|
||||
"ml",
|
||||
"mr",
|
||||
"my",
|
||||
"nb",
|
||||
"ne",
|
||||
"nl",
|
||||
"nn",
|
||||
"or",
|
||||
"pl",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
@@ -64,6 +70,7 @@ export default class I18nService extends BaseI18nService {
|
||||
"sl",
|
||||
"sr",
|
||||
"sv",
|
||||
"te",
|
||||
"th",
|
||||
"tr",
|
||||
"uk",
|
||||
|
||||
@@ -2,45 +2,70 @@ import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import {
|
||||
AbstractMemoryStorageService,
|
||||
AbstractStorageService,
|
||||
StorageUpdate,
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
import BrowserLocalStorageService from "./browser-local-storage.service";
|
||||
import BrowserMemoryStorageService from "./browser-memory-storage.service";
|
||||
import { LocalBackedSessionStorageService } from "./local-backed-session-storage.service";
|
||||
|
||||
describe("Browser Session Storage Service", () => {
|
||||
describe("LocalBackedSessionStorage", () => {
|
||||
let encryptService: MockProxy<EncryptService>;
|
||||
let keyGenerationService: MockProxy<KeyGenerationService>;
|
||||
let localStorageService: MockProxy<AbstractStorageService>;
|
||||
let sessionStorageService: MockProxy<AbstractMemoryStorageService>;
|
||||
|
||||
let cache: Map<string, any>;
|
||||
const testObj = { a: 1, b: 2 };
|
||||
|
||||
let localStorage: BrowserLocalStorageService;
|
||||
let sessionStorage: BrowserMemoryStorageService;
|
||||
|
||||
const key = new SymmetricCryptoKey(Utils.fromUtf8ToArray("00000000000000000000000000000000"));
|
||||
let getSessionKeySpy: jest.SpyInstance;
|
||||
let sendUpdateSpy: jest.SpyInstance<void, [storageUpdate: StorageUpdate]>;
|
||||
const mockEnc = (input: string) => Promise.resolve(new EncString("ENCRYPTED" + input));
|
||||
|
||||
let sut: LocalBackedSessionStorageService;
|
||||
|
||||
const mockExistingSessionKey = (key: SymmetricCryptoKey) => {
|
||||
sessionStorageService.get.mockImplementation((storageKey) => {
|
||||
if (storageKey === "localEncryptionKey_test") {
|
||||
return Promise.resolve(key?.toJSON());
|
||||
}
|
||||
|
||||
return Promise.reject("No implementation for " + storageKey);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
encryptService = mock<EncryptService>();
|
||||
keyGenerationService = mock<KeyGenerationService>();
|
||||
localStorageService = mock<AbstractStorageService>();
|
||||
sessionStorageService = mock<AbstractMemoryStorageService>();
|
||||
|
||||
sut = new LocalBackedSessionStorageService(encryptService, keyGenerationService);
|
||||
sut = new LocalBackedSessionStorageService(
|
||||
encryptService,
|
||||
keyGenerationService,
|
||||
localStorageService,
|
||||
sessionStorageService,
|
||||
"test",
|
||||
);
|
||||
|
||||
cache = sut["cache"];
|
||||
localStorage = sut["localStorage"];
|
||||
sessionStorage = sut["sessionStorage"];
|
||||
getSessionKeySpy = jest.spyOn(sut, "getSessionEncKey");
|
||||
getSessionKeySpy.mockResolvedValue(key);
|
||||
|
||||
keyGenerationService.createKeyWithPurpose.mockResolvedValue({
|
||||
derivedKey: key,
|
||||
salt: "bitwarden-ephemeral",
|
||||
material: null, // Not used
|
||||
});
|
||||
|
||||
it("should exist", () => {
|
||||
expect(sut).toBeInstanceOf(LocalBackedSessionStorageService);
|
||||
getSessionKeySpy = jest.spyOn(sut, "getSessionEncKey");
|
||||
getSessionKeySpy.mockResolvedValue(key);
|
||||
|
||||
sendUpdateSpy = jest.spyOn(sut, "sendUpdate");
|
||||
sendUpdateSpy.mockReturnValue();
|
||||
});
|
||||
|
||||
describe("get", () => {
|
||||
@@ -54,7 +79,7 @@ describe("Browser Session Storage Service", () => {
|
||||
const session = { test: testObj };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(sut, "getSessionEncKey").mockResolvedValue(key);
|
||||
mockExistingSessionKey(key);
|
||||
});
|
||||
|
||||
describe("no session retrieved", () => {
|
||||
@@ -62,6 +87,7 @@ describe("Browser Session Storage Service", () => {
|
||||
let spy: jest.SpyInstance;
|
||||
beforeEach(async () => {
|
||||
spy = jest.spyOn(sut, "getLocalSession").mockResolvedValue(null);
|
||||
localStorageService.get.mockResolvedValue(null);
|
||||
result = await sut.get("test");
|
||||
});
|
||||
|
||||
@@ -123,31 +149,31 @@ describe("Browser Session Storage Service", () => {
|
||||
|
||||
describe("remove", () => {
|
||||
it("should save null", async () => {
|
||||
const spy = jest.spyOn(sut, "save");
|
||||
spy.mockResolvedValue(null);
|
||||
await sut.remove("test");
|
||||
expect(spy).toHaveBeenCalledWith("test", null);
|
||||
expect(sendUpdateSpy).toHaveBeenCalledWith({ key: "test", updateType: "remove" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("save", () => {
|
||||
describe("caching", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(localStorage, "get").mockResolvedValue(null);
|
||||
jest.spyOn(sessionStorage, "get").mockResolvedValue(null);
|
||||
jest.spyOn(localStorage, "save").mockResolvedValue();
|
||||
jest.spyOn(sessionStorage, "save").mockResolvedValue();
|
||||
localStorageService.get.mockResolvedValue(null);
|
||||
sessionStorageService.get.mockResolvedValue(null);
|
||||
|
||||
localStorageService.save.mockResolvedValue();
|
||||
sessionStorageService.save.mockResolvedValue();
|
||||
|
||||
encryptService.encrypt.mockResolvedValue(mockEnc("{}"));
|
||||
});
|
||||
|
||||
it("should remove key from cache if value is null", async () => {
|
||||
cache.set("test", {});
|
||||
const deleteSpy = jest.spyOn(cache, "delete");
|
||||
const cacheSetSpy = jest.spyOn(cache, "set");
|
||||
expect(cache.has("test")).toBe(true);
|
||||
await sut.save("test", null);
|
||||
expect(cache.has("test")).toBe(false);
|
||||
expect(deleteSpy).toHaveBeenCalledWith("test");
|
||||
// Don't remove from cache, just replace with null
|
||||
expect(cache.get("test")).toBe(null);
|
||||
expect(cacheSetSpy).toHaveBeenCalledWith("test", null);
|
||||
});
|
||||
|
||||
it("should set cache if value is non-null", async () => {
|
||||
@@ -197,7 +223,7 @@ describe("Browser Session Storage Service", () => {
|
||||
});
|
||||
|
||||
it("should return the stored symmetric crypto key", async () => {
|
||||
jest.spyOn(sessionStorage, "get").mockResolvedValue({ ...key });
|
||||
sessionStorageService.get.mockResolvedValue({ ...key });
|
||||
const result = await sut.getSessionEncKey();
|
||||
|
||||
expect(result).toStrictEqual(key);
|
||||
@@ -205,7 +231,6 @@ describe("Browser Session Storage Service", () => {
|
||||
|
||||
describe("new key creation", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(sessionStorage, "get").mockResolvedValue(null);
|
||||
keyGenerationService.createKeyWithPurpose.mockResolvedValue({
|
||||
salt: "salt",
|
||||
material: null,
|
||||
@@ -218,25 +243,24 @@ describe("Browser Session Storage Service", () => {
|
||||
const result = await sut.getSessionEncKey();
|
||||
|
||||
expect(result).toStrictEqual(key);
|
||||
expect(keyGenerationService.createKeyWithPurpose).toBeCalledTimes(1);
|
||||
expect(keyGenerationService.createKeyWithPurpose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should store a symmetric crypto key if it makes one", async () => {
|
||||
const spy = jest.spyOn(sut, "setSessionEncKey").mockResolvedValue();
|
||||
await sut.getSessionEncKey();
|
||||
|
||||
expect(spy).toBeCalledWith(key);
|
||||
expect(spy).toHaveBeenCalledWith(key);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getLocalSession", () => {
|
||||
it("should return null if session is null", async () => {
|
||||
const spy = jest.spyOn(localStorage, "get").mockResolvedValue(null);
|
||||
const result = await sut.getLocalSession(key);
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(spy).toBeCalledWith("session");
|
||||
expect(localStorageService.get).toHaveBeenCalledWith("session_test");
|
||||
});
|
||||
|
||||
describe("non-null sessions", () => {
|
||||
@@ -245,7 +269,7 @@ describe("Browser Session Storage Service", () => {
|
||||
const decryptedSession = JSON.stringify(session);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(localStorage, "get").mockResolvedValue(encSession.encryptedString);
|
||||
localStorageService.get.mockResolvedValue(encSession.encryptedString);
|
||||
});
|
||||
|
||||
it("should decrypt returned sessions", async () => {
|
||||
@@ -267,13 +291,12 @@ describe("Browser Session Storage Service", () => {
|
||||
it("should remove state if decryption fails", async () => {
|
||||
encryptService.decryptToUtf8.mockResolvedValue(null);
|
||||
const setSessionEncKeySpy = jest.spyOn(sut, "setSessionEncKey").mockResolvedValue();
|
||||
const removeLocalSessionSpy = jest.spyOn(localStorage, "remove").mockResolvedValue();
|
||||
|
||||
const result = await sut.getLocalSession(key);
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(setSessionEncKeySpy).toHaveBeenCalledWith(null);
|
||||
expect(removeLocalSessionSpy).toHaveBeenCalledWith("session");
|
||||
expect(localStorageService.remove).toHaveBeenCalledWith("session_test");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -284,7 +307,7 @@ describe("Browser Session Storage Service", () => {
|
||||
|
||||
it("should encrypt a stringified session", async () => {
|
||||
encryptService.encrypt.mockImplementation(mockEnc);
|
||||
jest.spyOn(localStorage, "save").mockResolvedValue();
|
||||
localStorageService.save.mockResolvedValue();
|
||||
await sut.setLocalSession(testSession, key);
|
||||
|
||||
expect(encryptService.encrypt).toHaveBeenNthCalledWith(1, testJSON, key);
|
||||
@@ -292,32 +315,31 @@ describe("Browser Session Storage Service", () => {
|
||||
|
||||
it("should remove local session if null", async () => {
|
||||
encryptService.encrypt.mockResolvedValue(null);
|
||||
const spy = jest.spyOn(localStorage, "remove").mockResolvedValue();
|
||||
await sut.setLocalSession(null, key);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith("session");
|
||||
expect(localStorageService.remove).toHaveBeenCalledWith("session_test");
|
||||
});
|
||||
|
||||
it("should save encrypted string", async () => {
|
||||
encryptService.encrypt.mockImplementation(mockEnc);
|
||||
const spy = jest.spyOn(localStorage, "save").mockResolvedValue();
|
||||
await sut.setLocalSession(testSession, key);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith("session", (await mockEnc(testJSON)).encryptedString);
|
||||
expect(localStorageService.save).toHaveBeenCalledWith(
|
||||
"session_test",
|
||||
(await mockEnc(testJSON)).encryptedString,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setSessionKey", () => {
|
||||
it("should remove if null", async () => {
|
||||
const spy = jest.spyOn(sessionStorage, "remove").mockResolvedValue();
|
||||
await sut.setSessionEncKey(null);
|
||||
expect(spy).toHaveBeenCalledWith("localEncryptionKey");
|
||||
expect(sessionStorageService.remove).toHaveBeenCalledWith("localEncryptionKey_test");
|
||||
});
|
||||
|
||||
it("should save key when not null", async () => {
|
||||
const spy = jest.spyOn(sessionStorage, "save").mockResolvedValue();
|
||||
await sut.setSessionEncKey(key);
|
||||
expect(spy).toHaveBeenCalledWith("localEncryptionKey", key);
|
||||
expect(sessionStorageService.save).toHaveBeenCalledWith("localEncryptionKey_test", key);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,40 +1,60 @@
|
||||
import { Subject } from "rxjs";
|
||||
import { Observable, Subject, filter, map, merge, share, tap } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import {
|
||||
AbstractMemoryStorageService,
|
||||
AbstractStorageService,
|
||||
ObservableStorageService,
|
||||
StorageUpdate,
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { MemoryStorageOptions } from "@bitwarden/common/platform/models/domain/storage-options";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
import { fromChromeEvent } from "../browser/from-chrome-event";
|
||||
import { devFlag } from "../decorators/dev-flag.decorator";
|
||||
import { devFlagEnabled } from "../flags";
|
||||
|
||||
import BrowserLocalStorageService from "./browser-local-storage.service";
|
||||
import BrowserMemoryStorageService from "./browser-memory-storage.service";
|
||||
|
||||
const keys = {
|
||||
encKey: "localEncryptionKey",
|
||||
sessionKey: "session",
|
||||
};
|
||||
|
||||
export class LocalBackedSessionStorageService extends AbstractMemoryStorageService {
|
||||
export class LocalBackedSessionStorageService
|
||||
extends AbstractMemoryStorageService
|
||||
implements ObservableStorageService
|
||||
{
|
||||
private cache = new Map<string, unknown>();
|
||||
private localStorage = new BrowserLocalStorageService();
|
||||
private sessionStorage = new BrowserMemoryStorageService();
|
||||
private updatesSubject = new Subject<StorageUpdate>();
|
||||
updates$;
|
||||
|
||||
private commandName = `localBackedSessionStorage_${this.name}`;
|
||||
private encKey = `localEncryptionKey_${this.name}`;
|
||||
private sessionKey = `session_${this.name}`;
|
||||
|
||||
updates$: Observable<StorageUpdate>;
|
||||
|
||||
constructor(
|
||||
private encryptService: EncryptService,
|
||||
private keyGenerationService: KeyGenerationService,
|
||||
private localStorage: AbstractStorageService,
|
||||
private sessionStorage: AbstractStorageService,
|
||||
private name: string,
|
||||
) {
|
||||
super();
|
||||
this.updates$ = this.updatesSubject.asObservable();
|
||||
|
||||
const remoteObservable = fromChromeEvent(chrome.runtime.onMessage).pipe(
|
||||
filter(([msg]) => msg.command === this.commandName),
|
||||
map(([msg]) => msg.update as StorageUpdate),
|
||||
tap((update) => {
|
||||
if (update.updateType === "remove") {
|
||||
this.cache.set(update.key, null);
|
||||
} else {
|
||||
this.cache.delete(update.key);
|
||||
}
|
||||
}),
|
||||
share(),
|
||||
);
|
||||
|
||||
remoteObservable.subscribe();
|
||||
|
||||
this.updates$ = merge(this.updatesSubject.asObservable(), remoteObservable);
|
||||
}
|
||||
|
||||
get valuesRequireDeserialization(): boolean {
|
||||
@@ -70,23 +90,37 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
|
||||
|
||||
async save<T>(key: string, obj: T): Promise<void> {
|
||||
if (obj == null) {
|
||||
this.cache.delete(key);
|
||||
} else {
|
||||
this.cache.set(key, obj);
|
||||
return await this.remove(key);
|
||||
}
|
||||
|
||||
this.cache.set(key, obj);
|
||||
await this.updateLocalSessionValue(key, obj);
|
||||
this.sendUpdate({ key, updateType: "save" });
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
this.cache.set(key, null);
|
||||
await this.updateLocalSessionValue(key, null);
|
||||
this.sendUpdate({ key, updateType: "remove" });
|
||||
}
|
||||
|
||||
sendUpdate(storageUpdate: StorageUpdate) {
|
||||
this.updatesSubject.next(storageUpdate);
|
||||
void chrome.runtime.sendMessage({
|
||||
command: this.commandName,
|
||||
update: storageUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
private async updateLocalSessionValue<T>(key: string, obj: T) {
|
||||
const sessionEncKey = await this.getSessionEncKey();
|
||||
const localSession = (await this.getLocalSession(sessionEncKey)) ?? {};
|
||||
localSession[key] = obj;
|
||||
await this.setLocalSession(localSession, sessionEncKey);
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
await this.save(key, null);
|
||||
}
|
||||
|
||||
async getLocalSession(encKey: SymmetricCryptoKey): Promise<Record<string, unknown>> {
|
||||
const local = await this.localStorage.get<string>(keys.sessionKey);
|
||||
const local = await this.localStorage.get<string>(this.sessionKey);
|
||||
|
||||
if (local == null) {
|
||||
return null;
|
||||
@@ -100,7 +134,7 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
|
||||
if (sessionJson == null) {
|
||||
// Error with decryption -- session is lost, delete state and key and start over
|
||||
await this.setSessionEncKey(null);
|
||||
await this.localStorage.remove(keys.sessionKey);
|
||||
await this.localStorage.remove(this.sessionKey);
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(sessionJson);
|
||||
@@ -119,9 +153,9 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
|
||||
// Make sure we're storing the jsonified version of the session
|
||||
const jsonSession = JSON.parse(JSON.stringify(session));
|
||||
if (session == null) {
|
||||
await this.localStorage.remove(keys.sessionKey);
|
||||
await this.localStorage.remove(this.sessionKey);
|
||||
} else {
|
||||
await this.localStorage.save(keys.sessionKey, jsonSession);
|
||||
await this.localStorage.save(this.sessionKey, jsonSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +164,13 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
|
||||
const encSession = await this.encryptService.encrypt(jsonSession, key);
|
||||
|
||||
if (encSession == null) {
|
||||
return await this.localStorage.remove(keys.sessionKey);
|
||||
return await this.localStorage.remove(this.sessionKey);
|
||||
}
|
||||
await this.localStorage.save(keys.sessionKey, encSession.encryptedString);
|
||||
await this.localStorage.save(this.sessionKey, encSession.encryptedString);
|
||||
}
|
||||
|
||||
async getSessionEncKey(): Promise<SymmetricCryptoKey> {
|
||||
let storedKey = await this.sessionStorage.get<SymmetricCryptoKey>(keys.encKey);
|
||||
let storedKey = await this.sessionStorage.get<SymmetricCryptoKey>(this.encKey);
|
||||
if (storedKey == null || Object.keys(storedKey).length == 0) {
|
||||
const generatedKey = await this.keyGenerationService.createKeyWithPurpose(
|
||||
128,
|
||||
@@ -153,9 +187,9 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
|
||||
|
||||
async setSessionEncKey(input: SymmetricCryptoKey): Promise<void> {
|
||||
if (input == null) {
|
||||
await this.sessionStorage.remove(keys.encKey);
|
||||
await this.sessionStorage.remove(this.encKey);
|
||||
} else {
|
||||
await this.sessionStorage.save(keys.encKey, input);
|
||||
await this.sessionStorage.save(this.encKey, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { BrowserApi } from "../platform/browser/browser-api";
|
||||
import { ZonedMessageListenerService } from "../platform/browser/zoned-message-listener.service";
|
||||
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
|
||||
import { ForegroundPlatformUtilsService } from "../platform/services/platform-utils/foreground-platform-utils.service";
|
||||
import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service";
|
||||
|
||||
import { routerTransition } from "./app-routing.animations";
|
||||
import { DesktopSyncVerificationDialogComponent } from "./components/desktop-sync-verification-dialog.component";
|
||||
@@ -37,6 +38,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private i18nService: I18nService,
|
||||
private router: Router,
|
||||
private stateService: BrowserStateService,
|
||||
private vaultBrowserStateService: VaultBrowserStateService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private ngZone: NgZone,
|
||||
private platformUtilsService: ForegroundPlatformUtilsService,
|
||||
@@ -140,7 +142,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
};
|
||||
|
||||
(window as any).bitwardenPopupMainMessageListener = bitwardenPopupMainMessageListener;
|
||||
(self as any).bitwardenPopupMainMessageListener = bitwardenPopupMainMessageListener;
|
||||
this.browserMessagingApi.messageListener("app.component", bitwardenPopupMainMessageListener);
|
||||
|
||||
// eslint-disable-next-line rxjs/no-async-subscribe
|
||||
@@ -227,8 +229,8 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.stateService.setBrowserGroupingComponentState(null),
|
||||
this.stateService.setBrowserVaultItemsComponentState(null),
|
||||
this.vaultBrowserStateService.setBrowserGroupingsComponentState(null),
|
||||
this.vaultBrowserStateService.setBrowserVaultItemsComponentState(null),
|
||||
this.stateService.setBrowserSendComponentState(null),
|
||||
this.stateService.setBrowserSendTypeComponentState(null),
|
||||
]);
|
||||
|
||||
@@ -5,7 +5,6 @@ import { AbstractThemingService } from "@bitwarden/angular/platform/services/the
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
||||
@@ -19,7 +18,6 @@ export class InitService {
|
||||
private stateService: StateServiceAbstraction,
|
||||
private logService: LogServiceAbstraction,
|
||||
private themingService: AbstractThemingService,
|
||||
private configService: ConfigService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {}
|
||||
|
||||
@@ -55,7 +53,6 @@ export class InitService {
|
||||
this.logService.info("Force redraw is on");
|
||||
}
|
||||
|
||||
this.configService.init();
|
||||
this.setupVaultPopupHeartbeat();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
|
||||
export class PopupSearchService extends SearchService {
|
||||
constructor(
|
||||
private mainSearchService: SearchService,
|
||||
consoleLogService: ConsoleLogService,
|
||||
logService: LogService,
|
||||
i18nService: I18nService,
|
||||
) {
|
||||
super(consoleLogService, i18nService);
|
||||
super(logService, i18nService);
|
||||
}
|
||||
|
||||
clearIndex() {
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { Router } from "@angular/router";
|
||||
import { ToastrService } from "ngx-toastr";
|
||||
|
||||
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
|
||||
import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service";
|
||||
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
|
||||
import {
|
||||
MEMORY_STORAGE,
|
||||
SECURE_STORAGE,
|
||||
OBSERVABLE_DISK_STORAGE,
|
||||
OBSERVABLE_MEMORY_STORAGE,
|
||||
SYSTEM_THEME_OBSERVABLE,
|
||||
SafeInjectionToken,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import {
|
||||
AuthRequestServiceAbstraction,
|
||||
LoginStrategyServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
@@ -28,13 +30,11 @@ import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/ab
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||
import {
|
||||
AutofillSettingsService,
|
||||
AutofillSettingsServiceAbstraction,
|
||||
@@ -47,19 +47,13 @@ import {
|
||||
UserNotificationSettingsService,
|
||||
UserNotificationSettingsServiceAbstraction,
|
||||
} from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
import { FileUploadService } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import {
|
||||
LogService,
|
||||
LogService as LogServiceAbstraction,
|
||||
} from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
@@ -69,7 +63,6 @@ import {
|
||||
} from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
@@ -82,12 +75,6 @@ import {
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
|
||||
import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import {
|
||||
InternalSendService as InternalSendServiceAbstraction,
|
||||
SendService,
|
||||
} from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { CipherFileUploadService } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
|
||||
@@ -103,10 +90,9 @@ import MainBackground from "../../background/main.background";
|
||||
import { Account } from "../../models/account";
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
||||
import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service";
|
||||
import { BrowserStateService as StateServiceAbstraction } from "../../platform/services/abstractions/browser-state.service";
|
||||
import { BrowserConfigService } from "../../platform/services/browser-config.service";
|
||||
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
|
||||
import { BrowserFileDownloadService } from "../../platform/services/browser-file-download.service";
|
||||
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
|
||||
import BrowserMessagingPrivateModePopupService from "../../platform/services/browser-messaging-private-mode-popup.service";
|
||||
import BrowserMessagingService from "../../platform/services/browser-messaging.service";
|
||||
@@ -115,8 +101,8 @@ import I18nService from "../../platform/services/i18n.service";
|
||||
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
|
||||
import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider";
|
||||
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
|
||||
import { BrowserSendService } from "../../services/browser-send.service";
|
||||
import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service";
|
||||
import { VaultBrowserStateService } from "../../vault/services/vault-browser-state.service";
|
||||
import { VaultFilterService } from "../../vault/services/vault-filter.service";
|
||||
|
||||
import { DebounceNavigationService } from "./debounce-navigation.service";
|
||||
@@ -145,99 +131,115 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
InitService,
|
||||
DebounceNavigationService,
|
||||
DialogService,
|
||||
PopupCloseWarningService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
/**
|
||||
* Provider definitions used in the ngModule.
|
||||
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
|
||||
* If you need help please ask for it, do NOT change the type of this array.
|
||||
*/
|
||||
const safeProviders: SafeProvider[] = [
|
||||
safeProvider(InitService),
|
||||
safeProvider(DebounceNavigationService),
|
||||
safeProvider(DialogService),
|
||||
safeProvider(PopupCloseWarningService),
|
||||
safeProvider({
|
||||
provide: APP_INITIALIZER as SafeInjectionToken<() => Promise<void>>,
|
||||
useFactory: (initService: InitService) => initService.init(),
|
||||
deps: [InitService],
|
||||
multi: true,
|
||||
},
|
||||
{ provide: BaseUnauthGuardService, useClass: UnauthGuardService },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BaseUnauthGuardService,
|
||||
useClass: UnauthGuardService,
|
||||
deps: [AuthServiceAbstraction, Router],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: MessagingService,
|
||||
useFactory: () => {
|
||||
return needsBackgroundInit
|
||||
? new BrowserMessagingPrivateModePopupService()
|
||||
: new BrowserMessagingService();
|
||||
},
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TwoFactorService,
|
||||
useFactory: getBgService<TwoFactorService>("twoFactorService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AuthServiceAbstraction,
|
||||
useFactory: getBgService<AuthService>("authService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LoginStrategyServiceAbstraction,
|
||||
useFactory: getBgService<LoginStrategyServiceAbstraction>("loginStrategyService"),
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SsoLoginServiceAbstraction,
|
||||
useFactory: getBgService<SsoLoginServiceAbstraction>("ssoLoginService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SearchServiceAbstraction,
|
||||
useFactory: (logService: ConsoleLogService, i18nService: I18nServiceAbstraction) => {
|
||||
useFactory: (logService: LogService, i18nService: I18nServiceAbstraction) => {
|
||||
return new PopupSearchService(
|
||||
getBgService<SearchService>("searchService")(),
|
||||
logService,
|
||||
i18nService,
|
||||
);
|
||||
},
|
||||
deps: [LogServiceAbstraction, I18nServiceAbstraction],
|
||||
},
|
||||
{
|
||||
deps: [LogService, I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CipherFileUploadService,
|
||||
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
|
||||
deps: [],
|
||||
},
|
||||
{ provide: CipherService, useFactory: getBgService<CipherService>("cipherService"), deps: [] },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CipherService,
|
||||
useFactory: getBgService<CipherService>("cipherService"),
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoFunctionService,
|
||||
useFactory: () => new WebCryptoFunctionService(window),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CollectionService,
|
||||
useFactory: getBgService<CollectionService>("collectionService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: LogServiceAbstraction,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LogService,
|
||||
useFactory: (platformUtilsService: PlatformUtilsService) =>
|
||||
new ConsoleLogService(platformUtilsService.isDev()),
|
||||
deps: [PlatformUtilsService],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: EnvironmentService,
|
||||
useExisting: BrowserEnvironmentService,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BrowserEnvironmentService,
|
||||
useClass: BrowserEnvironmentService,
|
||||
deps: [LogService, StateProvider, AccountServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useExisting: BrowserEnvironmentService,
|
||||
},
|
||||
{ provide: TotpService, useFactory: getBgService<TotpService>("totpService"), deps: [] },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TotpService,
|
||||
useFactory: getBgService<TotpService>("totpService"),
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: I18nServiceAbstraction,
|
||||
useFactory: (globalStateProvider: GlobalStateProvider) => {
|
||||
return new I18nService(BrowserApi.getUILanguage(), globalStateProvider);
|
||||
},
|
||||
deps: [GlobalStateProvider],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoService,
|
||||
useFactory: (encryptService: EncryptService) => {
|
||||
const cryptoService = getBgService<CryptoService>("cryptoService")();
|
||||
@@ -245,27 +247,27 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
return cryptoService;
|
||||
},
|
||||
deps: [EncryptService],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AuthRequestServiceAbstraction,
|
||||
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DeviceTrustCryptoServiceAbstraction,
|
||||
useFactory: getBgService<DeviceTrustCryptoServiceAbstraction>("deviceTrustCryptoService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DevicesServiceAbstraction,
|
||||
useFactory: getBgService<DevicesServiceAbstraction>("devicesService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: PlatformUtilsService,
|
||||
useExisting: ForegroundPlatformUtilsService,
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ForegroundPlatformUtilsService,
|
||||
useClass: ForegroundPlatformUtilsService,
|
||||
useFactory: (sanitizer: DomSanitizer, toastrService: ToastrService) => {
|
||||
@@ -289,91 +291,63 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
);
|
||||
},
|
||||
deps: [DomSanitizer, ToastrService],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: PasswordGenerationServiceAbstraction,
|
||||
useFactory: getBgService<PasswordGenerationServiceAbstraction>("passwordGenerationService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
provide: SendService,
|
||||
useFactory: (
|
||||
cryptoService: CryptoService,
|
||||
i18nService: I18nServiceAbstraction,
|
||||
keyGenerationService: KeyGenerationService,
|
||||
stateServiceAbstraction: StateServiceAbstraction,
|
||||
) => {
|
||||
return new BrowserSendService(
|
||||
cryptoService,
|
||||
i18nService,
|
||||
keyGenerationService,
|
||||
stateServiceAbstraction,
|
||||
);
|
||||
},
|
||||
deps: [CryptoService, I18nServiceAbstraction, KeyGenerationService, StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: InternalSendServiceAbstraction,
|
||||
useExisting: SendService,
|
||||
},
|
||||
{
|
||||
provide: SendApiServiceAbstraction,
|
||||
useFactory: (
|
||||
apiService: ApiService,
|
||||
fileUploadService: FileUploadService,
|
||||
sendService: InternalSendServiceAbstraction,
|
||||
) => {
|
||||
return new SendApiService(apiService, fileUploadService, sendService);
|
||||
},
|
||||
deps: [ApiService, FileUploadService, InternalSendServiceAbstraction],
|
||||
},
|
||||
{ provide: SyncService, useFactory: getBgService<SyncService>("syncService"), deps: [] },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SyncService,
|
||||
useFactory: getBgService<SyncService>("syncService"),
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DomainSettingsService,
|
||||
useClass: DefaultDomainSettingsService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AbstractStorageService,
|
||||
useClass: BrowserLocalStorageService,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AutofillService,
|
||||
useFactory: getBgService<AutofillService>("autofillService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultExportServiceAbstraction,
|
||||
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: KeyConnectorService,
|
||||
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: UserVerificationService,
|
||||
useFactory: getBgService<UserVerificationService>("userVerificationService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultTimeoutSettingsService,
|
||||
useFactory: getBgService<VaultTimeoutSettingsService>("vaultTimeoutSettingsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultTimeoutService,
|
||||
useFactory: getBgService<VaultTimeoutService>("vaultTimeoutService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: NotificationsService,
|
||||
useFactory: getBgService<NotificationsService>("notificationsService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultFilterService,
|
||||
useClass: VaultFilterService,
|
||||
deps: [
|
||||
@@ -385,31 +359,39 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
StateProvider,
|
||||
AccountServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SECURE_STORAGE,
|
||||
useExisting: AbstractStorageService, // Secure storage is not available in the browser, so we use normal storage instead and warn users when it is used.
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: MEMORY_STORAGE,
|
||||
useFactory: getBgService<AbstractStorageService>("memoryStorageService"),
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: OBSERVABLE_MEMORY_STORAGE,
|
||||
useClass: ForegroundMemoryStorageService,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: OBSERVABLE_DISK_STORAGE,
|
||||
useExisting: AbstractStorageService,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: VaultBrowserStateService,
|
||||
useFactory: (stateProvider: StateProvider) => {
|
||||
return new VaultBrowserStateService(stateProvider);
|
||||
},
|
||||
{
|
||||
deps: [StateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: StateServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: AbstractStorageService,
|
||||
secureStorageService: AbstractStorageService,
|
||||
memoryStorageService: AbstractMemoryStorageService,
|
||||
logService: LogServiceAbstraction,
|
||||
logService: LogService,
|
||||
accountService: AccountServiceAbstraction,
|
||||
environmentService: EnvironmentService,
|
||||
tokenService: TokenService,
|
||||
@@ -431,33 +413,29 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
AbstractStorageService,
|
||||
SECURE_STORAGE,
|
||||
MEMORY_STORAGE,
|
||||
LogServiceAbstraction,
|
||||
LogService,
|
||||
AccountServiceAbstraction,
|
||||
EnvironmentService,
|
||||
TokenService,
|
||||
MigrationRunner,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: UsernameGenerationServiceAbstraction,
|
||||
useFactory: getBgService<UsernameGenerationServiceAbstraction>("usernameGenerationService"),
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BaseStateServiceAbstraction,
|
||||
useExisting: StateServiceAbstraction,
|
||||
deps: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: FileDownloadService,
|
||||
useClass: BrowserFileDownloadService,
|
||||
},
|
||||
{
|
||||
provide: LoginServiceAbstraction,
|
||||
useClass: LoginService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SYSTEM_THEME_OBSERVABLE,
|
||||
useFactory: (platformUtilsService: PlatformUtilsService) => {
|
||||
// Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light.
|
||||
@@ -471,41 +449,35 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
return AngularThemingService.createSystemThemeFromWindow(windowContext);
|
||||
},
|
||||
deps: [PlatformUtilsService],
|
||||
},
|
||||
{
|
||||
provide: ConfigService,
|
||||
useClass: BrowserConfigService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
ConfigApiServiceAbstraction,
|
||||
AuthServiceAbstraction,
|
||||
EnvironmentService,
|
||||
StateProvider,
|
||||
LogService,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: FilePopoutUtilsService,
|
||||
useFactory: (platformUtilsService: PlatformUtilsService) => {
|
||||
return new FilePopoutUtilsService(platformUtilsService);
|
||||
},
|
||||
deps: [PlatformUtilsService],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DerivedStateProvider,
|
||||
useClass: ForegroundDerivedStateProvider,
|
||||
deps: [OBSERVABLE_MEMORY_STORAGE, NgZone],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AutofillSettingsServiceAbstraction,
|
||||
useClass: AutofillSettingsService,
|
||||
deps: [StateProvider, PolicyService],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: UserNotificationSettingsServiceAbstraction,
|
||||
useClass: UserNotificationSettingsService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
||||
providers: safeProviders,
|
||||
})
|
||||
export class ServicesModule {}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component } from "@angular/core";
|
||||
import { combineLatest, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { ButtonModule, DialogModule } from "@bitwarden/components";
|
||||
|
||||
@@ -24,7 +24,7 @@ export class AboutComponent {
|
||||
]).pipe(map(([serverConfig, isCloud]) => ({ serverConfig, isCloud })));
|
||||
|
||||
constructor(
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private environmentService: EnvironmentService,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
|
||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
|
||||
|
||||
import { browserSession, sessionSync } from "../platform/decorators/session-sync-observable";
|
||||
|
||||
@browserSession
|
||||
export class BrowserSendService extends SendService {
|
||||
@sessionSync({ initializer: Send.fromJSON, initializeAs: "array" })
|
||||
protected _sends: BehaviorSubject<Send[]>;
|
||||
@sessionSync({ initializer: SendView.fromJSON, initializeAs: "array" })
|
||||
protected _sendViews: BehaviorSubject<SendView[]>;
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { firstValueFrom } from "rxjs";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { Importer, ImportResult, ImportServiceAbstraction } from "@bitwarden/importer/core";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { ImportServiceAbstraction } from "@bitwarden/importer/core";
|
||||
|
||||
@@ -55,7 +55,7 @@ class FilelessImporterBackground implements FilelessImporterBackgroundInterface
|
||||
* @param syncService - Used to trigger a full sync after the import is completed.
|
||||
*/
|
||||
constructor(
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private authService: AuthService,
|
||||
private policyService: PolicyService,
|
||||
private notificationBackground: NotificationBackground,
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component } from "@angular/core";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
import { Router } from "@angular/router";
|
||||
|
||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
@@ -13,6 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/vault-export-ui";
|
||||
|
||||
@Component({
|
||||
selector: "app-export",
|
||||
|
||||
@@ -138,13 +138,23 @@
|
||||
attr.aria-label="{{ 'typePasskey' | i18n }} {{ fido2CredentialCreationDateValue }}"
|
||||
>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row text-muted">
|
||||
<div class="box-content-row box-content-row-multi text-muted" appBoxRow>
|
||||
<button
|
||||
type="button"
|
||||
appStopClick
|
||||
(click)="removePasskey()"
|
||||
appA11yTitle="{{ 'removePasskey' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-minus-circle bwi-lg" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="row-main">
|
||||
<span class="row-label">{{ "typePasskey" | i18n }}</span>
|
||||
{{ "dateCreated" | i18n }}
|
||||
{{ cipher.login.fido2Credentials[0].creationDate | date: "short" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
|
||||
@@ -11,7 +11,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
@@ -68,7 +68,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
sendApiService: SendApiService,
|
||||
dialogService: DialogService,
|
||||
datePipe: DatePipe,
|
||||
configService: ConfigServiceAbstraction,
|
||||
configService: ConfigService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
|
||||
@@ -20,7 +20,7 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { BrowserGroupingsComponentState } from "../../../../models/browserGroupingsComponentState";
|
||||
import { BrowserApi } from "../../../../platform/browser/browser-api";
|
||||
import BrowserPopupUtils from "../../../../platform/popup/browser-popup-utils";
|
||||
import { BrowserStateService } from "../../../../platform/services/abstractions/browser-state.service";
|
||||
import { VaultBrowserStateService } from "../../../services/vault-browser-state.service";
|
||||
import { VaultFilterService } from "../../../services/vault-filter.service";
|
||||
|
||||
const ComponentId = "VaultComponent";
|
||||
@@ -84,8 +84,8 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private searchService: SearchService,
|
||||
private location: Location,
|
||||
private browserStateService: BrowserStateService,
|
||||
private vaultFilterService: VaultFilterService,
|
||||
private vaultBrowserStateService: VaultBrowserStateService,
|
||||
) {
|
||||
this.noFolderListSize = 100;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
this.showLeftHeader = !(
|
||||
BrowserPopupUtils.inSidebar(window) && this.platformUtilsService.isFirefox()
|
||||
);
|
||||
await this.browserStateService.setBrowserVaultItemsComponentState(null);
|
||||
await this.vaultBrowserStateService.setBrowserVaultItemsComponentState(null);
|
||||
|
||||
this.broadcasterService.subscribe(ComponentId, (message: any) => {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
@@ -120,7 +120,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
const restoredScopeState = await this.restoreState();
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
this.state = await this.browserStateService.getBrowserGroupingComponentState();
|
||||
this.state = await this.vaultBrowserStateService.getBrowserGroupingsComponentState();
|
||||
if (this.state?.searchText) {
|
||||
this.searchText = this.state.searchText;
|
||||
} else if (params.searchText) {
|
||||
@@ -413,11 +413,11 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
|
||||
collections: this.collections,
|
||||
deletedCount: this.deletedCount,
|
||||
});
|
||||
await this.browserStateService.setBrowserGroupingComponentState(this.state);
|
||||
await this.vaultBrowserStateService.setBrowserGroupingsComponentState(this.state);
|
||||
}
|
||||
|
||||
private async restoreState(): Promise<boolean> {
|
||||
this.state = await this.browserStateService.getBrowserGroupingComponentState();
|
||||
this.state = await this.vaultBrowserStateService.getBrowserGroupingsComponentState();
|
||||
if (this.state == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { BrowserComponentState } from "../../../../models/browserComponentState";
|
||||
import { BrowserApi } from "../../../../platform/browser/browser-api";
|
||||
import BrowserPopupUtils from "../../../../platform/popup/browser-popup-utils";
|
||||
import { BrowserStateService } from "../../../../platform/services/abstractions/browser-state.service";
|
||||
import { VaultBrowserStateService } from "../../../services/vault-browser-state.service";
|
||||
import { VaultFilterService } from "../../../services/vault-filter.service";
|
||||
|
||||
const ComponentId = "VaultItemsComponent";
|
||||
@@ -59,7 +59,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
|
||||
private ngZone: NgZone,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private stateService: BrowserStateService,
|
||||
private stateService: VaultBrowserStateService,
|
||||
private i18nService: I18nService,
|
||||
private collectionService: CollectionService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
FakeAccountService,
|
||||
mockAccountServiceWith,
|
||||
} from "@bitwarden/common/../spec/fake-account-service";
|
||||
import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
import { BrowserComponentState } from "../../models/browserComponentState";
|
||||
import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState";
|
||||
|
||||
import {
|
||||
VAULT_BROWSER_COMPONENT,
|
||||
VAULT_BROWSER_GROUPINGS_COMPONENT,
|
||||
VaultBrowserStateService,
|
||||
} from "./vault-browser-state.service";
|
||||
|
||||
describe("Vault Browser State Service", () => {
|
||||
let stateProvider: FakeStateProvider;
|
||||
|
||||
let accountService: FakeAccountService;
|
||||
let stateService: VaultBrowserStateService;
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
|
||||
beforeEach(() => {
|
||||
accountService = mockAccountServiceWith(mockUserId);
|
||||
stateProvider = new FakeStateProvider(accountService);
|
||||
|
||||
stateService = new VaultBrowserStateService(stateProvider);
|
||||
});
|
||||
|
||||
describe("getBrowserGroupingsComponentState", () => {
|
||||
it("should return a BrowserGroupingsComponentState", async () => {
|
||||
await stateService.setBrowserGroupingsComponentState(new BrowserGroupingsComponentState());
|
||||
|
||||
const actual = await stateService.getBrowserGroupingsComponentState();
|
||||
|
||||
expect(actual).toBeInstanceOf(BrowserGroupingsComponentState);
|
||||
});
|
||||
|
||||
it("should deserialize BrowserGroupingsComponentState", () => {
|
||||
const sut = VAULT_BROWSER_GROUPINGS_COMPONENT;
|
||||
|
||||
const expectedState = {
|
||||
deletedCount: 0,
|
||||
collectionCounts: new Map<string, number>(),
|
||||
folderCounts: new Map<string, number>(),
|
||||
typeCounts: new Map<CipherType, number>(),
|
||||
};
|
||||
|
||||
const result = sut.deserializer(
|
||||
JSON.parse(JSON.stringify(expectedState)) as Jsonify<BrowserGroupingsComponentState>,
|
||||
);
|
||||
|
||||
expect(result).toEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBrowserVaultItemsComponentState", () => {
|
||||
it("should deserialize BrowserComponentState", () => {
|
||||
const sut = VAULT_BROWSER_COMPONENT;
|
||||
|
||||
const expectedState = {
|
||||
scrollY: 0,
|
||||
searchText: "test",
|
||||
};
|
||||
|
||||
const result = sut.deserializer(JSON.parse(JSON.stringify(expectedState)));
|
||||
|
||||
expect(result).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it("should return a BrowserComponentState", async () => {
|
||||
const componentState = new BrowserComponentState();
|
||||
componentState.scrollY = 0;
|
||||
componentState.searchText = "test";
|
||||
|
||||
await stateService.setBrowserVaultItemsComponentState(componentState);
|
||||
|
||||
const actual = await stateService.getBrowserVaultItemsComponentState();
|
||||
expect(actual).toStrictEqual(componentState);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Observable, firstValueFrom } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import {
|
||||
ActiveUserState,
|
||||
KeyDefinition,
|
||||
StateProvider,
|
||||
VAULT_BROWSER_MEMORY,
|
||||
} from "@bitwarden/common/platform/state";
|
||||
|
||||
import { BrowserComponentState } from "../../models/browserComponentState";
|
||||
import { BrowserGroupingsComponentState } from "../../models/browserGroupingsComponentState";
|
||||
|
||||
export const VAULT_BROWSER_GROUPINGS_COMPONENT = new KeyDefinition<BrowserGroupingsComponentState>(
|
||||
VAULT_BROWSER_MEMORY,
|
||||
"vault_browser_groupings_component",
|
||||
{
|
||||
deserializer: (obj: Jsonify<BrowserGroupingsComponentState>) =>
|
||||
BrowserGroupingsComponentState.fromJSON(obj),
|
||||
},
|
||||
);
|
||||
|
||||
export const VAULT_BROWSER_COMPONENT = new KeyDefinition<BrowserComponentState>(
|
||||
VAULT_BROWSER_MEMORY,
|
||||
"vault_browser_component",
|
||||
{
|
||||
deserializer: (obj: Jsonify<BrowserComponentState>) => BrowserComponentState.fromJSON(obj),
|
||||
},
|
||||
);
|
||||
|
||||
export class VaultBrowserStateService {
|
||||
vaultBrowserGroupingsComponentState$: Observable<BrowserGroupingsComponentState>;
|
||||
vaultBrowserComponentState$: Observable<BrowserComponentState>;
|
||||
|
||||
private activeUserVaultBrowserGroupingsComponentState: ActiveUserState<BrowserGroupingsComponentState>;
|
||||
private activeUserVaultBrowserComponentState: ActiveUserState<BrowserComponentState>;
|
||||
|
||||
constructor(protected stateProvider: StateProvider) {
|
||||
this.activeUserVaultBrowserGroupingsComponentState = this.stateProvider.getActive(
|
||||
VAULT_BROWSER_GROUPINGS_COMPONENT,
|
||||
);
|
||||
this.activeUserVaultBrowserComponentState =
|
||||
this.stateProvider.getActive(VAULT_BROWSER_COMPONENT);
|
||||
|
||||
this.vaultBrowserGroupingsComponentState$ =
|
||||
this.activeUserVaultBrowserGroupingsComponentState.state$;
|
||||
this.vaultBrowserComponentState$ = this.activeUserVaultBrowserComponentState.state$;
|
||||
}
|
||||
|
||||
async getBrowserGroupingsComponentState(): Promise<BrowserGroupingsComponentState> {
|
||||
return await firstValueFrom(this.vaultBrowserGroupingsComponentState$);
|
||||
}
|
||||
|
||||
async setBrowserGroupingsComponentState(value: BrowserGroupingsComponentState): Promise<void> {
|
||||
await this.activeUserVaultBrowserGroupingsComponentState.update(() => value);
|
||||
}
|
||||
|
||||
async getBrowserVaultItemsComponentState(): Promise<BrowserComponentState> {
|
||||
return await firstValueFrom(this.vaultBrowserComponentState$);
|
||||
}
|
||||
|
||||
async setBrowserVaultItemsComponentState(value: BrowserComponentState): Promise<void> {
|
||||
await this.activeUserVaultBrowserComponentState.update(() => value);
|
||||
}
|
||||
}
|
||||
@@ -118,58 +118,58 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Bitwarden – Free Password Manager</value>
|
||||
<value>Bitwarden – Xestor de contrasinais gratuíto</value>
|
||||
</data>
|
||||
<data name="Summary" xml:space="preserve">
|
||||
<value>A secure and free password manager for all of your devices</value>
|
||||
<value>Un xestor de contrasinais seguro e gratuíto para todos os teus dispositivos</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Bitwarden, Inc. is the parent company of 8bit Solutions LLC.
|
||||
<value>Bitwarden, Inc. é a empresa matriz de 8bit Solutions LLC.
|
||||
|
||||
NAMED BEST PASSWORD MANAGER BY THE VERGE, U.S. NEWS & WORLD REPORT, CNET, AND MORE.
|
||||
NOMEADO MELLOR ADMINISTRADOR DE CONTRASINAIS POR THE VERGE, Ou.S. NEWS & WORLD REPORT, CNET E MÁS.
|
||||
|
||||
Manage, store, secure, and share unlimited passwords across unlimited devices from anywhere. Bitwarden delivers open source password management solutions to everyone, whether at home, at work, or on the go.
|
||||
Administre, almacene, protexa e comparta contrasinais ilimitados en dispositivos ilimitados desde calquera lugar. Bitwarden ofrece solucións de xestión de contrasinais de código aberto para todos, xa sexa en casa, no traballo ou en mentres estás de viaxe.
|
||||
|
||||
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
|
||||
Xere contrasinais seguros, únicas e aleatorias en función dos requisitos de seguridade de cada sitio web que frecuenta.
|
||||
|
||||
Bitwarden Send quickly transmits encrypted information --- files and plaintext -- directly to anyone.
|
||||
Bitwarden Send transmite rapidamente información cifrada --- arquivos e texto sen formato, directamente a calquera persoa.
|
||||
|
||||
Bitwarden offers Teams and Enterprise plans for companies so you can securely share passwords with colleagues.
|
||||
Bitwarden ofrece plans Teams e Enterprise para empresas para que poida compartir contrasinais de forma segura con colegas.
|
||||
|
||||
Why Choose Bitwarden:
|
||||
Por que elixir Bitwarden?
|
||||
|
||||
World-Class Encryption
|
||||
Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private.
|
||||
Cifrado de clase mundial
|
||||
Os contrasinais están protexidas con cifrado avanzado de extremo a extremo (AES-256 bits, salted hashing e PBKDF2 XA-256) para que os seus datos permanezan seguros e privados.
|
||||
|
||||
Built-in Password Generator
|
||||
Generate strong, unique, and random passwords based on security requirements for every website you frequent.
|
||||
Xerador de contrasinais incorporado
|
||||
Xere contrasinais fortes, únicas e aleatorias en función dos requisitos de seguridade de cada sitio web que frecuenta.
|
||||
|
||||
Global Translations
|
||||
Bitwarden translations exist in 40 languages and are growing, thanks to our global community.
|
||||
Traducións Globais
|
||||
As traducións de Bitwarden existen en 40 idiomas e están a crecer, grazas á nosa comunidade global.
|
||||
|
||||
Cross-Platform Applications
|
||||
Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more.
|
||||
Aplicacións multiplataforma
|
||||
Protexa e comparta datos confidenciais dentro da súa Caixa Forte de Bitwarden desde calquera navegador, dispositivo móbil ou sistema operativo de escritorio, e máis.
|
||||
</value>
|
||||
</data>
|
||||
<data name="AssetTitle" xml:space="preserve">
|
||||
<value>A secure and free password manager for all of your devices</value>
|
||||
<value>Un xestor de contrasinais seguro e gratuíto para todos os teus dispositivos</value>
|
||||
</data>
|
||||
<data name="ScreenshotSync" xml:space="preserve">
|
||||
<value>Sync and access your vault from multiple devices</value>
|
||||
<value>Sincroniza e accede á túa caixa forte desde múltiples dispositivos</value>
|
||||
</data>
|
||||
<data name="ScreenshotVault" xml:space="preserve">
|
||||
<value>Manage all your logins and passwords from a secure vault</value>
|
||||
<value>Xestiona todos os teus usuarios e contrasinais desde unha caixa forte segura</value>
|
||||
</data>
|
||||
<data name="ScreenshotAutofill" xml:space="preserve">
|
||||
<value>Quickly auto-fill your login credentials into any website that you visit</value>
|
||||
<value>Autocompleta rapidamente os teus datos de acceso en calquera páxina web que visites</value>
|
||||
</data>
|
||||
<data name="ScreenshotMenu" xml:space="preserve">
|
||||
<value>Your vault is also conveniently accessible from the right-click menu</value>
|
||||
<value>A túa caixa forte tamén é facilmente accesible desde o menú de clic dereito</value>
|
||||
</data>
|
||||
<data name="ScreenshotPassword" xml:space="preserve">
|
||||
<value>Automatically generate strong, random, and secure passwords</value>
|
||||
<value>Xera automaticamente contrasinais fortes, aleatorias e seguras</value>
|
||||
</data>
|
||||
<data name="ScreenshotEdit" xml:space="preserve">
|
||||
<value>Your information is managed securely using AES-256 bit encryption</value>
|
||||
<value>A túa información é xestionada de forma segura con cifrado AES de 256 bits</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/cli",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.3.0",
|
||||
"version": "2024.3.1",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
@@ -71,7 +71,7 @@
|
||||
"papaparse": "5.4.1",
|
||||
"proper-lockfile": "4.1.2",
|
||||
"rxjs": "7.8.1",
|
||||
"tldts": "6.1.13",
|
||||
"tldts": "6.1.16",
|
||||
"zxcvbn": "4.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
|
||||
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
|
||||
import {
|
||||
@@ -60,6 +61,7 @@ import { GlobalState } from "@bitwarden/common/platform/models/domain/global-sta
|
||||
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/services/broadcaster.service";
|
||||
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
|
||||
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/services/crypto.service";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
@@ -104,6 +106,7 @@ import {
|
||||
PasswordStrengthServiceAbstraction,
|
||||
} from "@bitwarden/common/tools/password-strength";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
|
||||
import { SendStateProvider } from "@bitwarden/common/tools/send/services/send-state.provider";
|
||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
@@ -131,7 +134,6 @@ import {
|
||||
VaultExportServiceAbstraction,
|
||||
} from "@bitwarden/vault-export-core";
|
||||
|
||||
import { CliConfigService } from "./platform/services/cli-config.service";
|
||||
import { CliPlatformUtilsService } from "./platform/services/cli-platform-utils.service";
|
||||
import { ConsoleLogService } from "./platform/services/console-log.service";
|
||||
import { I18nService } from "./platform/services/i18n.service";
|
||||
@@ -193,6 +195,7 @@ export class Main {
|
||||
sendProgram: SendProgram;
|
||||
logService: ConsoleLogService;
|
||||
sendService: SendService;
|
||||
sendStateProvider: SendStateProvider;
|
||||
fileUploadService: FileUploadService;
|
||||
cipherFileUploadService: CipherFileUploadService;
|
||||
keyConnectorService: KeyConnectorService;
|
||||
@@ -214,7 +217,7 @@ export class Main {
|
||||
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction;
|
||||
authRequestService: AuthRequestService;
|
||||
configApiService: ConfigApiServiceAbstraction;
|
||||
configService: CliConfigService;
|
||||
configService: ConfigService;
|
||||
accountService: AccountService;
|
||||
globalStateProvider: GlobalStateProvider;
|
||||
singleUserStateProvider: SingleUserStateProvider;
|
||||
@@ -318,11 +321,16 @@ export class Main {
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
this.singleUserStateProvider,
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService.supportsSecureStorage(),
|
||||
this.secureStorageService,
|
||||
this.keyGenerationService,
|
||||
this.encryptService,
|
||||
this.logService,
|
||||
);
|
||||
|
||||
const migrationRunner = new MigrationRunner(
|
||||
@@ -343,8 +351,6 @@ export class Main {
|
||||
migrationRunner,
|
||||
);
|
||||
|
||||
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
|
||||
|
||||
this.cryptoService = new CryptoService(
|
||||
this.keyGenerationService,
|
||||
this.cryptoFunctionService,
|
||||
@@ -384,11 +390,14 @@ export class Main {
|
||||
|
||||
this.fileUploadService = new FileUploadService(this.logService);
|
||||
|
||||
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
||||
|
||||
this.sendService = new SendService(
|
||||
this.cryptoService,
|
||||
this.i18nService,
|
||||
this.keyGenerationService,
|
||||
this.stateService,
|
||||
this.sendStateProvider,
|
||||
this.encryptService,
|
||||
);
|
||||
|
||||
this.cipherFileUploadService = new CipherFileUploadService(
|
||||
@@ -423,7 +432,6 @@ export class Main {
|
||||
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
|
||||
|
||||
this.keyConnectorService = new KeyConnectorService(
|
||||
this.stateService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.tokenService,
|
||||
@@ -431,6 +439,7 @@ export class Main {
|
||||
this.organizationService,
|
||||
this.keyGenerationService,
|
||||
async (expired: boolean) => await this.logout(),
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService);
|
||||
@@ -451,11 +460,12 @@ export class Main {
|
||||
this.cryptoFunctionService,
|
||||
this.cryptoService,
|
||||
this.encryptService,
|
||||
this.stateService,
|
||||
this.appIdService,
|
||||
this.devicesApiService,
|
||||
this.i18nService,
|
||||
this.platformUtilsService,
|
||||
this.stateProvider,
|
||||
this.secureStorageService,
|
||||
this.userDecryptionOptionsService,
|
||||
);
|
||||
|
||||
@@ -467,7 +477,7 @@ export class Main {
|
||||
);
|
||||
|
||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||
this.activeUserStateProvider,
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.loginStrategyService = new LoginStrategyService(
|
||||
@@ -494,22 +504,21 @@ export class Main {
|
||||
);
|
||||
|
||||
this.authService = new AuthService(
|
||||
this.accountService,
|
||||
this.messagingService,
|
||||
this.cryptoService,
|
||||
this.apiService,
|
||||
this.stateService,
|
||||
this.tokenService,
|
||||
);
|
||||
|
||||
this.configApiService = new ConfigApiService(this.apiService, this.authService);
|
||||
this.configApiService = new ConfigApiService(this.apiService, this.tokenService);
|
||||
|
||||
this.configService = new CliConfigService(
|
||||
this.stateService,
|
||||
this.configService = new DefaultConfigService(
|
||||
this.configApiService,
|
||||
this.authService,
|
||||
this.environmentService,
|
||||
this.logService,
|
||||
this.stateProvider,
|
||||
true,
|
||||
);
|
||||
|
||||
this.cipherService = new CipherService(
|
||||
@@ -710,13 +719,6 @@ export class Main {
|
||||
this.containerService.attachToGlobal(global);
|
||||
await this.i18nService.init();
|
||||
this.twoFactorService.init();
|
||||
this.configService.init();
|
||||
|
||||
const installedVersion = await this.stateService.getInstalledVersion();
|
||||
const currentVersion = await this.platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null || installedVersion !== currentVersion) {
|
||||
await this.stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { NEVER } from "rxjs";
|
||||
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
|
||||
export class CliConfigService extends ConfigService {
|
||||
// The rxjs timer uses setTimeout/setInterval under the hood, which prevents the node process from exiting
|
||||
// when the command is finished. Cli should never be alive long enough to use the timer, so we disable it.
|
||||
protected refreshTimer$ = NEVER;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export class SendRemovePasswordCommand {
|
||||
try {
|
||||
await this.sendApiService.removePassword(id);
|
||||
|
||||
const updatedSend = await this.sendService.get(id);
|
||||
const updatedSend = await firstValueFrom(this.sendService.get$(id));
|
||||
const decSend = await updatedSend.decrypt();
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const webVaultUrl = env.getWebVaultUrl();
|
||||
|
||||
91
apps/desktop/desktop_native/Cargo.lock
generated
91
apps/desktop/desktop_native/Cargo.lock
generated
@@ -45,9 +45,9 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.3.0"
|
||||
version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08"
|
||||
checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"log",
|
||||
@@ -56,7 +56,6 @@ dependencies = [
|
||||
"objc_id",
|
||||
"parking_lot",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
@@ -176,13 +175,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.5.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
|
||||
checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
"str-buf",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -375,13 +372,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "2.3.1"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"str-buf",
|
||||
]
|
||||
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
@@ -476,12 +469,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.3.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177"
|
||||
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -529,7 +522,7 @@ dependencies = [
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -661,12 +654,12 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -767,9 +760,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi"
|
||||
version = "2.13.3"
|
||||
version = "2.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd063c93b900149304e3ba96ce5bf210cd4f81ef5eb80ded0d100df3e85a3ac0"
|
||||
checksum = "54a63d0570e4c3e0daf7a8d380563610e159f538e20448d6c911337246f40e84"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"ctor",
|
||||
@@ -781,29 +774,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "napi-build"
|
||||
version = "2.0.1"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e"
|
||||
checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43"
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive"
|
||||
version = "2.13.0"
|
||||
version = "2.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da1c6a8fa84d549aa8708fcd062372bf8ec6e849de39016ab921067d21bde367"
|
||||
checksum = "05bb7c37e3c1dda9312fdbe4a9fc7507fca72288ba154ec093e2d49114e727ce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"convert_case",
|
||||
"napi-derive-backend",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-derive-backend"
|
||||
version = "1.0.52"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20bbc7c69168d06a848f925ec5f0e0997f98e8c8d4f2cc30157f0da51c009e17"
|
||||
checksum = "f785a8b8d7b83e925f5aa6d2ae3c159d17fe137ac368dc185bef410e7acdaeb4"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"once_cell",
|
||||
@@ -811,14 +804,14 @@ dependencies = [
|
||||
"quote",
|
||||
"regex",
|
||||
"semver",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "napi-sys"
|
||||
version = "2.2.3"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3"
|
||||
checksum = "2503fa6af34dc83fb74888df8b22afe933b58d37daf7d80424b1c60c68196b8b"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
@@ -896,9 +889,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
@@ -1201,12 +1194,6 @@ version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@@ -1506,15 +1493,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-wsapoll"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -1704,22 +1682,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a"
|
||||
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"nix",
|
||||
"winapi",
|
||||
"winapi-wsapoll",
|
||||
"rustix",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc"
|
||||
dependencies = [
|
||||
"nix",
|
||||
]
|
||||
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
|
||||
|
||||
@@ -15,11 +15,11 @@ manual_test = []
|
||||
[dependencies]
|
||||
aes = "=0.8.4"
|
||||
anyhow = "=1.0.80"
|
||||
arboard = { version = "=3.3.0", default-features = false, features = ["wayland-data-control"] }
|
||||
arboard = { version = "=3.3.2", default-features = false, features = ["wayland-data-control"] }
|
||||
base64 = "=0.22.0"
|
||||
cbc = { version = "=0.1.2", features = ["alloc"] }
|
||||
napi = { version = "=2.13.3", features = ["async"] }
|
||||
napi-derive = "=2.13.0"
|
||||
napi = { version = "=2.16.0", features = ["async"] }
|
||||
napi-derive = "=2.16.0"
|
||||
rand = "=0.8.5"
|
||||
retry = "=2.0.0"
|
||||
scopeguard = "=1.2.0"
|
||||
@@ -28,7 +28,7 @@ thiserror = "=1.0.51"
|
||||
typenum = "=1.17.0"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "=2.0.1"
|
||||
napi-build = "=2.1.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
widestring = "=1.0.2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@bitwarden/desktop",
|
||||
"description": "A secure and free password manager for all of your devices.",
|
||||
"version": "2024.3.1",
|
||||
"version": "2024.3.2",
|
||||
"keywords": [
|
||||
"bitwarden",
|
||||
"password",
|
||||
|
||||
@@ -42,7 +42,6 @@ export class SettingsComponent implements OnInit {
|
||||
themeOptions: any[];
|
||||
clearClipboardOptions: any[];
|
||||
supportsBiometric: boolean;
|
||||
additionalBiometricSettingsText: string;
|
||||
showAlwaysShowDock = false;
|
||||
requireEnableTray = false;
|
||||
showDuckDuckGoIntegrationOption = false;
|
||||
@@ -283,10 +282,6 @@ export class SettingsComponent implements OnInit {
|
||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.additionalBiometricSettingsText =
|
||||
this.biometricText === "unlockWithTouchId"
|
||||
? "additionalTouchIdSettings"
|
||||
: "additionalWindowsHelloSettings";
|
||||
this.previousVaultTimeout = this.form.value.vaultTimeout;
|
||||
|
||||
this.refreshTimeoutSettings$
|
||||
@@ -700,4 +695,15 @@ export class SettingsComponent implements OnInit {
|
||||
throw new Error("Unsupported platform");
|
||||
}
|
||||
}
|
||||
|
||||
get additionalBiometricSettingsText() {
|
||||
switch (this.platformUtilsService.getDevice()) {
|
||||
case DeviceType.MacOsDesktop:
|
||||
return "additionalTouchIdSettings";
|
||||
case DeviceType.WindowsDesktop:
|
||||
return "additionalWindowsHelloSettings";
|
||||
default:
|
||||
throw new Error("Unsupported platform");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@@ -147,7 +147,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private modalService: ModalService,
|
||||
private keyConnectorService: KeyConnectorService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private dialogService: DialogService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private stateEventRunnerService: StateEventRunnerService,
|
||||
@@ -265,7 +265,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.updateAppMenu();
|
||||
this.configService.triggerServerConfigFetch();
|
||||
await this.configService.ensureConfigFetched();
|
||||
}
|
||||
break;
|
||||
case "openSettings":
|
||||
@@ -584,7 +584,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
await this.passwordGenerationService.clear(userBeingLoggedOut);
|
||||
await this.vaultTimeoutSettingsService.clear(userBeingLoggedOut);
|
||||
await this.policyService.clear(userBeingLoggedOut);
|
||||
await this.keyConnectorService.clear();
|
||||
await this.biometricStateService.logout(userBeingLoggedOut as UserId);
|
||||
await this.providerService.save(null, userBeingLoggedOut as UserId);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
@@ -91,6 +92,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
private tokenService: TokenService,
|
||||
private environmentService: EnvironmentService,
|
||||
private loginEmailService: LoginEmailServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
@@ -137,7 +139,10 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
|
||||
async addAccount() {
|
||||
this.close();
|
||||
await this.stateService.setRememberedEmail(null);
|
||||
|
||||
this.loginEmailService.setRememberEmail(false);
|
||||
await this.loginEmailService.saveEmailSettings();
|
||||
|
||||
await this.router.navigate(["/login"]);
|
||||
await this.stateService.setActiveUser(null);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/services/config/config.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
||||
@@ -36,7 +35,6 @@ export class InitService {
|
||||
private nativeMessagingService: NativeMessagingService,
|
||||
private themingService: AbstractThemingService,
|
||||
private encryptService: EncryptService,
|
||||
private configService: ConfigService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {}
|
||||
|
||||
@@ -55,23 +53,9 @@ export class InitService {
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("os_" + this.platformUtilsService.getDeviceString());
|
||||
this.themingService.applyThemeChangesTo(this.document);
|
||||
let installAction = null;
|
||||
const installedVersion = await this.stateService.getInstalledVersion();
|
||||
const currentVersion = await this.platformUtilsService.getApplicationVersion();
|
||||
if (installedVersion == null) {
|
||||
installAction = "install";
|
||||
} else if (installedVersion !== currentVersion) {
|
||||
installAction = "update";
|
||||
}
|
||||
|
||||
if (installAction != null) {
|
||||
await this.stateService.setInstalledVersion(currentVersion);
|
||||
}
|
||||
|
||||
const containerService = new ContainerService(this.cryptoService, this.encryptService);
|
||||
containerService.attachToGlobal(this.win);
|
||||
|
||||
this.configService.init();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { APP_INITIALIZER, InjectionToken, NgModule } from "@angular/core";
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
|
||||
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
|
||||
import {
|
||||
SECURE_STORAGE,
|
||||
STATE_FACTORY,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
LOCALES_DIRECTORY,
|
||||
SYSTEM_LANGUAGE,
|
||||
@@ -12,15 +12,15 @@ import {
|
||||
WINDOW,
|
||||
SUPPORTS_SECURE_STORAGE,
|
||||
SYSTEM_THEME_OBSERVABLE,
|
||||
SafeInjectionToken,
|
||||
STATE_FACTORY,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
@@ -77,60 +77,87 @@ import { DesktopFileDownloadService } from "./desktop-file-download.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { RendererCryptoFunctionService } from "./renderer-crypto-function.service";
|
||||
|
||||
const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
providers: [
|
||||
InitService,
|
||||
NativeMessagingService,
|
||||
SearchBarService,
|
||||
LoginGuard,
|
||||
DialogService,
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
// Desktop has its own Account definition which must be used in its StateService
|
||||
const DESKTOP_STATE_FACTORY = new SafeInjectionToken<StateFactory<GlobalState, Account>>(
|
||||
"DESKTOP_STATE_FACTORY",
|
||||
);
|
||||
|
||||
/**
|
||||
* Provider definitions used in the ngModule.
|
||||
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
|
||||
* If you need help please ask for it, do NOT change the type of this array.
|
||||
*/
|
||||
const safeProviders: SafeProvider[] = [
|
||||
safeProvider(InitService),
|
||||
safeProvider(NativeMessagingService),
|
||||
safeProvider(SearchBarService),
|
||||
safeProvider(LoginGuard),
|
||||
safeProvider(DialogService),
|
||||
safeProvider({
|
||||
provide: APP_INITIALIZER as SafeInjectionToken<() => void>,
|
||||
useFactory: (initService: InitService) => initService.init(),
|
||||
deps: [InitService],
|
||||
multi: true,
|
||||
},
|
||||
{
|
||||
provide: STATE_FACTORY,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DESKTOP_STATE_FACTORY,
|
||||
useValue: new StateFactory(GlobalState, Account),
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: STATE_FACTORY,
|
||||
useValue: null,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: RELOAD_CALLBACK,
|
||||
useValue: null,
|
||||
},
|
||||
{ provide: LogServiceAbstraction, useClass: ElectronLogRendererService, deps: [] },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: LogServiceAbstraction,
|
||||
useClass: ElectronLogRendererService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: PlatformUtilsServiceAbstraction,
|
||||
useClass: ElectronPlatformUtilsService,
|
||||
deps: [I18nServiceAbstraction, MessagingServiceAbstraction],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
||||
// the TokenService having to inject the PlatformUtilsService which introduces a
|
||||
// circular dependency on Desktop only.
|
||||
provide: SUPPORTS_SECURE_STORAGE,
|
||||
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: I18nServiceAbstraction,
|
||||
useClass: I18nRendererService,
|
||||
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY, GlobalStateProvider],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: MessagingServiceAbstraction,
|
||||
useClass: ElectronRendererMessagingService,
|
||||
deps: [BroadcasterServiceAbstraction],
|
||||
},
|
||||
{ provide: AbstractStorageService, useClass: ElectronRendererStorageService },
|
||||
{ provide: SECURE_STORAGE, useClass: ElectronRendererSecureStorageService },
|
||||
{ provide: MEMORY_STORAGE, useClass: MemoryStorageService },
|
||||
{ provide: OBSERVABLE_MEMORY_STORAGE, useClass: MemoryStorageServiceForStateProviders },
|
||||
{ provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService },
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AbstractStorageService,
|
||||
useClass: ElectronRendererStorageService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SECURE_STORAGE,
|
||||
useClass: ElectronRendererSecureStorageService,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({ provide: MEMORY_STORAGE, useClass: MemoryStorageService, deps: [] }),
|
||||
safeProvider({
|
||||
provide: OBSERVABLE_MEMORY_STORAGE,
|
||||
useClass: MemoryStorageServiceForStateProviders,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({ provide: OBSERVABLE_DISK_STORAGE, useExisting: AbstractStorageService }),
|
||||
safeProvider({
|
||||
provide: SystemServiceAbstraction,
|
||||
useClass: SystemService,
|
||||
deps: [
|
||||
@@ -142,8 +169,8 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
VaultTimeoutSettingsService,
|
||||
BiometricStateService,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: ElectronStateService,
|
||||
deps: [
|
||||
@@ -151,23 +178,25 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
SECURE_STORAGE,
|
||||
MEMORY_STORAGE,
|
||||
LogService,
|
||||
STATE_FACTORY,
|
||||
DESKTOP_STATE_FACTORY,
|
||||
AccountServiceAbstraction,
|
||||
EnvironmentService,
|
||||
TokenService,
|
||||
MigrationRunner,
|
||||
STATE_SERVICE_USE_CACHE,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: FileDownloadService,
|
||||
useClass: DesktopFileDownloadService,
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SYSTEM_THEME_OBSERVABLE,
|
||||
useFactory: () => fromIpcSystemTheme(),
|
||||
},
|
||||
{
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: EncryptedMessageHandlerService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
@@ -177,8 +206,8 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
MessagingServiceAbstraction,
|
||||
PasswordGenerationServiceAbstraction,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: NativeMessageHandlerService,
|
||||
deps: [
|
||||
StateServiceAbstraction,
|
||||
@@ -187,19 +216,15 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
MessagingServiceAbstraction,
|
||||
EncryptedMessageHandlerService,
|
||||
DialogService,
|
||||
DesktopAutofillSettingsService,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: LoginServiceAbstraction,
|
||||
useClass: LoginService,
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoFunctionServiceAbstraction,
|
||||
useClass: RendererCryptoFunctionService,
|
||||
deps: [WINDOW],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: ElectronCryptoService,
|
||||
deps: [
|
||||
@@ -213,17 +238,21 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
StateProvider,
|
||||
BiometricStateService,
|
||||
],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DesktopSettingsService,
|
||||
useClass: DesktopSettingsService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
{
|
||||
}),
|
||||
safeProvider({
|
||||
provide: DesktopAutofillSettingsService,
|
||||
useClass: DesktopAutofillSettingsService,
|
||||
deps: [StateProvider],
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [JslibServicesModule],
|
||||
declarations: [],
|
||||
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
||||
providers: safeProviders,
|
||||
})
|
||||
export class ServicesModule {}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
|
||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/tools/export/components/export.component";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
@@ -12,6 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
|
||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/vault-export-ui";
|
||||
|
||||
@Component({
|
||||
selector: "app-export",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user