mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-14 10:26:19 +01:00
Merge branch 'main' into auth/pm-4816/create-shared-LoginApprovalComponent
This commit is contained in:
commit
3cb7ce4597
20
.github/renovate.json
vendored
20
.github/renovate.json
vendored
@ -73,7 +73,7 @@
|
|||||||
"reviewers": ["team:team-admin-console-dev"]
|
"reviewers": ["team:team-admin-console-dev"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious", "regedit"],
|
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious"],
|
||||||
"description": "Auth owned dependencies",
|
"description": "Auth owned dependencies",
|
||||||
"commitMessagePrefix": "[deps] Auth:",
|
"commitMessagePrefix": "[deps] Auth:",
|
||||||
"reviewers": ["team:team-auth-dev"]
|
"reviewers": ["team:team-auth-dev"]
|
||||||
@ -110,6 +110,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matchPackageNames": [
|
"matchPackageNames": [
|
||||||
|
"@electron/notarize",
|
||||||
|
"@electron/rebuild",
|
||||||
"@types/argon2-browser",
|
"@types/argon2-browser",
|
||||||
"@types/chrome",
|
"@types/chrome",
|
||||||
"@types/firefox-webext-browser",
|
"@types/firefox-webext-browser",
|
||||||
@ -119,6 +121,12 @@
|
|||||||
"argon2",
|
"argon2",
|
||||||
"argon2-browser",
|
"argon2-browser",
|
||||||
"big-integer",
|
"big-integer",
|
||||||
|
"electron-builder",
|
||||||
|
"electron-log",
|
||||||
|
"electron-reload",
|
||||||
|
"electron-store",
|
||||||
|
"electron-updater",
|
||||||
|
"electron",
|
||||||
"node-forge",
|
"node-forge",
|
||||||
"rxjs",
|
"rxjs",
|
||||||
"type-fest",
|
"type-fest",
|
||||||
@ -197,19 +205,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"matchPackageNames": [
|
"matchPackageNames": [
|
||||||
"@electron/notarize",
|
|
||||||
"@electron/rebuild",
|
|
||||||
"@microsoft/signalr-protocol-msgpack",
|
"@microsoft/signalr-protocol-msgpack",
|
||||||
"@microsoft/signalr",
|
"@microsoft/signalr",
|
||||||
"@types/jsdom",
|
"@types/jsdom",
|
||||||
"@types/papaparse",
|
"@types/papaparse",
|
||||||
"@types/zxcvbn",
|
"@types/zxcvbn",
|
||||||
"electron-builder",
|
|
||||||
"electron-log",
|
|
||||||
"electron-reload",
|
|
||||||
"electron-store",
|
|
||||||
"electron-updater",
|
|
||||||
"electron",
|
|
||||||
"jsdom",
|
"jsdom",
|
||||||
"jszip",
|
"jszip",
|
||||||
"oidc-client-ts",
|
"oidc-client-ts",
|
||||||
@ -258,5 +258,5 @@
|
|||||||
"reviewers": ["team:team-vault-dev"]
|
"reviewers": ["team:team-vault-dev"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm", "regedit"]
|
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm"]
|
||||||
}
|
}
|
||||||
|
44
.github/workflows/build-browser.yml
vendored
44
.github/workflows/build-browser.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
name: Build Browser
|
name: Build Browser
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'cf-pages'
|
- 'cf-pages'
|
||||||
@ -33,6 +34,10 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-run:
|
||||||
|
name: Check PR run
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
name: Setup
|
name: Setup
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@ -41,8 +46,10 @@ jobs:
|
|||||||
adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }}
|
adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }}
|
||||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Get Package Version
|
- name: Get Package Version
|
||||||
id: gen_vars
|
id: gen_vars
|
||||||
@ -71,8 +78,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/browser
|
working-directory: apps/browser
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Testing locales - extName length
|
- name: Testing locales - extName length
|
||||||
run: |
|
run: |
|
||||||
@ -109,8 +118,10 @@ jobs:
|
|||||||
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
|
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
|
||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -163,10 +174,6 @@ jobs:
|
|||||||
run: npm run dist:mv3
|
run: npm run dist:mv3
|
||||||
working-directory: browser-source/apps/browser
|
working-directory: browser-source/apps/browser
|
||||||
|
|
||||||
- name: Build Chrome Manifest v3 Beta
|
|
||||||
run: npm run dist:chrome:beta
|
|
||||||
working-directory: browser-source/apps/browser
|
|
||||||
|
|
||||||
- name: Upload Opera artifact
|
- name: Upload Opera artifact
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
@ -188,13 +195,6 @@ jobs:
|
|||||||
path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload Chrome MV3 Beta artifact (DO NOT USE FOR PROD)
|
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
|
||||||
with:
|
|
||||||
name: DO-NOT-USE-FOR-PROD-dist-chrome-MV3-beta-${{ env._BUILD_NUMBER }}.zip
|
|
||||||
path: browser-source/apps/browser/dist/dist-chrome-mv3-beta.zip
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Upload Firefox artifact
|
- name: Upload Firefox artifact
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
@ -236,12 +236,15 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- setup
|
- setup
|
||||||
- locales-test
|
- locales-test
|
||||||
|
- check-run
|
||||||
env:
|
env:
|
||||||
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
|
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
|
||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -353,8 +356,10 @@ jobs:
|
|||||||
- build
|
- build
|
||||||
- build-safari
|
- build-safari
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -392,7 +397,10 @@ jobs:
|
|||||||
- crowdin-push
|
- crowdin-push
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure')
|
if: |
|
||||||
|
github.event_name != 'pull_request_target'
|
||||||
|
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||||
|
&& contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
|
30
.github/workflows/build-cli.yml
vendored
30
.github/workflows/build-cli.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
name: Build CLI
|
name: Build CLI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'cf-pages'
|
- 'cf-pages'
|
||||||
@ -34,6 +35,10 @@ defaults:
|
|||||||
working-directory: apps/cli
|
working-directory: apps/cli
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-run:
|
||||||
|
name: Check PR run
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
name: Setup
|
name: Setup
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@ -41,8 +46,10 @@ jobs:
|
|||||||
package_version: ${{ steps.retrieve-package-version.outputs.package_version }}
|
package_version: ${{ steps.retrieve-package-version.outputs.package_version }}
|
||||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Get Package Version
|
- name: Get Package Version
|
||||||
id: retrieve-package-version
|
id: retrieve-package-version
|
||||||
@ -58,7 +65,6 @@ jobs:
|
|||||||
NODE_VERSION=${NODE_NVMRC/v/''}
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
||||||
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
cli:
|
cli:
|
||||||
name: "${{ matrix.os.base }} - ${{ matrix.license_type.readable }}"
|
name: "${{ matrix.os.base }} - ${{ matrix.license_type.readable }}"
|
||||||
strategy:
|
strategy:
|
||||||
@ -82,8 +88,10 @@ jobs:
|
|||||||
_WIN_PKG_FETCH_VERSION: 20.11.1
|
_WIN_PKG_FETCH_VERSION: 20.11.1
|
||||||
_WIN_PKG_VERSION: 3.5
|
_WIN_PKG_VERSION: 3.5
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Setup Unix Vars
|
- name: Setup Unix Vars
|
||||||
run: |
|
run: |
|
||||||
@ -160,8 +168,10 @@ jobs:
|
|||||||
_WIN_PKG_FETCH_VERSION: 20.11.1
|
_WIN_PKG_FETCH_VERSION: 20.11.1
|
||||||
_WIN_PKG_VERSION: 3.5
|
_WIN_PKG_VERSION: 3.5
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Setup Windows builder
|
- name: Setup Windows builder
|
||||||
run: |
|
run: |
|
||||||
@ -310,8 +320,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
@ -386,10 +398,14 @@ jobs:
|
|||||||
- cli
|
- cli
|
||||||
- cli-windows
|
- cli-windows
|
||||||
- snap
|
- snap
|
||||||
|
- check-run
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure')
|
if: |
|
||||||
|
github.event_name != 'pull_request_target'
|
||||||
|
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||||
|
&& contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
|
56
.github/workflows/build-desktop.yml
vendored
56
.github/workflows/build-desktop.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
name: Build Desktop
|
name: Build Desktop
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'cf-pages'
|
- 'cf-pages'
|
||||||
@ -32,12 +33,18 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-run:
|
||||||
|
name: Check PR run
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||||
|
|
||||||
electron-verify:
|
electron-verify:
|
||||||
name: Verify Electron Version
|
name: Verify Electron Version
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Verify
|
- name: Verify
|
||||||
run: |
|
run: |
|
||||||
@ -65,8 +72,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Get Package Version
|
- name: Get Package Version
|
||||||
id: retrieve-version
|
id: retrieve-version
|
||||||
@ -138,8 +147,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -238,7 +249,9 @@ jobs:
|
|||||||
windows:
|
windows:
|
||||||
name: Windows Build
|
name: Windows Build
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
needs: setup
|
needs:
|
||||||
|
- setup
|
||||||
|
- check-run
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
@ -248,8 +261,10 @@ jobs:
|
|||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
NODE_OPTIONS: --max_old_space_size=4096
|
NODE_OPTIONS: --max_old_space_size=4096
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -447,7 +462,9 @@ jobs:
|
|||||||
macos-build:
|
macos-build:
|
||||||
name: MacOS Build
|
name: MacOS Build
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
needs: setup
|
needs:
|
||||||
|
- setup
|
||||||
|
- check-run
|
||||||
env:
|
env:
|
||||||
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
|
||||||
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
|
||||||
@ -456,8 +473,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -622,8 +641,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -841,8 +862,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -1088,8 +1111,10 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
working-directory: apps/desktop
|
working-directory: apps/desktop
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -1279,8 +1304,10 @@ jobs:
|
|||||||
- macos-package-mas
|
- macos-package-mas
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -1323,7 +1350,10 @@ jobs:
|
|||||||
- crowdin-push
|
- crowdin-push
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure')
|
if: |
|
||||||
|
github.event_name != 'pull_request_target'
|
||||||
|
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||||
|
&& contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
|
39
.github/workflows/build-web.yml
vendored
39
.github/workflows/build-web.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
name: Build Web
|
name: Build Web
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
|
types: [opened, synchronize]
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'l10n_master'
|
- 'l10n_master'
|
||||||
- 'cf-pages'
|
- 'cf-pages'
|
||||||
@ -36,6 +37,10 @@ env:
|
|||||||
_AZ_REGISTRY: bitwardenprod.azurecr.io
|
_AZ_REGISTRY: bitwardenprod.azurecr.io
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
check-run:
|
||||||
|
name: Check PR run
|
||||||
|
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
name: Setup
|
name: Setup
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@ -43,8 +48,10 @@ jobs:
|
|||||||
version: ${{ steps.version.outputs.value }}
|
version: ${{ steps.version.outputs.value }}
|
||||||
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Get GitHub sha as version
|
- name: Get GitHub sha as version
|
||||||
id: version
|
id: version
|
||||||
@ -89,8 +96,10 @@ jobs:
|
|||||||
git_metadata: true
|
git_metadata: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Set up Node
|
- name: Set up Node
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
@ -142,6 +151,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- setup
|
- setup
|
||||||
- build-artifacts
|
- build-artifacts
|
||||||
|
- check-run
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -155,8 +165,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
_VERSION: ${{ needs.setup.outputs.version }}
|
_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Check Branch to Publish
|
- name: Check Branch to Publish
|
||||||
env:
|
env:
|
||||||
@ -250,11 +262,15 @@ jobs:
|
|||||||
crowdin-push:
|
crowdin-push:
|
||||||
name: Crowdin Push
|
name: Crowdin Push
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
needs: build-artifacts
|
needs:
|
||||||
|
- build-artifacts
|
||||||
|
- check-run
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
- name: Login to Azure
|
- name: Login to Azure
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -282,9 +298,11 @@ jobs:
|
|||||||
|
|
||||||
trigger-web-vault-deploy:
|
trigger-web-vault-deploy:
|
||||||
name: Trigger web vault deploy
|
name: Trigger web vault deploy
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: build-artifacts
|
needs:
|
||||||
|
- build-artifacts
|
||||||
|
- check-run
|
||||||
steps:
|
steps:
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Login to Azure - CI Subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -326,7 +344,10 @@ jobs:
|
|||||||
- trigger-web-vault-deploy
|
- trigger-web-vault-deploy
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure')
|
if: |
|
||||||
|
github.event_name != 'pull_request_target'
|
||||||
|
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||||
|
&& contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
- name: Login to Azure - Prod Subscription
|
||||||
|
@ -1,124 +1,130 @@
|
|||||||
name: Version Bump
|
name: Repository management
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
|
task:
|
||||||
|
default: "Version Bump"
|
||||||
|
description: "Task to execute"
|
||||||
|
options:
|
||||||
|
- "Version Bump"
|
||||||
|
- "Version Bump and Cut rc"
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
bump_browser:
|
bump_browser:
|
||||||
description: "Bump Browser?"
|
description: "Bump Browser version?"
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
bump_cli:
|
bump_cli:
|
||||||
description: "Bump CLI?"
|
description: "Bump CLI version?"
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
bump_desktop:
|
bump_desktop:
|
||||||
description: "Bump Desktop?"
|
description: "Bump Desktop version?"
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
bump_web:
|
bump_web:
|
||||||
description: "Bump Web?"
|
description: "Bump Web version?"
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
target_ref:
|
||||||
|
default: "main"
|
||||||
|
description: "Branch/Tag to target for cut"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
version_number_override:
|
version_number_override:
|
||||||
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
|
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
cut_rc_branch:
|
|
||||||
description: "Cut RC branch?"
|
|
||||||
default: true
|
|
||||||
type: boolean
|
|
||||||
enable_slack_notification:
|
|
||||||
description: "Enable Slack notifications for upcoming release?"
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
setup:
|
||||||
|
name: Setup
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
outputs:
|
||||||
|
branch: ${{ steps.set-branch.outputs.branch }}
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
steps:
|
||||||
|
- name: Set branch
|
||||||
|
id: set-branch
|
||||||
|
env:
|
||||||
|
TASK: ${{ inputs.task }}
|
||||||
|
run: |
|
||||||
|
if [[ "$TASK" == "Version Bump" ]]; then
|
||||||
|
BRANCH="none"
|
||||||
|
elif [[ "$TASK" == "Version Bump and Cut rc" ]]; then
|
||||||
|
BRANCH="rc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Generate GH App token
|
||||||
|
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||||
|
private-key: ${{ secrets.BW_GHAPP_KEY }}
|
||||||
|
|
||||||
|
|
||||||
|
cut_branch:
|
||||||
|
name: Cut branch
|
||||||
|
if: ${{ needs.setup.outputs.branch == 'rc' }}
|
||||||
|
needs: setup
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Check out target ref
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.target_ref }}
|
||||||
|
token: ${{ needs.setup.outputs.token }}
|
||||||
|
|
||||||
|
- name: Check if ${{ needs.setup.outputs.branch }} branch exists
|
||||||
|
env:
|
||||||
|
BRANCH_NAME: ${{ needs.setup.outputs.branch }}
|
||||||
|
run: |
|
||||||
|
if [[ $(git ls-remote --heads origin $BRANCH_NAME) ]]; then
|
||||||
|
echo "$BRANCH_NAME already exists! Please delete $BRANCH_NAME before running again." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Cut branch
|
||||||
|
env:
|
||||||
|
BRANCH_NAME: ${{ needs.setup.outputs.branch }}
|
||||||
|
run: |
|
||||||
|
git switch --quiet --create $BRANCH_NAME
|
||||||
|
git push --quiet --set-upstream origin $BRANCH_NAME
|
||||||
|
|
||||||
|
|
||||||
bump_version:
|
bump_version:
|
||||||
name: Bump Version
|
name: Bump Version
|
||||||
runs-on: ubuntu-22.04
|
if: ${{ always() }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs:
|
||||||
|
- cut_branch
|
||||||
|
- setup
|
||||||
outputs:
|
outputs:
|
||||||
version_browser: ${{ steps.set-final-version-output.outputs.version_browser }}
|
version_browser: ${{ steps.set-final-version-output.outputs.version_browser }}
|
||||||
version_cli: ${{ steps.set-final-version-output.outputs.version_cli }}
|
version_cli: ${{ steps.set-final-version-output.outputs.version_cli }}
|
||||||
version_desktop: ${{ steps.set-final-version-output.outputs.version_desktop }}
|
version_desktop: ${{ steps.set-final-version-output.outputs.version_desktop }}
|
||||||
version_web: ${{ steps.set-final-version-output.outputs.version_web }}
|
version_web: ${{ steps.set-final-version-output.outputs.version_web }}
|
||||||
steps:
|
steps:
|
||||||
- name: Validate version input
|
- name: Validate version input format
|
||||||
if: ${{ inputs.version_number_override != '' }}
|
if: ${{ inputs.version_number_override != '' }}
|
||||||
uses: bitwarden/gh-actions/version-check@main
|
uses: bitwarden/gh-actions/version-check@main
|
||||||
with:
|
with:
|
||||||
version: ${{ inputs.version_number_override }}
|
version: ${{ inputs.version_number_override }}
|
||||||
|
|
||||||
- name: Slack Notification Check
|
- name: Check out branch
|
||||||
run: |
|
|
||||||
if [[ "${{ inputs.enable_slack_notification }}" == true ]]; then
|
|
||||||
echo "Slack notifications enabled."
|
|
||||||
else
|
|
||||||
echo "Slack notifications disabled."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Checkout Branch
|
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
token: ${{ needs.setup.outputs.token }}
|
||||||
|
|
||||||
- name: Check if RC branch exists
|
- name: Configure Git
|
||||||
if: ${{ inputs.cut_rc_branch == true }}
|
|
||||||
run: |
|
run: |
|
||||||
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
|
git config --local user.email "actions@github.com"
|
||||||
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
|
git config --local user.name "Github Actions"
|
||||||
echo "Remote RC branch exists."
|
|
||||||
echo "Please delete current RC branch before running again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Retrieve secrets
|
|
||||||
id: retrieve-secrets
|
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-ci"
|
|
||||||
secrets: "github-gpg-private-key,
|
|
||||||
github-gpg-private-key-passphrase"
|
|
||||||
|
|
||||||
- name: Import GPG key
|
|
||||||
uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 # v6.2.0
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
|
|
||||||
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
|
|
||||||
git_user_signingkey: true
|
|
||||||
git_commit_gpgsign: true
|
|
||||||
|
|
||||||
- name: Setup git
|
|
||||||
run: |
|
|
||||||
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
|
|
||||||
git config --local user.name "bitwarden-devops-bot"
|
|
||||||
|
|
||||||
- name: Create Version Branch
|
|
||||||
id: create-branch
|
|
||||||
run: |
|
|
||||||
CLIENTS=()
|
|
||||||
if [[ ${{ inputs.bump_browser }} == true ]]; then
|
|
||||||
CLIENTS+=("browser")
|
|
||||||
fi
|
|
||||||
if [[ ${{ inputs.bump_cli }} == true ]]; then
|
|
||||||
CLIENTS+=("cli")
|
|
||||||
fi
|
|
||||||
if [[ ${{ inputs.bump_desktop }} == true ]]; then
|
|
||||||
CLIENTS+=("desktop")
|
|
||||||
fi
|
|
||||||
if [[ ${{ inputs.bump_web }} == true ]]; then
|
|
||||||
CLIENTS+=("web")
|
|
||||||
fi
|
|
||||||
printf -v joined '%s,' "${CLIENTS[@]}"
|
|
||||||
echo "client=${joined%,}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
NAME=version_bump_${{ github.ref_name }}_$(date +"%Y-%m-%d")
|
|
||||||
git switch -c $NAME
|
|
||||||
echo "name=$NAME" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# VERSION BUMP SECTION #
|
# VERSION BUMP SECTION #
|
||||||
@ -165,7 +171,9 @@ jobs:
|
|||||||
- name: Bump Browser Version - Version Override
|
- name: Bump Browser Version - Version Override
|
||||||
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
|
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
|
||||||
id: bump-browser-version-override
|
id: bump-browser-version-override
|
||||||
run: npm version --workspace=@bitwarden/browser ${{ inputs.version_number_override }}
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
|
run: npm version --workspace=@bitwarden/browser $VERSION
|
||||||
|
|
||||||
- name: Bump Browser Version - Automatic Calculation
|
- name: Bump Browser Version - Automatic Calculation
|
||||||
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
|
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
|
||||||
@ -250,7 +258,9 @@ jobs:
|
|||||||
- name: Bump CLI Version - Version Override
|
- name: Bump CLI Version - Version Override
|
||||||
if: ${{ inputs.bump_cli == true && inputs.version_number_override != '' }}
|
if: ${{ inputs.bump_cli == true && inputs.version_number_override != '' }}
|
||||||
id: bump-cli-version-override
|
id: bump-cli-version-override
|
||||||
run: npm version --workspace=@bitwarden/cli ${{ inputs.version_number_override }}
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
|
run: npm version --workspace=@bitwarden/cli $VERSION
|
||||||
|
|
||||||
- name: Bump CLI Version - Automatic Calculation
|
- name: Bump CLI Version - Automatic Calculation
|
||||||
if: ${{ inputs.bump_cli == true && inputs.version_number_override == '' }}
|
if: ${{ inputs.bump_cli == true && inputs.version_number_override == '' }}
|
||||||
@ -300,7 +310,9 @@ jobs:
|
|||||||
- name: Bump Desktop Version - Root - Version Override
|
- name: Bump Desktop Version - Root - Version Override
|
||||||
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
|
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
|
||||||
id: bump-desktop-version-override
|
id: bump-desktop-version-override
|
||||||
run: npm version --workspace=@bitwarden/desktop ${{ inputs.version_number_override }}
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
|
run: npm version --workspace=@bitwarden/desktop $VERSION
|
||||||
|
|
||||||
- name: Bump Desktop Version - Root - Automatic Calculation
|
- name: Bump Desktop Version - Root - Automatic Calculation
|
||||||
if: ${{ inputs.bump_desktop == true && inputs.version_number_override == '' }}
|
if: ${{ inputs.bump_desktop == true && inputs.version_number_override == '' }}
|
||||||
@ -311,7 +323,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Bump Desktop Version - App - Version Override
|
- name: Bump Desktop Version - App - Version Override
|
||||||
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
|
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
|
||||||
run: npm version ${{ inputs.version_number_override }}
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
|
run: npm version $VERSION
|
||||||
working-directory: "apps/desktop/src"
|
working-directory: "apps/desktop/src"
|
||||||
|
|
||||||
- name: Bump Desktop Version - App - Automatic Calculation
|
- name: Bump Desktop Version - App - Automatic Calculation
|
||||||
@ -362,7 +376,9 @@ jobs:
|
|||||||
- name: Bump Web Version - Version Override
|
- name: Bump Web Version - Version Override
|
||||||
if: ${{ inputs.bump_web == true && inputs.version_number_override != '' }}
|
if: ${{ inputs.bump_web == true && inputs.version_number_override != '' }}
|
||||||
id: bump-web-version-override
|
id: bump-web-version-override
|
||||||
run: npm version --workspace=@bitwarden/web-vault ${{ inputs.version_number_override }}
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
|
run: npm version --workspace=@bitwarden/web-vault $VERSION
|
||||||
|
|
||||||
- name: Bump Web Version - Automatic Calculation
|
- name: Bump Web Version - Automatic Calculation
|
||||||
if: ${{ inputs.bump_web == true && inputs.version_number_override == '' }}
|
if: ${{ inputs.bump_web == true && inputs.version_number_override == '' }}
|
||||||
@ -375,27 +391,29 @@ jobs:
|
|||||||
|
|
||||||
- name: Set final version output
|
- name: Set final version output
|
||||||
id: set-final-version-output
|
id: set-final-version-output
|
||||||
|
env:
|
||||||
|
VERSION: ${{ inputs.version_number_override }}
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ steps.bump-browser-version-override.outcome }}" = "success" ]]; then
|
if [[ "${{ steps.bump-browser-version-override.outcome }}" = "success" ]]; then
|
||||||
echo "version_browser=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
echo "version_browser=$VERSION" >> $GITHUB_OUTPUT
|
||||||
elif [[ "${{ steps.bump-browser-version-automatic.outcome }}" = "success" ]]; then
|
elif [[ "${{ steps.bump-browser-version-automatic.outcome }}" = "success" ]]; then
|
||||||
echo "version_browser=${{ steps.calculate-next-browser-version.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "version_browser=${{ steps.calculate-next-browser-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${{ steps.bump-cli-version-override.outcome }}" = "success" ]]; then
|
if [[ "${{ steps.bump-cli-version-override.outcome }}" = "success" ]]; then
|
||||||
echo "version_cli=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
echo "version_cli=$VERSION" >> $GITHUB_OUTPUT
|
||||||
elif [[ "${{ steps.bump-cli-version-automatic.outcome }}" = "success" ]]; then
|
elif [[ "${{ steps.bump-cli-version-automatic.outcome }}" = "success" ]]; then
|
||||||
echo "version_cli=${{ steps.calculate-next-cli-version.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "version_cli=${{ steps.calculate-next-cli-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${{ steps.bump-desktop-version-override.outcome }}" = "success" ]]; then
|
if [[ "${{ steps.bump-desktop-version-override.outcome }}" = "success" ]]; then
|
||||||
echo "version_desktop=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
echo "version_desktop=$VERSION" >> $GITHUB_OUTPUT
|
||||||
elif [[ "${{ steps.bump-desktop-version-automatic.outcome }}" = "success" ]]; then
|
elif [[ "${{ steps.bump-desktop-version-automatic.outcome }}" = "success" ]]; then
|
||||||
echo "version_desktop=${{ steps.calculate-next-desktop-version.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "version_desktop=${{ steps.calculate-next-desktop-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${{ steps.bump-web-version-override.outcome }}" = "success" ]]; then
|
if [[ "${{ steps.bump-web-version-override.outcome }}" = "success" ]]; then
|
||||||
echo "version_web=${{ inputs.version_number_override }}" >> $GITHUB_OUTPUT
|
echo "version_web=$VERSION" >> $GITHUB_OUTPUT
|
||||||
elif [[ "${{ steps.bump-web-version-automatic.outcome }}" = "success" ]]; then
|
elif [[ "${{ steps.bump-web-version-automatic.outcome }}" = "success" ]]; then
|
||||||
echo "version_web=${{ steps.calculate-next-web-version.outputs.version }}" >> $GITHUB_OUTPUT
|
echo "version_web=${{ steps.calculate-next-web-version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
@ -416,199 +434,52 @@ jobs:
|
|||||||
|
|
||||||
- name: Push changes
|
- name: Push changes
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
||||||
env:
|
run: git push
|
||||||
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
|
||||||
run: git push -u origin $PR_BRANCH
|
|
||||||
|
|
||||||
- name: Generate PR message
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
|
||||||
id: pr-message
|
|
||||||
run: |
|
|
||||||
MESSAGE=""
|
|
||||||
if [[ "${{ inputs.bump_browser }}" == "true" ]]; then
|
|
||||||
MESSAGE+=$' Browser version bump to ${{ steps.set-final-version-output.outputs.version_browser }}\n'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${{ inputs.bump_cli }}" == "true" ]]; then
|
cherry_pick:
|
||||||
MESSAGE+=$' CLI version bump to ${{ steps.set-final-version-output.outputs.version_cli }}\n'
|
name: Cherry-Pick Commit(s)
|
||||||
fi
|
if: ${{ needs.setup.outputs.branch == 'rc' }}
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
if [[ "${{ inputs.bump_desktop }}" == "true" ]]; then
|
needs:
|
||||||
MESSAGE+=$' Desktop version bump to ${{ steps.set-final-version-output.outputs.version_desktop }}\n'
|
- bump_version
|
||||||
fi
|
- setup
|
||||||
|
|
||||||
if [[ "${{ inputs.bump_web }}" == "true" ]]; then
|
|
||||||
MESSAGE+=$' Web version bump to ${{ steps.set-final-version-output.outputs.version_web }}\n'
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "MESSAGE<<EOF" >> $GITHUB_ENV
|
|
||||||
echo "$MESSAGE" >> $GITHUB_ENV
|
|
||||||
echo "EOF" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Generate GH App token
|
|
||||||
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
|
||||||
id: app-token
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.BW_GHAPP_ID }}
|
|
||||||
private-key: ${{ secrets.BW_GHAPP_KEY }}
|
|
||||||
owner: ${{ github.repository_owner }}
|
|
||||||
|
|
||||||
- name: Create Version PR
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
|
||||||
id: create-pr
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
||||||
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
|
|
||||||
TITLE: "Bump client(s) version"
|
|
||||||
run: |
|
|
||||||
PR_URL=$(gh pr create --title "$TITLE" \
|
|
||||||
--base "main" \
|
|
||||||
--head "$PR_BRANCH" \
|
|
||||||
--label "version update" \
|
|
||||||
--label "automated pr" \
|
|
||||||
--body "
|
|
||||||
## Type of change
|
|
||||||
- [ ] Bug fix
|
|
||||||
- [ ] New feature development
|
|
||||||
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
|
|
||||||
- [ ] Build/deploy pipeline (DevOps)
|
|
||||||
- [X] Other
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
$MESSAGE")
|
|
||||||
|
|
||||||
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Approve PR
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
|
|
||||||
run: gh pr review $PR_NUMBER --approve
|
|
||||||
|
|
||||||
- name: Merge PR
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
||||||
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
|
|
||||||
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
|
|
||||||
|
|
||||||
- name: Report upcoming browser release version to Slack
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && steps.set-final-version-output.outputs.version_browser != '' && inputs.enable_slack_notification == true }}
|
|
||||||
uses: bitwarden/gh-actions/report-upcoming-release-version@main
|
|
||||||
with:
|
|
||||||
version: ${{ steps.set-final-version-output.outputs.version_browser }}
|
|
||||||
project: browser
|
|
||||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Report upcoming cli release version to Slack
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && steps.set-final-version-output.outputs.version_cli != '' && inputs.enable_slack_notification == true }}
|
|
||||||
uses: bitwarden/gh-actions/report-upcoming-release-version@main
|
|
||||||
with:
|
|
||||||
version: ${{ steps.set-final-version-output.outputs.version_cli }}
|
|
||||||
project: cli
|
|
||||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Report upcoming desktop release version to Slack
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && steps.set-final-version-output.outputs.version_desktop != '' && inputs.enable_slack_notification == true }}
|
|
||||||
uses: bitwarden/gh-actions/report-upcoming-release-version@main
|
|
||||||
with:
|
|
||||||
version: ${{ steps.set-final-version-output.outputs.version_desktop }}
|
|
||||||
project: desktop
|
|
||||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Report upcoming web release version to Slack
|
|
||||||
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' && steps.set-final-version-output.outputs.version_web != '' && inputs.enable_slack_notification == true }}
|
|
||||||
uses: bitwarden/gh-actions/report-upcoming-release-version@main
|
|
||||||
with:
|
|
||||||
version: ${{ steps.set-final-version-output.outputs.version_web }}
|
|
||||||
project: web
|
|
||||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
cut_rc:
|
|
||||||
name: Cut RC branch
|
|
||||||
if: ${{ inputs.cut_rc_branch == true }}
|
|
||||||
needs: bump_version
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Branch
|
- name: Check out main branch
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
token: ${{ needs.setup.outputs.token }}
|
||||||
|
|
||||||
### Browser
|
- name: Configure Git
|
||||||
- name: Browser - Verify version has been updated
|
|
||||||
if: ${{ inputs.bump_browser == true }}
|
|
||||||
env:
|
|
||||||
NEW_VERSION: ${{ needs.bump_version.outputs.version_browser }}
|
|
||||||
run: |
|
run: |
|
||||||
# Wait for version to change.
|
git config --local user.email "actions@github.com"
|
||||||
while : ; do
|
git config --local user.name "Github Actions"
|
||||||
echo "Waiting for version to be updated..."
|
|
||||||
git pull --force
|
|
||||||
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
|
||||||
|
|
||||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
- name: Perform cherry-pick(s)
|
||||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
working-directory: apps/browser
|
|
||||||
|
|
||||||
### CLI
|
|
||||||
- name: CLI - Verify version has been updated
|
|
||||||
if: ${{ inputs.bump_cli == true }}
|
|
||||||
env:
|
|
||||||
NEW_VERSION: ${{ needs.bump_version.outputs.version_cli }}
|
|
||||||
run: |
|
run: |
|
||||||
# Wait for version to change.
|
# Function for cherry-picking
|
||||||
while : ; do
|
cherry_pick () {
|
||||||
echo "Waiting for version to be updated..."
|
local package_path="apps/$1/package.json"
|
||||||
git pull --force
|
local source_branch=$2
|
||||||
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
local destination_branch=$3
|
||||||
|
|
||||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
# Get project commit/version from source branch
|
||||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
git switch $source_branch
|
||||||
sleep 10
|
SOURCE_COMMIT=$(git log --reverse --pretty=format:"%H" --max-count=1 $package_path)
|
||||||
done
|
SOURCE_VERSION=$(cat $package_path | jq -r '.version')
|
||||||
working-directory: apps/cli
|
|
||||||
|
|
||||||
### Desktop
|
# Get project commit/version from destination branch
|
||||||
- name: Desktop - Verify version has been updated
|
git switch $destination_branch
|
||||||
if: ${{ inputs.bump_desktop == true }}
|
DESTINATION_VERSION=$(cat $package_path | jq -r '.version')
|
||||||
env:
|
|
||||||
NEW_VERSION: ${{ needs.bump_version.outputs.version_desktop }}
|
|
||||||
run: |
|
|
||||||
# Wait for version to change.
|
|
||||||
while : ; do
|
|
||||||
echo "Waiting for version to be updated..."
|
|
||||||
git pull --force
|
|
||||||
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
|
||||||
|
|
||||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
if [[ "$DESTINATION_VERSION" != "$SOURCE_VERSION" ]]; then
|
||||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
git cherry-pick --strategy-option=theirs -x $SOURCE_COMMIT
|
||||||
sleep 10
|
git push -u origin $destination_branch
|
||||||
done
|
fi
|
||||||
working-directory: apps/desktop
|
|
||||||
|
|
||||||
### Web
|
# Cherry-pick from 'main' into 'rc'
|
||||||
- name: Web - Verify version has been updated
|
cherry_pick browser main rc
|
||||||
if: ${{ inputs.bump_web == true }}
|
cherry_pick cli main rc
|
||||||
env:
|
cherry_pick desktop main rc
|
||||||
NEW_VERSION: ${{ needs.bump_version.outputs.version_web }}
|
cherry_pick web main rc
|
||||||
run: |
|
|
||||||
# Wait for version to change.
|
|
||||||
while : ; do
|
|
||||||
echo "Waiting for version to be updated..."
|
|
||||||
git pull --force
|
|
||||||
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
|
||||||
|
|
||||||
# If the versions don't match we continue the loop, otherwise we break out of the loop.
|
|
||||||
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
working-directory: apps/web
|
|
||||||
|
|
||||||
- name: Cut RC branch
|
|
||||||
run: |
|
|
||||||
git switch --quiet --create rc
|
|
||||||
git push --quiet --set-upstream origin rc
|
|
64
.github/workflows/version-auto-bump.yml
vendored
64
.github/workflows/version-auto-bump.yml
vendored
@ -8,27 +8,55 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
bump-version:
|
bump-version:
|
||||||
name: Bump Desktop Version
|
name: Bump Desktop Version
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Login to Azure - CI Subscription
|
- name: Generate GH App token
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
|
||||||
|
id: app-token
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
app-id: ${{ secrets.BW_GHAPP_ID }}
|
||||||
|
private-key: ${{ secrets.BW_GHAPP_KEY }}
|
||||||
|
|
||||||
- name: Retrieve bot secrets
|
- name: Check out target ref
|
||||||
id: retrieve-bot-secrets
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@main
|
|
||||||
with:
|
with:
|
||||||
keyvault: bitwarden-ci
|
ref: main
|
||||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
- name: Trigger Version Bump workflow
|
- name: Configure Git
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
|
|
||||||
run: |
|
run: |
|
||||||
echo '{"cut_rc_branch": "false",
|
git config --local user.email "actions@github.com"
|
||||||
"bump_browser": "false",
|
git config --local user.name "Github Actions"
|
||||||
"bump_cli": "false",
|
|
||||||
"bump_desktop": "true",
|
- name: Get current Desktop version
|
||||||
"bump_web": "false"}' | \
|
id: current-desktop-version
|
||||||
gh workflow run version-bump.yml --json --repo bitwarden/clients
|
run: |
|
||||||
|
CURRENT_VERSION=$(cat package.json | jq -r '.version')
|
||||||
|
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
working-directory: apps/desktop
|
||||||
|
|
||||||
|
- name: Calculate next Desktop release version
|
||||||
|
id: calculate-next-desktop-version
|
||||||
|
uses: bitwarden/gh-actions/version-next@main
|
||||||
|
with:
|
||||||
|
version: ${{ steps.current-desktop-version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Bump Desktop Version - Root - Automatic Calculation
|
||||||
|
id: bump-desktop-version-automatic
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
|
||||||
|
run: npm version --workspace=@bitwarden/desktop $VERSION
|
||||||
|
|
||||||
|
- name: Bump Desktop Version - App - Automatic Calculation
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
|
||||||
|
run: npm version $VERSION
|
||||||
|
working-directory: "apps/desktop/src"
|
||||||
|
|
||||||
|
- name: Commit files
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
|
||||||
|
run: git commit -m "Bumped Desktop client to $VERSION" -a
|
||||||
|
|
||||||
|
- name: Push changes
|
||||||
|
run: git push
|
||||||
|
@ -128,10 +128,10 @@
|
|||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"browserTarget": "test-storybook:build:production"
|
"buildTarget": "test-storybook:build:production"
|
||||||
},
|
},
|
||||||
"development": {
|
"development": {
|
||||||
"browserTarget": "test-storybook:build:development"
|
"buildTarget": "test-storybook:build:development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
|
@ -9,7 +9,6 @@ const replace = require("gulp-replace");
|
|||||||
|
|
||||||
const manifest = require("./src/manifest.json");
|
const manifest = require("./src/manifest.json");
|
||||||
const manifestVersion = parseInt(process.env.MANIFEST_VERSION || manifest.version);
|
const manifestVersion = parseInt(process.env.MANIFEST_VERSION || manifest.version);
|
||||||
const betaBuild = process.env.BETA_BUILD === "1";
|
|
||||||
|
|
||||||
const paths = {
|
const paths = {
|
||||||
build: "./build/",
|
build: "./build/",
|
||||||
@ -17,27 +16,11 @@ const paths = {
|
|||||||
safari: "./src/safari/",
|
safari: "./src/safari/",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a number to a tuple containing two Uint16's
|
|
||||||
* @param num {number} This number is expected to be a integer style number with no decimals
|
|
||||||
*
|
|
||||||
* @returns {number[]} A tuple containing two elements that are both numbers.
|
|
||||||
*/
|
|
||||||
function numToUint16s(num) {
|
|
||||||
var arr = new ArrayBuffer(4);
|
|
||||||
var view = new DataView(arr);
|
|
||||||
view.setUint32(0, num, false);
|
|
||||||
return [view.getUint16(0), view.getUint16(2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildString() {
|
function buildString() {
|
||||||
var build = "";
|
var build = "";
|
||||||
if (process.env.MANIFEST_VERSION) {
|
if (process.env.MANIFEST_VERSION) {
|
||||||
build = `-mv${process.env.MANIFEST_VERSION}`;
|
build = `-mv${process.env.MANIFEST_VERSION}`;
|
||||||
}
|
}
|
||||||
if (betaBuild) {
|
|
||||||
build += "-beta";
|
|
||||||
}
|
|
||||||
if (process.env.BUILD_NUMBER && process.env.BUILD_NUMBER !== "") {
|
if (process.env.BUILD_NUMBER && process.env.BUILD_NUMBER !== "") {
|
||||||
build = `-${process.env.BUILD_NUMBER}`;
|
build = `-${process.env.BUILD_NUMBER}`;
|
||||||
}
|
}
|
||||||
@ -71,9 +54,6 @@ function distFirefox() {
|
|||||||
manifest.optional_permissions = manifest.optional_permissions.filter(
|
manifest.optional_permissions = manifest.optional_permissions.filter(
|
||||||
(permission) => permission !== "privacy",
|
(permission) => permission !== "privacy",
|
||||||
);
|
);
|
||||||
if (betaBuild) {
|
|
||||||
manifest = applyBetaLabels(manifest);
|
|
||||||
}
|
|
||||||
return manifest;
|
return manifest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -90,9 +70,6 @@ function distOpera() {
|
|||||||
delete manifest.commands._execute_sidebar_action;
|
delete manifest.commands._execute_sidebar_action;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (betaBuild) {
|
|
||||||
manifest = applyBetaLabels(manifest);
|
|
||||||
}
|
|
||||||
return manifest;
|
return manifest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -102,9 +79,6 @@ function distChrome() {
|
|||||||
delete manifest.applications;
|
delete manifest.applications;
|
||||||
delete manifest.sidebar_action;
|
delete manifest.sidebar_action;
|
||||||
delete manifest.commands._execute_sidebar_action;
|
delete manifest.commands._execute_sidebar_action;
|
||||||
if (betaBuild) {
|
|
||||||
manifest = applyBetaLabels(manifest);
|
|
||||||
}
|
|
||||||
return manifest;
|
return manifest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -114,9 +88,6 @@ function distEdge() {
|
|||||||
delete manifest.applications;
|
delete manifest.applications;
|
||||||
delete manifest.sidebar_action;
|
delete manifest.sidebar_action;
|
||||||
delete manifest.commands._execute_sidebar_action;
|
delete manifest.commands._execute_sidebar_action;
|
||||||
if (betaBuild) {
|
|
||||||
manifest = applyBetaLabels(manifest);
|
|
||||||
}
|
|
||||||
return manifest;
|
return manifest;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -237,9 +208,6 @@ async function safariCopyBuild(source, dest) {
|
|||||||
delete manifest.commands._execute_sidebar_action;
|
delete manifest.commands._execute_sidebar_action;
|
||||||
delete manifest.optional_permissions;
|
delete manifest.optional_permissions;
|
||||||
manifest.permissions.push("nativeMessaging");
|
manifest.permissions.push("nativeMessaging");
|
||||||
if (betaBuild) {
|
|
||||||
manifest = applyBetaLabels(manifest);
|
|
||||||
}
|
|
||||||
return manifest;
|
return manifest;
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -254,30 +222,6 @@ function stdOutProc(proc) {
|
|||||||
proc.stderr.on("data", (data) => console.error(data.toString()));
|
proc.stderr.on("data", (data) => console.error(data.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyBetaLabels(manifest) {
|
|
||||||
manifest.name = "Bitwarden Password Manager BETA";
|
|
||||||
manifest.short_name = "Bitwarden BETA";
|
|
||||||
manifest.description = "THIS EXTENSION IS FOR BETA TESTING BITWARDEN.";
|
|
||||||
if (process.env.GITHUB_RUN_ID) {
|
|
||||||
const existingVersionParts = manifest.version.split("."); // 3 parts expected 2024.4.0
|
|
||||||
|
|
||||||
// GITHUB_RUN_ID is a number like: 8853654662
|
|
||||||
// which will convert to [ 4024, 3206 ]
|
|
||||||
// and a single incremented id of 8853654663 will become [ 4024, 3207 ]
|
|
||||||
const runIdParts = numToUint16s(parseInt(process.env.GITHUB_RUN_ID));
|
|
||||||
|
|
||||||
// Only use the first 2 parts from the given version number and base the other 2 numbers from the GITHUB_RUN_ID
|
|
||||||
// Example: 2024.4.4024.3206
|
|
||||||
const betaVersion = `${existingVersionParts[0]}.${existingVersionParts[1]}.${runIdParts[0]}.${runIdParts[1]}`;
|
|
||||||
|
|
||||||
manifest.version_name = `${betaVersion} beta - ${process.env.GITHUB_SHA.slice(0, 8)}`;
|
|
||||||
manifest.version = betaVersion;
|
|
||||||
} else {
|
|
||||||
manifest.version = `${manifest.version}.0`;
|
|
||||||
}
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
exports["dist:firefox"] = distFirefox;
|
exports["dist:firefox"] = distFirefox;
|
||||||
exports["dist:chrome"] = distChrome;
|
exports["dist:chrome"] = distChrome;
|
||||||
exports["dist:opera"] = distOpera;
|
exports["dist:opera"] = distOpera;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/browser",
|
"name": "@bitwarden/browser",
|
||||||
"version": "2024.10.1",
|
"version": "2024.11.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env MANIFEST_VERSION=3 webpack",
|
"build": "cross-env MANIFEST_VERSION=3 webpack",
|
||||||
"build:mv2": "webpack",
|
"build:mv2": "webpack",
|
||||||
@ -10,12 +10,9 @@
|
|||||||
"build:watch:safari": "cross-env MANIFEST_VERSION=3 BROWSER=safari webpack --watch",
|
"build:watch:safari": "cross-env MANIFEST_VERSION=3 BROWSER=safari webpack --watch",
|
||||||
"build:watch:mv2": "webpack --watch",
|
"build:watch:mv2": "webpack --watch",
|
||||||
"build:prod": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" webpack",
|
"build:prod": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" webpack",
|
||||||
"build:prod:beta": "cross-env BETA_BUILD=1 NODE_ENV=production webpack",
|
|
||||||
"build:prod:watch": "cross-env NODE_ENV=production webpack --watch",
|
"build:prod:watch": "cross-env NODE_ENV=production webpack --watch",
|
||||||
"dist": "npm run build:prod && gulp dist",
|
"dist": "npm run build:prod && gulp dist",
|
||||||
"dist:beta": "npm run build:prod:beta && cross-env BETA_BUILD=1 gulp dist",
|
|
||||||
"dist:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && cross-env MANIFEST_VERSION=3 gulp dist",
|
"dist:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && cross-env MANIFEST_VERSION=3 gulp dist",
|
||||||
"dist:mv3:beta": "cross-env MANIFEST_VERSION=3 npm run build:prod:beta && cross-env MANIFEST_VERSION=3 BETA_BUILD=1 gulp dist",
|
|
||||||
"dist:chrome": "npm run build:prod && gulp dist:chrome",
|
"dist:chrome": "npm run build:prod && gulp dist:chrome",
|
||||||
"dist:chrome:beta": "cross-env MANIFEST_VERSION=3 npm run build:prod:beta && cross-env MANIFEST_VERSION=3 BETA_BUILD=1 gulp dist:chrome",
|
"dist:chrome:beta": "cross-env MANIFEST_VERSION=3 npm run build:prod:beta && cross-env MANIFEST_VERSION=3 BETA_BUILD=1 gulp dist:chrome",
|
||||||
"dist:firefox": "npm run build:prod && gulp dist:firefox",
|
"dist:firefox": "npm run build:prod && gulp dist:firefox",
|
||||||
|
@ -2878,7 +2878,7 @@
|
|||||||
"message": "Luo sähköpostiosoite"
|
"message": "Luo sähköpostiosoite"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Arvon tulee olla väliltä $MIN$ - $MAX$",
|
"message": "Arvon tulee olla väliltä $MIN$—$MAX$",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"message": "Bitwarden"
|
"message": "Bitwarden"
|
||||||
},
|
},
|
||||||
"extName": {
|
"extName": {
|
||||||
"message": "Bitwarden Password Manager",
|
"message": "Bitwarden passordbehandler",
|
||||||
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
|
"description": "Extension name, MUST be less than 40 characters (Safari restriction)"
|
||||||
},
|
},
|
||||||
"extDesc": {
|
"extDesc": {
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"message": "Use single sign-on"
|
"message": "Use single sign-on"
|
||||||
},
|
},
|
||||||
"welcomeBack": {
|
"welcomeBack": {
|
||||||
"message": "Welcome back"
|
"message": "Velkommen tilbake"
|
||||||
},
|
},
|
||||||
"setAStrongPassword": {
|
"setAStrongPassword": {
|
||||||
"message": "Set a strong password"
|
"message": "Set a strong password"
|
||||||
@ -168,7 +168,7 @@
|
|||||||
"message": "Copy notes"
|
"message": "Copy notes"
|
||||||
},
|
},
|
||||||
"fill": {
|
"fill": {
|
||||||
"message": "Fill",
|
"message": "Fyll",
|
||||||
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
||||||
},
|
},
|
||||||
"autoFill": {
|
"autoFill": {
|
||||||
@ -357,10 +357,10 @@
|
|||||||
"message": "Rediger mappen"
|
"message": "Rediger mappen"
|
||||||
},
|
},
|
||||||
"newFolder": {
|
"newFolder": {
|
||||||
"message": "New folder"
|
"message": "Ny mappe"
|
||||||
},
|
},
|
||||||
"folderName": {
|
"folderName": {
|
||||||
"message": "Folder name"
|
"message": "Mappenavn"
|
||||||
},
|
},
|
||||||
"folderHintText": {
|
"folderHintText": {
|
||||||
"message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums"
|
"message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums"
|
||||||
@ -458,7 +458,7 @@
|
|||||||
"description": "deprecated. Use specialCharactersLabel instead."
|
"description": "deprecated. Use specialCharactersLabel instead."
|
||||||
},
|
},
|
||||||
"include": {
|
"include": {
|
||||||
"message": "Include",
|
"message": "Inkluder",
|
||||||
"description": "Card header for password generator include block"
|
"description": "Card header for password generator include block"
|
||||||
},
|
},
|
||||||
"uppercaseDescription": {
|
"uppercaseDescription": {
|
||||||
@ -466,7 +466,7 @@
|
|||||||
"description": "Tooltip for the password generator uppercase character checkbox"
|
"description": "Tooltip for the password generator uppercase character checkbox"
|
||||||
},
|
},
|
||||||
"uppercaseLabel": {
|
"uppercaseLabel": {
|
||||||
"message": "A-Z",
|
"message": "A-Å",
|
||||||
"description": "Label for the password generator uppercase character checkbox"
|
"description": "Label for the password generator uppercase character checkbox"
|
||||||
},
|
},
|
||||||
"lowercaseDescription": {
|
"lowercaseDescription": {
|
||||||
@ -478,7 +478,7 @@
|
|||||||
"description": "Label for the password generator lowercase character checkbox"
|
"description": "Label for the password generator lowercase character checkbox"
|
||||||
},
|
},
|
||||||
"numbersDescription": {
|
"numbersDescription": {
|
||||||
"message": "Include numbers",
|
"message": "Inkluder tall",
|
||||||
"description": "Full description for the password generator numbers checkbox"
|
"description": "Full description for the password generator numbers checkbox"
|
||||||
},
|
},
|
||||||
"numbersLabel": {
|
"numbersLabel": {
|
||||||
@ -627,7 +627,7 @@
|
|||||||
"message": "Vault timeout"
|
"message": "Vault timeout"
|
||||||
},
|
},
|
||||||
"otherOptions": {
|
"otherOptions": {
|
||||||
"message": "Other options"
|
"message": "Andre valg"
|
||||||
},
|
},
|
||||||
"rateExtension": {
|
"rateExtension": {
|
||||||
"message": "Gi denne utvidelsen en vurdering"
|
"message": "Gi denne utvidelsen en vurdering"
|
||||||
@ -676,7 +676,7 @@
|
|||||||
"message": "Tidsavbrudd i hvelvet"
|
"message": "Tidsavbrudd i hvelvet"
|
||||||
},
|
},
|
||||||
"vaultTimeout1": {
|
"vaultTimeout1": {
|
||||||
"message": "Timeout"
|
"message": "Tidsavbrudd"
|
||||||
},
|
},
|
||||||
"lockNow": {
|
"lockNow": {
|
||||||
"message": "Lås nå"
|
"message": "Lås nå"
|
||||||
@ -730,10 +730,10 @@
|
|||||||
"message": "Sikkerhet"
|
"message": "Sikkerhet"
|
||||||
},
|
},
|
||||||
"confirmMasterPassword": {
|
"confirmMasterPassword": {
|
||||||
"message": "Confirm master password"
|
"message": "Bekreft hovedpassord"
|
||||||
},
|
},
|
||||||
"masterPassword": {
|
"masterPassword": {
|
||||||
"message": "Master password"
|
"message": "Hovedpassord"
|
||||||
},
|
},
|
||||||
"masterPassImportant": {
|
"masterPassImportant": {
|
||||||
"message": "Your master password cannot be recovered if you forget it!"
|
"message": "Your master password cannot be recovered if you forget it!"
|
||||||
@ -843,7 +843,7 @@
|
|||||||
"message": "Din innloggingsøkt har utløpt."
|
"message": "Din innloggingsøkt har utløpt."
|
||||||
},
|
},
|
||||||
"logIn": {
|
"logIn": {
|
||||||
"message": "Log in"
|
"message": "Logg inn"
|
||||||
},
|
},
|
||||||
"logInToBitwarden": {
|
"logInToBitwarden": {
|
||||||
"message": "Log in to Bitwarden"
|
"message": "Log in to Bitwarden"
|
||||||
@ -928,7 +928,7 @@
|
|||||||
"message": "Ny URI"
|
"message": "Ny URI"
|
||||||
},
|
},
|
||||||
"addDomain": {
|
"addDomain": {
|
||||||
"message": "Add domain",
|
"message": "Legg til domene",
|
||||||
"description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context."
|
"description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context."
|
||||||
},
|
},
|
||||||
"addedItem": {
|
"addedItem": {
|
||||||
@ -1092,7 +1092,7 @@
|
|||||||
"message": "This file export will be password protected and require the file password to decrypt."
|
"message": "This file export will be password protected and require the file password to decrypt."
|
||||||
},
|
},
|
||||||
"filePassword": {
|
"filePassword": {
|
||||||
"message": "File password"
|
"message": "Filpassord"
|
||||||
},
|
},
|
||||||
"exportPasswordDescription": {
|
"exportPasswordDescription": {
|
||||||
"message": "This password will be used to export and import this file"
|
"message": "This password will be used to export and import this file"
|
||||||
@ -1917,10 +1917,10 @@
|
|||||||
"message": "Det er ingen passord å liste opp."
|
"message": "Det er ingen passord å liste opp."
|
||||||
},
|
},
|
||||||
"clearHistory": {
|
"clearHistory": {
|
||||||
"message": "Clear history"
|
"message": "Tøm historikk"
|
||||||
},
|
},
|
||||||
"nothingToShow": {
|
"nothingToShow": {
|
||||||
"message": "Nothing to show"
|
"message": "Ingenting å vise"
|
||||||
},
|
},
|
||||||
"nothingGeneratedRecently": {
|
"nothingGeneratedRecently": {
|
||||||
"message": "You haven't generated anything recently"
|
"message": "You haven't generated anything recently"
|
||||||
@ -1984,10 +1984,10 @@
|
|||||||
"message": "Lås opp med PIN-kode"
|
"message": "Lås opp med PIN-kode"
|
||||||
},
|
},
|
||||||
"setYourPinTitle": {
|
"setYourPinTitle": {
|
||||||
"message": "Set PIN"
|
"message": "Velg PIN"
|
||||||
},
|
},
|
||||||
"setYourPinButton": {
|
"setYourPinButton": {
|
||||||
"message": "Set PIN"
|
"message": "Velg PIN"
|
||||||
},
|
},
|
||||||
"setYourPinCode": {
|
"setYourPinCode": {
|
||||||
"message": "Angi PIN-koden din for å låse opp Bitwarden. PIN-innstillingene tilbakestilles hvis du logger deg helt ut av programmet."
|
"message": "Angi PIN-koden din for å låse opp Bitwarden. PIN-innstillingene tilbakestilles hvis du logger deg helt ut av programmet."
|
||||||
@ -2041,7 +2041,7 @@
|
|||||||
"message": "Username generator"
|
"message": "Username generator"
|
||||||
},
|
},
|
||||||
"useThisPassword": {
|
"useThisPassword": {
|
||||||
"message": "Use this password"
|
"message": "Bruk dette passordet"
|
||||||
},
|
},
|
||||||
"useThisUsername": {
|
"useThisUsername": {
|
||||||
"message": "Use this username"
|
"message": "Use this username"
|
||||||
@ -2186,7 +2186,7 @@
|
|||||||
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
|
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
|
||||||
},
|
},
|
||||||
"unsubscribe": {
|
"unsubscribe": {
|
||||||
"message": "Unsubscribe"
|
"message": "Avslutt abonnement"
|
||||||
},
|
},
|
||||||
"atAnyTime": {
|
"atAnyTime": {
|
||||||
"message": "at any time."
|
"message": "at any time."
|
||||||
@ -2195,7 +2195,7 @@
|
|||||||
"message": "By continuing, you agree to the"
|
"message": "By continuing, you agree to the"
|
||||||
},
|
},
|
||||||
"and": {
|
"and": {
|
||||||
"message": "and"
|
"message": "og"
|
||||||
},
|
},
|
||||||
"acceptPolicies": {
|
"acceptPolicies": {
|
||||||
"message": "Ved å merke av denne boksen sier du deg enig i følgende:"
|
"message": "Ved å merke av denne boksen sier du deg enig i følgende:"
|
||||||
@ -2315,7 +2315,7 @@
|
|||||||
"message": "An organization policy has blocked importing items into your individual vault."
|
"message": "An organization policy has blocked importing items into your individual vault."
|
||||||
},
|
},
|
||||||
"domainsTitle": {
|
"domainsTitle": {
|
||||||
"message": "Domains",
|
"message": "Domener",
|
||||||
"description": "A category title describing the concept of web domains"
|
"description": "A category title describing the concept of web domains"
|
||||||
},
|
},
|
||||||
"excludedDomains": {
|
"excludedDomains": {
|
||||||
@ -2411,7 +2411,7 @@
|
|||||||
"message": "Passord beskyttet"
|
"message": "Passord beskyttet"
|
||||||
},
|
},
|
||||||
"copyLink": {
|
"copyLink": {
|
||||||
"message": "Copy link"
|
"message": "Kopier lenke"
|
||||||
},
|
},
|
||||||
"copySendLink": {
|
"copySendLink": {
|
||||||
"message": "Kopier Send-lenke",
|
"message": "Kopier Send-lenke",
|
||||||
@ -2668,7 +2668,7 @@
|
|||||||
"message": "E-postbekreftelse kreves"
|
"message": "E-postbekreftelse kreves"
|
||||||
},
|
},
|
||||||
"emailVerifiedV2": {
|
"emailVerifiedV2": {
|
||||||
"message": "Email verified"
|
"message": "E-post bekreftet"
|
||||||
},
|
},
|
||||||
"emailVerificationRequiredDesc": {
|
"emailVerificationRequiredDesc": {
|
||||||
"message": "Du må bekrefte e-posten din for å bruke denne funksjonen. Du kan bekrefte e-postadressen din i netthvelvet."
|
"message": "Du må bekrefte e-posten din for å bruke denne funksjonen. Du kan bekrefte e-postadressen din i netthvelvet."
|
||||||
@ -3247,10 +3247,10 @@
|
|||||||
"message": "Uncheck if using a public device"
|
"message": "Uncheck if using a public device"
|
||||||
},
|
},
|
||||||
"approveFromYourOtherDevice": {
|
"approveFromYourOtherDevice": {
|
||||||
"message": "Approve from your other device"
|
"message": "Godkjenn fra en av dine andre enheter"
|
||||||
},
|
},
|
||||||
"requestAdminApproval": {
|
"requestAdminApproval": {
|
||||||
"message": "Request admin approval"
|
"message": "Be om administratorgodkjennelse"
|
||||||
},
|
},
|
||||||
"approveWithMasterPassword": {
|
"approveWithMasterPassword": {
|
||||||
"message": "Godkjenn med hovedpassord"
|
"message": "Godkjenn med hovedpassord"
|
||||||
@ -3274,7 +3274,7 @@
|
|||||||
"message": "No email?"
|
"message": "No email?"
|
||||||
},
|
},
|
||||||
"goBack": {
|
"goBack": {
|
||||||
"message": "Go back"
|
"message": "Gå tilbake"
|
||||||
},
|
},
|
||||||
"toEditYourEmailAddress": {
|
"toEditYourEmailAddress": {
|
||||||
"message": "to edit your email address."
|
"message": "to edit your email address."
|
||||||
@ -3290,7 +3290,7 @@
|
|||||||
"message": "Generelt"
|
"message": "Generelt"
|
||||||
},
|
},
|
||||||
"display": {
|
"display": {
|
||||||
"message": "Display"
|
"message": "Vis"
|
||||||
},
|
},
|
||||||
"accountSuccessfullyCreated": {
|
"accountSuccessfullyCreated": {
|
||||||
"message": "Account successfully created!"
|
"message": "Account successfully created!"
|
||||||
@ -3416,7 +3416,7 @@
|
|||||||
"message": "— Skriv for å filtrere —"
|
"message": "— Skriv for å filtrere —"
|
||||||
},
|
},
|
||||||
"multiSelectLoading": {
|
"multiSelectLoading": {
|
||||||
"message": "Retrieving options..."
|
"message": "Innhenter alternativer..."
|
||||||
},
|
},
|
||||||
"multiSelectNotFound": {
|
"multiSelectNotFound": {
|
||||||
"message": "Ingen gjenstander funnet"
|
"message": "Ingen gjenstander funnet"
|
||||||
@ -3542,7 +3542,7 @@
|
|||||||
"description": "Screen reader text (aria-label) for new item button in overlay"
|
"description": "Screen reader text (aria-label) for new item button in overlay"
|
||||||
},
|
},
|
||||||
"newLogin": {
|
"newLogin": {
|
||||||
"message": "New login",
|
"message": "Ny innlogging",
|
||||||
"description": "Button text to display within inline menu when there are no matching items on a login field"
|
"description": "Button text to display within inline menu when there are no matching items on a login field"
|
||||||
},
|
},
|
||||||
"addNewLoginItemAria": {
|
"addNewLoginItemAria": {
|
||||||
@ -3558,7 +3558,7 @@
|
|||||||
"description": "Screen reader text (aria-label) for new card button within inline menu"
|
"description": "Screen reader text (aria-label) for new card button within inline menu"
|
||||||
},
|
},
|
||||||
"newIdentity": {
|
"newIdentity": {
|
||||||
"message": "New identity",
|
"message": "Ny identitet",
|
||||||
"description": "Button text to display within inline menu when there are no matching items on an identity field"
|
"description": "Button text to display within inline menu when there are no matching items on an identity field"
|
||||||
},
|
},
|
||||||
"addNewIdentityItemAria": {
|
"addNewIdentityItemAria": {
|
||||||
@ -3592,7 +3592,7 @@
|
|||||||
"message": "Beskrivelse"
|
"message": "Beskrivelse"
|
||||||
},
|
},
|
||||||
"importSuccess": {
|
"importSuccess": {
|
||||||
"message": "Data successfully imported"
|
"message": "Dataene ble vellykket importert"
|
||||||
},
|
},
|
||||||
"importSuccessNumberOfItems": {
|
"importSuccessNumberOfItems": {
|
||||||
"message": "$AMOUNT$ gjenstander totalt ble importert.",
|
"message": "$AMOUNT$ gjenstander totalt ble importert.",
|
||||||
@ -3682,7 +3682,7 @@
|
|||||||
"message": "Invalid file password, please use the password you entered when you created the export file."
|
"message": "Invalid file password, please use the password you entered when you created the export file."
|
||||||
},
|
},
|
||||||
"destination": {
|
"destination": {
|
||||||
"message": "Destination"
|
"message": "Destinasjon"
|
||||||
},
|
},
|
||||||
"learnAboutImportOptions": {
|
"learnAboutImportOptions": {
|
||||||
"message": "Lær mer om importalternativene dine"
|
"message": "Lær mer om importalternativene dine"
|
||||||
@ -3719,7 +3719,7 @@
|
|||||||
"message": "Ingen fil er valgt"
|
"message": "Ingen fil er valgt"
|
||||||
},
|
},
|
||||||
"orCopyPasteFileContents": {
|
"orCopyPasteFileContents": {
|
||||||
"message": "or copy/paste the import file contents"
|
"message": "eller kopier/lim inn importfilens innhold"
|
||||||
},
|
},
|
||||||
"instructionsFor": {
|
"instructionsFor": {
|
||||||
"message": "$NAME$-instruksjoner",
|
"message": "$NAME$-instruksjoner",
|
||||||
@ -3810,7 +3810,7 @@
|
|||||||
"message": "Multifaktorautentisering ble avbrutt"
|
"message": "Multifaktorautentisering ble avbrutt"
|
||||||
},
|
},
|
||||||
"noLastPassDataFound": {
|
"noLastPassDataFound": {
|
||||||
"message": "No LastPass data found"
|
"message": "Ingen LastPass-data ble funnet"
|
||||||
},
|
},
|
||||||
"incorrectUsernameOrPassword": {
|
"incorrectUsernameOrPassword": {
|
||||||
"message": "Feil brukernavn eller passord"
|
"message": "Feil brukernavn eller passord"
|
||||||
@ -3837,7 +3837,7 @@
|
|||||||
"message": "Importerer kontoen din…"
|
"message": "Importerer kontoen din…"
|
||||||
},
|
},
|
||||||
"lastPassMFARequired": {
|
"lastPassMFARequired": {
|
||||||
"message": "LastPass multifactor authentication required"
|
"message": "LastPass-multifaktorautentisering kreves"
|
||||||
},
|
},
|
||||||
"lastPassMFADesc": {
|
"lastPassMFADesc": {
|
||||||
"message": "Enter your one-time passcode from your authentication app"
|
"message": "Enter your one-time passcode from your authentication app"
|
||||||
@ -4003,7 +4003,7 @@
|
|||||||
"description": "Notification message for when saving credentials has failed."
|
"description": "Notification message for when saving credentials has failed."
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"message": "Success"
|
"message": "Suksess"
|
||||||
},
|
},
|
||||||
"removePasskey": {
|
"removePasskey": {
|
||||||
"message": "Remove passkey"
|
"message": "Remove passkey"
|
||||||
@ -4105,13 +4105,13 @@
|
|||||||
"message": "Admin Console"
|
"message": "Admin Console"
|
||||||
},
|
},
|
||||||
"accountSecurity": {
|
"accountSecurity": {
|
||||||
"message": "Account security"
|
"message": "Kontosikkerhet"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"message": "Notifications"
|
"message": "Varsler"
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
"message": "Appearance"
|
"message": "Utseende"
|
||||||
},
|
},
|
||||||
"errorAssigningTargetCollection": {
|
"errorAssigningTargetCollection": {
|
||||||
"message": "Error assigning target collection."
|
"message": "Error assigning target collection."
|
||||||
@ -4140,7 +4140,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"new": {
|
"new": {
|
||||||
"message": "New"
|
"message": "Ny"
|
||||||
},
|
},
|
||||||
"removeItem": {
|
"removeItem": {
|
||||||
"message": "Remove $NAME$",
|
"message": "Remove $NAME$",
|
||||||
@ -4174,17 +4174,17 @@
|
|||||||
"message": "Organization is deactivated"
|
"message": "Organization is deactivated"
|
||||||
},
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
"message": "Owner"
|
"message": "Eier"
|
||||||
},
|
},
|
||||||
"selfOwnershipLabel": {
|
"selfOwnershipLabel": {
|
||||||
"message": "You",
|
"message": "Du",
|
||||||
"description": "Used as a label to indicate that the user is the owner of an item."
|
"description": "Used as a label to indicate that the user is the owner of an item."
|
||||||
},
|
},
|
||||||
"contactYourOrgAdmin": {
|
"contactYourOrgAdmin": {
|
||||||
"message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance."
|
"message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance."
|
||||||
},
|
},
|
||||||
"additionalInformation": {
|
"additionalInformation": {
|
||||||
"message": "Additional information"
|
"message": "Tilleggsinformasjon"
|
||||||
},
|
},
|
||||||
"itemHistory": {
|
"itemHistory": {
|
||||||
"message": "Item history"
|
"message": "Item history"
|
||||||
@ -4196,13 +4196,13 @@
|
|||||||
"message": "Owner: You"
|
"message": "Owner: You"
|
||||||
},
|
},
|
||||||
"linked": {
|
"linked": {
|
||||||
"message": "Linked"
|
"message": "Tilknyttet"
|
||||||
},
|
},
|
||||||
"copySuccessful": {
|
"copySuccessful": {
|
||||||
"message": "Copy Successful"
|
"message": "Copy Successful"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"message": "Upload"
|
"message": "Last opp"
|
||||||
},
|
},
|
||||||
"addAttachment": {
|
"addAttachment": {
|
||||||
"message": "Add attachment"
|
"message": "Add attachment"
|
||||||
@ -4238,7 +4238,7 @@
|
|||||||
"message": "Free organizations cannot use attachments"
|
"message": "Free organizations cannot use attachments"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"message": "Filters"
|
"message": "Filtre"
|
||||||
},
|
},
|
||||||
"personalDetails": {
|
"personalDetails": {
|
||||||
"message": "Personal details"
|
"message": "Personal details"
|
||||||
@ -4247,7 +4247,7 @@
|
|||||||
"message": "Identification"
|
"message": "Identification"
|
||||||
},
|
},
|
||||||
"contactInfo": {
|
"contactInfo": {
|
||||||
"message": "Contact info"
|
"message": "Kontaktinfo"
|
||||||
},
|
},
|
||||||
"downloadAttachment": {
|
"downloadAttachment": {
|
||||||
"message": "Download - $ITEMNAME$",
|
"message": "Download - $ITEMNAME$",
|
||||||
@ -4263,7 +4263,7 @@
|
|||||||
"description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher."
|
"description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher."
|
||||||
},
|
},
|
||||||
"loginCredentials": {
|
"loginCredentials": {
|
||||||
"message": "Login credentials"
|
"message": "Legitimasjoner for innlogging"
|
||||||
},
|
},
|
||||||
"authenticatorKey": {
|
"authenticatorKey": {
|
||||||
"message": "Authenticator key"
|
"message": "Authenticator key"
|
||||||
@ -4288,10 +4288,10 @@
|
|||||||
"message": "Website added"
|
"message": "Website added"
|
||||||
},
|
},
|
||||||
"addWebsite": {
|
"addWebsite": {
|
||||||
"message": "Add website"
|
"message": "Legg til nettsted"
|
||||||
},
|
},
|
||||||
"deleteWebsite": {
|
"deleteWebsite": {
|
||||||
"message": "Delete website"
|
"message": "Slett nettsted"
|
||||||
},
|
},
|
||||||
"defaultLabel": {
|
"defaultLabel": {
|
||||||
"message": "Default ($VALUE$)",
|
"message": "Default ($VALUE$)",
|
||||||
@ -4343,16 +4343,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"enableAnimations": {
|
"enableAnimations": {
|
||||||
"message": "Enable animations"
|
"message": "Aktiver animasjoner"
|
||||||
},
|
},
|
||||||
"showAnimations": {
|
"showAnimations": {
|
||||||
"message": "Show animations"
|
"message": "Show animations"
|
||||||
},
|
},
|
||||||
"addAccount": {
|
"addAccount": {
|
||||||
"message": "Add account"
|
"message": "Legg til konto"
|
||||||
},
|
},
|
||||||
"loading": {
|
"loading": {
|
||||||
"message": "Loading"
|
"message": "Laster"
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"message": "Data"
|
"message": "Data"
|
||||||
@ -4370,7 +4370,7 @@
|
|||||||
"description": "ARIA label for the inline menu button that logs in with a passkey."
|
"description": "ARIA label for the inline menu button that logs in with a passkey."
|
||||||
},
|
},
|
||||||
"assign": {
|
"assign": {
|
||||||
"message": "Assign"
|
"message": "Knytt"
|
||||||
},
|
},
|
||||||
"bulkCollectionAssignmentDialogDescriptionSingular": {
|
"bulkCollectionAssignmentDialogDescriptionSingular": {
|
||||||
"message": "Only organization members with access to these collections will be able to see the item."
|
"message": "Only organization members with access to these collections will be able to see the item."
|
||||||
@ -4394,7 +4394,7 @@
|
|||||||
"message": "Add field"
|
"message": "Add field"
|
||||||
},
|
},
|
||||||
"add": {
|
"add": {
|
||||||
"message": "Add"
|
"message": "Legg til"
|
||||||
},
|
},
|
||||||
"fieldType": {
|
"fieldType": {
|
||||||
"message": "Field type"
|
"message": "Field type"
|
||||||
@ -4418,7 +4418,7 @@
|
|||||||
"message": "Enter the the field's html id, name, aria-label, or placeholder."
|
"message": "Enter the the field's html id, name, aria-label, or placeholder."
|
||||||
},
|
},
|
||||||
"editField": {
|
"editField": {
|
||||||
"message": "Edit field"
|
"message": "Rediger felt"
|
||||||
},
|
},
|
||||||
"editFieldLabel": {
|
"editFieldLabel": {
|
||||||
"message": "Edit $LABEL$",
|
"message": "Edit $LABEL$",
|
||||||
@ -4588,13 +4588,13 @@
|
|||||||
"message": "Show number of login autofill suggestions on extension icon"
|
"message": "Show number of login autofill suggestions on extension icon"
|
||||||
},
|
},
|
||||||
"systemDefault": {
|
"systemDefault": {
|
||||||
"message": "System default"
|
"message": "Systemforvalg"
|
||||||
},
|
},
|
||||||
"enterprisePolicyRequirementsApplied": {
|
"enterprisePolicyRequirementsApplied": {
|
||||||
"message": "Enterprise policy requirements have been applied to this setting"
|
"message": "Enterprise policy requirements have been applied to this setting"
|
||||||
},
|
},
|
||||||
"retry": {
|
"retry": {
|
||||||
"message": "Retry"
|
"message": "Prøv igjen"
|
||||||
},
|
},
|
||||||
"vaultCustomTimeoutMinimum": {
|
"vaultCustomTimeoutMinimum": {
|
||||||
"message": "Minimum custom timeout is 1 minute."
|
"message": "Minimum custom timeout is 1 minute."
|
||||||
@ -4624,16 +4624,16 @@
|
|||||||
"message": "Items that have been in trash more than 30 days will automatically be deleted"
|
"message": "Items that have been in trash more than 30 days will automatically be deleted"
|
||||||
},
|
},
|
||||||
"restore": {
|
"restore": {
|
||||||
"message": "Restore"
|
"message": "Gjenopprett"
|
||||||
},
|
},
|
||||||
"deleteForever": {
|
"deleteForever": {
|
||||||
"message": "Delete forever"
|
"message": "Slett for alltid"
|
||||||
},
|
},
|
||||||
"noEditPermissions": {
|
"noEditPermissions": {
|
||||||
"message": "You don't have permission to edit this item"
|
"message": "You don't have permission to edit this item"
|
||||||
},
|
},
|
||||||
"authenticating": {
|
"authenticating": {
|
||||||
"message": "Authenticating"
|
"message": "Autentiserer"
|
||||||
},
|
},
|
||||||
"fillGeneratedPassword": {
|
"fillGeneratedPassword": {
|
||||||
"message": "Fill generated password",
|
"message": "Fill generated password",
|
||||||
@ -4648,7 +4648,7 @@
|
|||||||
"description": "Confirmation message for saving a login to Bitwarden"
|
"description": "Confirmation message for saving a login to Bitwarden"
|
||||||
},
|
},
|
||||||
"spaceCharacterDescriptor": {
|
"spaceCharacterDescriptor": {
|
||||||
"message": "Space",
|
"message": "Mellomrom",
|
||||||
"description": "Represents the space key in screen reader content as a readable word"
|
"description": "Represents the space key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"tildeCharacterDescriptor": {
|
"tildeCharacterDescriptor": {
|
||||||
@ -4656,15 +4656,15 @@
|
|||||||
"description": "Represents the ~ key in screen reader content as a readable word"
|
"description": "Represents the ~ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"backtickCharacterDescriptor": {
|
"backtickCharacterDescriptor": {
|
||||||
"message": "Backtick",
|
"message": "Baklengs apostrof",
|
||||||
"description": "Represents the ` key in screen reader content as a readable word"
|
"description": "Represents the ` key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"exclamationCharacterDescriptor": {
|
"exclamationCharacterDescriptor": {
|
||||||
"message": "Exclamation mark",
|
"message": "Utropstegn",
|
||||||
"description": "Represents the ! key in screen reader content as a readable word"
|
"description": "Represents the ! key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"atSignCharacterDescriptor": {
|
"atSignCharacterDescriptor": {
|
||||||
"message": "At sign",
|
"message": "Alfakrøll",
|
||||||
"description": "Represents the @ key in screen reader content as a readable word"
|
"description": "Represents the @ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hashSignCharacterDescriptor": {
|
"hashSignCharacterDescriptor": {
|
||||||
@ -4672,7 +4672,7 @@
|
|||||||
"description": "Represents the # key in screen reader content as a readable word"
|
"description": "Represents the # key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"dollarSignCharacterDescriptor": {
|
"dollarSignCharacterDescriptor": {
|
||||||
"message": "Dollar sign",
|
"message": "Dollartegn",
|
||||||
"description": "Represents the $ key in screen reader content as a readable word"
|
"description": "Represents the $ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"percentSignCharacterDescriptor": {
|
"percentSignCharacterDescriptor": {
|
||||||
@ -4684,7 +4684,7 @@
|
|||||||
"description": "Represents the ^ key in screen reader content as a readable word"
|
"description": "Represents the ^ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"ampersandCharacterDescriptor": {
|
"ampersandCharacterDescriptor": {
|
||||||
"message": "Ampersand",
|
"message": "Prosenttegn",
|
||||||
"description": "Represents the & key in screen reader content as a readable word"
|
"description": "Represents the & key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"asteriskCharacterDescriptor": {
|
"asteriskCharacterDescriptor": {
|
||||||
@ -4700,7 +4700,7 @@
|
|||||||
"description": "Represents the ) key in screen reader content as a readable word"
|
"description": "Represents the ) key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hyphenCharacterDescriptor": {
|
"hyphenCharacterDescriptor": {
|
||||||
"message": "Underscore",
|
"message": "Understrek",
|
||||||
"description": "Represents the _ key in screen reader content as a readable word"
|
"description": "Represents the _ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"underscoreCharacterDescriptor": {
|
"underscoreCharacterDescriptor": {
|
||||||
@ -4740,11 +4740,11 @@
|
|||||||
"description": "Represents the back slash key in screen reader content as a readable word"
|
"description": "Represents the back slash key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"colonCharacterDescriptor": {
|
"colonCharacterDescriptor": {
|
||||||
"message": "Colon",
|
"message": "Kolon",
|
||||||
"description": "Represents the : key in screen reader content as a readable word"
|
"description": "Represents the : key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"semicolonCharacterDescriptor": {
|
"semicolonCharacterDescriptor": {
|
||||||
"message": "Semicolon",
|
"message": "Semikolon",
|
||||||
"description": "Represents the ; key in screen reader content as a readable word"
|
"description": "Represents the ; key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"doubleQuoteCharacterDescriptor": {
|
"doubleQuoteCharacterDescriptor": {
|
||||||
@ -4756,7 +4756,7 @@
|
|||||||
"description": "Represents the ' key in screen reader content as a readable word"
|
"description": "Represents the ' key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"lessThanCharacterDescriptor": {
|
"lessThanCharacterDescriptor": {
|
||||||
"message": "Less than",
|
"message": "Mindre enn",
|
||||||
"description": "Represents the < key in screen reader content as a readable word"
|
"description": "Represents the < key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"greaterThanCharacterDescriptor": {
|
"greaterThanCharacterDescriptor": {
|
||||||
@ -4764,7 +4764,7 @@
|
|||||||
"description": "Represents the > key in screen reader content as a readable word"
|
"description": "Represents the > key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"commaCharacterDescriptor": {
|
"commaCharacterDescriptor": {
|
||||||
"message": "Comma",
|
"message": "Komma",
|
||||||
"description": "Represents the , key in screen reader content as a readable word"
|
"description": "Represents the , key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"periodCharacterDescriptor": {
|
"periodCharacterDescriptor": {
|
||||||
@ -4772,7 +4772,7 @@
|
|||||||
"description": "Represents the . key in screen reader content as a readable word"
|
"description": "Represents the . key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"questionCharacterDescriptor": {
|
"questionCharacterDescriptor": {
|
||||||
"message": "Question mark",
|
"message": "Spørsmålstegn",
|
||||||
"description": "Represents the ? key in screen reader content as a readable word"
|
"description": "Represents the ? key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"forwardSlashCharacterDescriptor": {
|
"forwardSlashCharacterDescriptor": {
|
||||||
@ -4780,10 +4780,10 @@
|
|||||||
"description": "Represents the / key in screen reader content as a readable word"
|
"description": "Represents the / key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"lowercaseAriaLabel": {
|
"lowercaseAriaLabel": {
|
||||||
"message": "Lowercase"
|
"message": "Små bokstaver"
|
||||||
},
|
},
|
||||||
"uppercaseAriaLabel": {
|
"uppercaseAriaLabel": {
|
||||||
"message": "Uppercase"
|
"message": "Store bokstaver"
|
||||||
},
|
},
|
||||||
"generatedPassword": {
|
"generatedPassword": {
|
||||||
"message": "Generated password"
|
"message": "Generated password"
|
||||||
|
@ -20,16 +20,16 @@
|
|||||||
"message": "Креирај налог"
|
"message": "Креирај налог"
|
||||||
},
|
},
|
||||||
"newToBitwarden": {
|
"newToBitwarden": {
|
||||||
"message": "New to Bitwarden?"
|
"message": "Нови сте у Bitwarden-у?"
|
||||||
},
|
},
|
||||||
"logInWithPasskey": {
|
"logInWithPasskey": {
|
||||||
"message": "Log in with passkey"
|
"message": "Пријавите се са приступним кључем"
|
||||||
},
|
},
|
||||||
"useSingleSignOn": {
|
"useSingleSignOn": {
|
||||||
"message": "Use single sign-on"
|
"message": "Употребити једнократну пријаву"
|
||||||
},
|
},
|
||||||
"welcomeBack": {
|
"welcomeBack": {
|
||||||
"message": "Welcome back"
|
"message": "Добродошли назад"
|
||||||
},
|
},
|
||||||
"setAStrongPassword": {
|
"setAStrongPassword": {
|
||||||
"message": "Поставите јаку лозинку"
|
"message": "Поставите јаку лозинку"
|
||||||
@ -120,7 +120,7 @@
|
|||||||
"message": "Копирај лозинку"
|
"message": "Копирај лозинку"
|
||||||
},
|
},
|
||||||
"copyPassphrase": {
|
"copyPassphrase": {
|
||||||
"message": "Copy passphrase"
|
"message": "Копирај приступну фразу"
|
||||||
},
|
},
|
||||||
"copyNote": {
|
"copyNote": {
|
||||||
"message": "Копирај белешку"
|
"message": "Копирај белешку"
|
||||||
@ -168,7 +168,7 @@
|
|||||||
"message": "Копирати белешке"
|
"message": "Копирати белешке"
|
||||||
},
|
},
|
||||||
"fill": {
|
"fill": {
|
||||||
"message": "Fill",
|
"message": "Попуни",
|
||||||
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
||||||
},
|
},
|
||||||
"autoFill": {
|
"autoFill": {
|
||||||
@ -427,7 +427,7 @@
|
|||||||
"message": "Генерисање лозинке"
|
"message": "Генерисање лозинке"
|
||||||
},
|
},
|
||||||
"generatePassphrase": {
|
"generatePassphrase": {
|
||||||
"message": "Generate passphrase"
|
"message": "Генеришите приступну фразу"
|
||||||
},
|
},
|
||||||
"regeneratePassword": {
|
"regeneratePassword": {
|
||||||
"message": "Поново генериши лозинку"
|
"message": "Поново генериши лозинку"
|
||||||
@ -591,7 +591,7 @@
|
|||||||
"message": "Покрените веб локацију"
|
"message": "Покрените веб локацију"
|
||||||
},
|
},
|
||||||
"launchWebsiteName": {
|
"launchWebsiteName": {
|
||||||
"message": "Launch website $ITEMNAME$",
|
"message": "Покренути сајт $ITEMNAME$",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"itemname": {
|
"itemname": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -846,7 +846,7 @@
|
|||||||
"message": "Пријави се"
|
"message": "Пријави се"
|
||||||
},
|
},
|
||||||
"logInToBitwarden": {
|
"logInToBitwarden": {
|
||||||
"message": "Log in to Bitwarden"
|
"message": "Пријавите се на Bitwarden"
|
||||||
},
|
},
|
||||||
"restartRegistration": {
|
"restartRegistration": {
|
||||||
"message": "Поново покрените регистрацију"
|
"message": "Поново покрените регистрацију"
|
||||||
@ -1424,7 +1424,7 @@
|
|||||||
"message": "УРЛ Сервера"
|
"message": "УРЛ Сервера"
|
||||||
},
|
},
|
||||||
"selfHostBaseUrl": {
|
"selfHostBaseUrl": {
|
||||||
"message": "Self-host server URL",
|
"message": "УРЛ сервера који се самостално хостује",
|
||||||
"description": "Label for field requesting a self-hosted integration service URL"
|
"description": "Label for field requesting a self-hosted integration service URL"
|
||||||
},
|
},
|
||||||
"apiUrl": {
|
"apiUrl": {
|
||||||
@ -1795,13 +1795,13 @@
|
|||||||
"message": "Историја Лозинке"
|
"message": "Историја Лозинке"
|
||||||
},
|
},
|
||||||
"generatorHistory": {
|
"generatorHistory": {
|
||||||
"message": "Generator history"
|
"message": "Генератор историје"
|
||||||
},
|
},
|
||||||
"clearGeneratorHistoryTitle": {
|
"clearGeneratorHistoryTitle": {
|
||||||
"message": "Clear generator history"
|
"message": "Испразнити генератор историје"
|
||||||
},
|
},
|
||||||
"cleargGeneratorHistoryDescription": {
|
"cleargGeneratorHistoryDescription": {
|
||||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
"message": "Ако наставите, сви уноси ће бити трајно избрисани из генератора историје. Да ли сте сигурни да желите да наставите?"
|
||||||
},
|
},
|
||||||
"back": {
|
"back": {
|
||||||
"message": "Назад"
|
"message": "Назад"
|
||||||
@ -1920,10 +1920,10 @@
|
|||||||
"message": "Обриши историју"
|
"message": "Обриши историју"
|
||||||
},
|
},
|
||||||
"nothingToShow": {
|
"nothingToShow": {
|
||||||
"message": "Nothing to show"
|
"message": "Нема шта да се прикаже"
|
||||||
},
|
},
|
||||||
"nothingGeneratedRecently": {
|
"nothingGeneratedRecently": {
|
||||||
"message": "You haven't generated anything recently"
|
"message": "Недавно нисте ништа генерисали"
|
||||||
},
|
},
|
||||||
"remove": {
|
"remove": {
|
||||||
"message": "Уклони"
|
"message": "Уклони"
|
||||||
@ -2183,7 +2183,7 @@
|
|||||||
"message": "Ваша нова главна лозинка не испуњава захтеве смерница."
|
"message": "Ваша нова главна лозинка не испуњава захтеве смерница."
|
||||||
},
|
},
|
||||||
"receiveMarketingEmailsV2": {
|
"receiveMarketingEmailsV2": {
|
||||||
"message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox."
|
"message": "Добијајте савете, најаве и могућности истраживања од Bitwarden-а у пријемном сандучету."
|
||||||
},
|
},
|
||||||
"unsubscribe": {
|
"unsubscribe": {
|
||||||
"message": "Одјави се"
|
"message": "Одјави се"
|
||||||
@ -2512,7 +2512,7 @@
|
|||||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||||
},
|
},
|
||||||
"sendPasswordDescV3": {
|
"sendPasswordDescV3": {
|
||||||
"message": "Add an optional password for recipients to access this Send.",
|
"message": "Додајте опционалну лозинку за примаоце да приступе овом Send.",
|
||||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||||
},
|
},
|
||||||
"sendNotesDesc": {
|
"sendNotesDesc": {
|
||||||
@ -2875,10 +2875,10 @@
|
|||||||
"message": "Генериши име"
|
"message": "Генериши име"
|
||||||
},
|
},
|
||||||
"generateEmail": {
|
"generateEmail": {
|
||||||
"message": "Generate email"
|
"message": "Генеришите имејл"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Value must be between $MIN$ and $MAX$",
|
"message": "Вредност мора бити између $MIN$ и $MAX$",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
@ -2932,11 +2932,11 @@
|
|||||||
"message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања."
|
"message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања."
|
||||||
},
|
},
|
||||||
"forwarderDomainName": {
|
"forwarderDomainName": {
|
||||||
"message": "Email domain",
|
"message": "Домен имејла",
|
||||||
"description": "Labels the domain name email forwarder service option"
|
"description": "Labels the domain name email forwarder service option"
|
||||||
},
|
},
|
||||||
"forwarderDomainNameHint": {
|
"forwarderDomainNameHint": {
|
||||||
"message": "Choose a domain that is supported by the selected service",
|
"message": "Изаберите домен који подржава изабрана услуга",
|
||||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||||
},
|
},
|
||||||
"forwarderError": {
|
"forwarderError": {
|
||||||
@ -3664,7 +3664,7 @@
|
|||||||
"message": "Искачући додатак да бисте довршили пријаву."
|
"message": "Искачући додатак да бисте довршили пријаву."
|
||||||
},
|
},
|
||||||
"popoutExtension": {
|
"popoutExtension": {
|
||||||
"message": "Popout extension"
|
"message": "Искачући додатак"
|
||||||
},
|
},
|
||||||
"launchDuo": {
|
"launchDuo": {
|
||||||
"message": "Покренути DUO"
|
"message": "Покренути DUO"
|
||||||
@ -3744,25 +3744,25 @@
|
|||||||
"message": "Подаци из сефа су извезени"
|
"message": "Подаци из сефа су извезени"
|
||||||
},
|
},
|
||||||
"typePasskey": {
|
"typePasskey": {
|
||||||
"message": "Приступачни кључ"
|
"message": "Приступни кључ"
|
||||||
},
|
},
|
||||||
"accessing": {
|
"accessing": {
|
||||||
"message": "Приступ"
|
"message": "Приступ"
|
||||||
},
|
},
|
||||||
"passkeyNotCopied": {
|
"passkeyNotCopied": {
|
||||||
"message": "Приступачни кључ неће бити копиран"
|
"message": "Приступни кључ неће бити копиран"
|
||||||
},
|
},
|
||||||
"passkeyNotCopiedAlert": {
|
"passkeyNotCopiedAlert": {
|
||||||
"message": "Приступачни кључ неће бити копиран на клонирану ставку. Да ли желите да наставите са клонирањем ставке?"
|
"message": "Приступни кључ неће бити копиран на клонирану ставку. Да ли желите да наставите са клонирањем ставке?"
|
||||||
},
|
},
|
||||||
"passkeyFeatureIsNotImplementedForAccountsWithoutMasterPassword": {
|
"passkeyFeatureIsNotImplementedForAccountsWithoutMasterPassword": {
|
||||||
"message": "Верификацију захтева сајт који покреће. Ова функција још увек није имплементирана за налоге без главне лозинке."
|
"message": "Верификацију захтева сајт који покреће. Ова функција још увек није имплементирана за налоге без главне лозинке."
|
||||||
},
|
},
|
||||||
"logInWithPasskeyQuestion": {
|
"logInWithPasskeyQuestion": {
|
||||||
"message": "Пријавите се са приступачним кључем?"
|
"message": "Пријавите се са приступним кључем?"
|
||||||
},
|
},
|
||||||
"passkeyAlreadyExists": {
|
"passkeyAlreadyExists": {
|
||||||
"message": "За ову апликацију већ постоји приступачни кључ."
|
"message": "За ову апликацију већ постоји приступни кључ."
|
||||||
},
|
},
|
||||||
"noPasskeysFoundForThisApplication": {
|
"noPasskeysFoundForThisApplication": {
|
||||||
"message": "Нису пронађени приступни кључеви за ову апликацију."
|
"message": "Нису пронађени приступни кључеви за ову апликацију."
|
||||||
@ -3792,7 +3792,7 @@
|
|||||||
"message": "Изаберите приступни кључ за пријаву"
|
"message": "Изаберите приступни кључ за пријаву"
|
||||||
},
|
},
|
||||||
"passkeyItem": {
|
"passkeyItem": {
|
||||||
"message": "Ставка приступачног кључа"
|
"message": "Ставка приступног кључа"
|
||||||
},
|
},
|
||||||
"overwritePasskey": {
|
"overwritePasskey": {
|
||||||
"message": "Заменити приступни кључ?"
|
"message": "Заменити приступни кључ?"
|
||||||
@ -3910,7 +3910,7 @@
|
|||||||
"message": "сервер"
|
"message": "сервер"
|
||||||
},
|
},
|
||||||
"hostedAt": {
|
"hostedAt": {
|
||||||
"message": "hosted at"
|
"message": "хостиран на"
|
||||||
},
|
},
|
||||||
"useDeviceOrHardwareKey": {
|
"useDeviceOrHardwareKey": {
|
||||||
"message": "Користите свој уређај или хардверски кључ"
|
"message": "Користите свој уређај или хардверски кључ"
|
||||||
@ -4006,10 +4006,10 @@
|
|||||||
"message": "Успех"
|
"message": "Успех"
|
||||||
},
|
},
|
||||||
"removePasskey": {
|
"removePasskey": {
|
||||||
"message": "Уклонити приступачни кључ"
|
"message": "Уклонити приступни кључ"
|
||||||
},
|
},
|
||||||
"passkeyRemoved": {
|
"passkeyRemoved": {
|
||||||
"message": "Приступачни кључ је уклоњен"
|
"message": "Приступни кључ је уклоњен"
|
||||||
},
|
},
|
||||||
"autofillSuggestions": {
|
"autofillSuggestions": {
|
||||||
"message": "Предлози за ауто-попуњавање"
|
"message": "Предлози за ауто-попуњавање"
|
||||||
@ -4358,7 +4358,7 @@
|
|||||||
"message": "Подаци"
|
"message": "Подаци"
|
||||||
},
|
},
|
||||||
"passkeys": {
|
"passkeys": {
|
||||||
"message": "Приступачни кључеви",
|
"message": "Приступни кључеви",
|
||||||
"description": "A section header for a list of passkeys."
|
"description": "A section header for a list of passkeys."
|
||||||
},
|
},
|
||||||
"passwords": {
|
"passwords": {
|
||||||
@ -4366,7 +4366,7 @@
|
|||||||
"description": "A section header for a list of passwords."
|
"description": "A section header for a list of passwords."
|
||||||
},
|
},
|
||||||
"logInWithPasskeyAriaLabel": {
|
"logInWithPasskeyAriaLabel": {
|
||||||
"message": "Пријавите се са приступачним кључем",
|
"message": "Пријавите се са приступним кључем",
|
||||||
"description": "ARIA label for the inline menu button that logs in with a passkey."
|
"description": "ARIA label for the inline menu button that logs in with a passkey."
|
||||||
},
|
},
|
||||||
"assign": {
|
"assign": {
|
||||||
@ -4564,13 +4564,13 @@
|
|||||||
"message": "Смештај ставке"
|
"message": "Смештај ставке"
|
||||||
},
|
},
|
||||||
"fileSend": {
|
"fileSend": {
|
||||||
"message": "File Send"
|
"message": "Датотека „Send“"
|
||||||
},
|
},
|
||||||
"fileSends": {
|
"fileSends": {
|
||||||
"message": "Датотека „Send“"
|
"message": "Датотека „Send“"
|
||||||
},
|
},
|
||||||
"textSend": {
|
"textSend": {
|
||||||
"message": "Text Send"
|
"message": "Текст „Send“"
|
||||||
},
|
},
|
||||||
"textSends": {
|
"textSends": {
|
||||||
"message": "Текст „Send“"
|
"message": "Текст „Send“"
|
||||||
@ -4603,7 +4603,7 @@
|
|||||||
"message": "Додатни садржај је доступан"
|
"message": "Додатни садржај је доступан"
|
||||||
},
|
},
|
||||||
"fileSavedToDevice": {
|
"fileSavedToDevice": {
|
||||||
"message": "File saved to device. Manage from your device downloads."
|
"message": "Датотека је сачувана на уређају. Управљајте преузимањима са свог уређаја."
|
||||||
},
|
},
|
||||||
"showCharacterCount": {
|
"showCharacterCount": {
|
||||||
"message": "Прикажи бројање слова"
|
"message": "Прикажи бројање слова"
|
||||||
@ -4636,23 +4636,23 @@
|
|||||||
"message": "Аутентификација"
|
"message": "Аутентификација"
|
||||||
},
|
},
|
||||||
"fillGeneratedPassword": {
|
"fillGeneratedPassword": {
|
||||||
"message": "Fill generated password",
|
"message": "Попуните генерисану лозинку",
|
||||||
"description": "Heading for the password generator within the inline menu"
|
"description": "Heading for the password generator within the inline menu"
|
||||||
},
|
},
|
||||||
"passwordRegenerated": {
|
"passwordRegenerated": {
|
||||||
"message": "Password regenerated",
|
"message": "Лозинка поново генерисана",
|
||||||
"description": "Notification message for when a password has been regenerated"
|
"description": "Notification message for when a password has been regenerated"
|
||||||
},
|
},
|
||||||
"saveLoginToBitwarden": {
|
"saveLoginToBitwarden": {
|
||||||
"message": "Save login to Bitwarden?",
|
"message": "Сачувати пријаву на Bitwarden?",
|
||||||
"description": "Confirmation message for saving a login to Bitwarden"
|
"description": "Confirmation message for saving a login to Bitwarden"
|
||||||
},
|
},
|
||||||
"spaceCharacterDescriptor": {
|
"spaceCharacterDescriptor": {
|
||||||
"message": "Space",
|
"message": "Простор",
|
||||||
"description": "Represents the space key in screen reader content as a readable word"
|
"description": "Represents the space key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"tildeCharacterDescriptor": {
|
"tildeCharacterDescriptor": {
|
||||||
"message": "Tilde",
|
"message": "Тилда",
|
||||||
"description": "Represents the ~ key in screen reader content as a readable word"
|
"description": "Represents the ~ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"backtickCharacterDescriptor": {
|
"backtickCharacterDescriptor": {
|
||||||
@ -4660,55 +4660,55 @@
|
|||||||
"description": "Represents the ` key in screen reader content as a readable word"
|
"description": "Represents the ` key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"exclamationCharacterDescriptor": {
|
"exclamationCharacterDescriptor": {
|
||||||
"message": "Exclamation mark",
|
"message": "Узвичник",
|
||||||
"description": "Represents the ! key in screen reader content as a readable word"
|
"description": "Represents the ! key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"atSignCharacterDescriptor": {
|
"atSignCharacterDescriptor": {
|
||||||
"message": "At sign",
|
"message": "Знак „ет“",
|
||||||
"description": "Represents the @ key in screen reader content as a readable word"
|
"description": "Represents the @ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hashSignCharacterDescriptor": {
|
"hashSignCharacterDescriptor": {
|
||||||
"message": "Hash sign",
|
"message": "Знак „хеш“",
|
||||||
"description": "Represents the # key in screen reader content as a readable word"
|
"description": "Represents the # key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"dollarSignCharacterDescriptor": {
|
"dollarSignCharacterDescriptor": {
|
||||||
"message": "Dollar sign",
|
"message": "Знак долар",
|
||||||
"description": "Represents the $ key in screen reader content as a readable word"
|
"description": "Represents the $ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"percentSignCharacterDescriptor": {
|
"percentSignCharacterDescriptor": {
|
||||||
"message": "Percent sign",
|
"message": "Знак постотак",
|
||||||
"description": "Represents the % key in screen reader content as a readable word"
|
"description": "Represents the % key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"caretCharacterDescriptor": {
|
"caretCharacterDescriptor": {
|
||||||
"message": "Caret",
|
"message": "Знак за уметање",
|
||||||
"description": "Represents the ^ key in screen reader content as a readable word"
|
"description": "Represents the ^ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"ampersandCharacterDescriptor": {
|
"ampersandCharacterDescriptor": {
|
||||||
"message": "Ampersand",
|
"message": "Знак Ampersand",
|
||||||
"description": "Represents the & key in screen reader content as a readable word"
|
"description": "Represents the & key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"asteriskCharacterDescriptor": {
|
"asteriskCharacterDescriptor": {
|
||||||
"message": "Asterisk",
|
"message": "Знак звездица",
|
||||||
"description": "Represents the * key in screen reader content as a readable word"
|
"description": "Represents the * key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"parenLeftCharacterDescriptor": {
|
"parenLeftCharacterDescriptor": {
|
||||||
"message": "Left parenthesis",
|
"message": "Отворена заграда",
|
||||||
"description": "Represents the ( key in screen reader content as a readable word"
|
"description": "Represents the ( key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"parenRightCharacterDescriptor": {
|
"parenRightCharacterDescriptor": {
|
||||||
"message": "Right parenthesis",
|
"message": "Затворена заграда",
|
||||||
"description": "Represents the ) key in screen reader content as a readable word"
|
"description": "Represents the ) key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hyphenCharacterDescriptor": {
|
"hyphenCharacterDescriptor": {
|
||||||
"message": "Underscore",
|
"message": "Доња црта",
|
||||||
"description": "Represents the _ key in screen reader content as a readable word"
|
"description": "Represents the _ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"underscoreCharacterDescriptor": {
|
"underscoreCharacterDescriptor": {
|
||||||
"message": "Hyphen",
|
"message": "Цртица",
|
||||||
"description": "Represents the - key in screen reader content as a readable word"
|
"description": "Represents the - key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"plusCharacterDescriptor": {
|
"plusCharacterDescriptor": {
|
||||||
"message": "Plus",
|
"message": "Плус",
|
||||||
"description": "Represents the + key in screen reader content as a readable word"
|
"description": "Represents the + key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"equalsCharacterDescriptor": {
|
"equalsCharacterDescriptor": {
|
||||||
|
@ -1511,7 +1511,7 @@
|
|||||||
"message": "Komprometterade eller ej betrodda webbplatser kan utnyttja automatisk ifyllnad vid sidladdning."
|
"message": "Komprometterade eller ej betrodda webbplatser kan utnyttja automatisk ifyllnad vid sidladdning."
|
||||||
},
|
},
|
||||||
"learnMoreAboutAutofillOnPageLoadLinkText": {
|
"learnMoreAboutAutofillOnPageLoadLinkText": {
|
||||||
"message": "Learn more about risks"
|
"message": "Läs mer om risker"
|
||||||
},
|
},
|
||||||
"learnMoreAboutAutofill": {
|
"learnMoreAboutAutofill": {
|
||||||
"message": "Läs mer om automatisk ifyllnad"
|
"message": "Läs mer om automatisk ifyllnad"
|
||||||
@ -2411,7 +2411,7 @@
|
|||||||
"message": "Lösenordsskyddad"
|
"message": "Lösenordsskyddad"
|
||||||
},
|
},
|
||||||
"copyLink": {
|
"copyLink": {
|
||||||
"message": "Copy link"
|
"message": "Kopiera länk"
|
||||||
},
|
},
|
||||||
"copySendLink": {
|
"copySendLink": {
|
||||||
"message": "Kopiera Send-länk",
|
"message": "Kopiera Send-länk",
|
||||||
|
@ -2878,7 +2878,7 @@
|
|||||||
"message": "Генерувати е-пошту"
|
"message": "Генерувати е-пошту"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Value must be between $MIN$ and $MAX$",
|
"message": "Значення має бути між $MIN$ та $MAX$",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
|
@ -20,16 +20,16 @@
|
|||||||
"message": "创建账户"
|
"message": "创建账户"
|
||||||
},
|
},
|
||||||
"newToBitwarden": {
|
"newToBitwarden": {
|
||||||
"message": "New to Bitwarden?"
|
"message": "Bitwarden 新手吗?"
|
||||||
},
|
},
|
||||||
"logInWithPasskey": {
|
"logInWithPasskey": {
|
||||||
"message": "Log in with passkey"
|
"message": "使用通行密钥登录"
|
||||||
},
|
},
|
||||||
"useSingleSignOn": {
|
"useSingleSignOn": {
|
||||||
"message": "Use single sign-on"
|
"message": "使用单点登录"
|
||||||
},
|
},
|
||||||
"welcomeBack": {
|
"welcomeBack": {
|
||||||
"message": "Welcome back"
|
"message": "欢迎回来"
|
||||||
},
|
},
|
||||||
"setAStrongPassword": {
|
"setAStrongPassword": {
|
||||||
"message": "设置强密码"
|
"message": "设置强密码"
|
||||||
@ -120,7 +120,7 @@
|
|||||||
"message": "复制密码"
|
"message": "复制密码"
|
||||||
},
|
},
|
||||||
"copyPassphrase": {
|
"copyPassphrase": {
|
||||||
"message": "Copy passphrase"
|
"message": "复制密码短语"
|
||||||
},
|
},
|
||||||
"copyNote": {
|
"copyNote": {
|
||||||
"message": "复制备注"
|
"message": "复制备注"
|
||||||
@ -168,7 +168,7 @@
|
|||||||
"message": "复制备注"
|
"message": "复制备注"
|
||||||
},
|
},
|
||||||
"fill": {
|
"fill": {
|
||||||
"message": "Fill",
|
"message": "填充",
|
||||||
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
"description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible."
|
||||||
},
|
},
|
||||||
"autoFill": {
|
"autoFill": {
|
||||||
@ -427,7 +427,7 @@
|
|||||||
"message": "生成密码"
|
"message": "生成密码"
|
||||||
},
|
},
|
||||||
"generatePassphrase": {
|
"generatePassphrase": {
|
||||||
"message": "Generate passphrase"
|
"message": "生成密码短语"
|
||||||
},
|
},
|
||||||
"regeneratePassword": {
|
"regeneratePassword": {
|
||||||
"message": "重新生成密码"
|
"message": "重新生成密码"
|
||||||
@ -591,7 +591,7 @@
|
|||||||
"message": "启动网站"
|
"message": "启动网站"
|
||||||
},
|
},
|
||||||
"launchWebsiteName": {
|
"launchWebsiteName": {
|
||||||
"message": "Launch website $ITEMNAME$",
|
"message": "前往 $ITEMNAME$ 的网站",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"itemname": {
|
"itemname": {
|
||||||
"content": "$1",
|
"content": "$1",
|
||||||
@ -846,7 +846,7 @@
|
|||||||
"message": "登录"
|
"message": "登录"
|
||||||
},
|
},
|
||||||
"logInToBitwarden": {
|
"logInToBitwarden": {
|
||||||
"message": "Log in to Bitwarden"
|
"message": "登录到 Bitwarden"
|
||||||
},
|
},
|
||||||
"restartRegistration": {
|
"restartRegistration": {
|
||||||
"message": "重新开始注册"
|
"message": "重新开始注册"
|
||||||
@ -1424,7 +1424,7 @@
|
|||||||
"message": "服务器 URL"
|
"message": "服务器 URL"
|
||||||
},
|
},
|
||||||
"selfHostBaseUrl": {
|
"selfHostBaseUrl": {
|
||||||
"message": "Self-host server URL",
|
"message": "自托管服务器 URL",
|
||||||
"description": "Label for field requesting a self-hosted integration service URL"
|
"description": "Label for field requesting a self-hosted integration service URL"
|
||||||
},
|
},
|
||||||
"apiUrl": {
|
"apiUrl": {
|
||||||
@ -1795,13 +1795,13 @@
|
|||||||
"message": "密码历史记录"
|
"message": "密码历史记录"
|
||||||
},
|
},
|
||||||
"generatorHistory": {
|
"generatorHistory": {
|
||||||
"message": "Generator history"
|
"message": "生成器历史记录"
|
||||||
},
|
},
|
||||||
"clearGeneratorHistoryTitle": {
|
"clearGeneratorHistoryTitle": {
|
||||||
"message": "Clear generator history"
|
"message": "清除生成器历史记录"
|
||||||
},
|
},
|
||||||
"cleargGeneratorHistoryDescription": {
|
"cleargGeneratorHistoryDescription": {
|
||||||
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
"message": "若继续,所有条目将从生成器历史记录中永久删除。确定要继续吗?"
|
||||||
},
|
},
|
||||||
"back": {
|
"back": {
|
||||||
"message": "后退"
|
"message": "后退"
|
||||||
@ -1920,10 +1920,10 @@
|
|||||||
"message": "清除历史记录"
|
"message": "清除历史记录"
|
||||||
},
|
},
|
||||||
"nothingToShow": {
|
"nothingToShow": {
|
||||||
"message": "Nothing to show"
|
"message": "没有可显示的内容"
|
||||||
},
|
},
|
||||||
"nothingGeneratedRecently": {
|
"nothingGeneratedRecently": {
|
||||||
"message": "You haven't generated anything recently"
|
"message": "您最近没有生成任何内容"
|
||||||
},
|
},
|
||||||
"remove": {
|
"remove": {
|
||||||
"message": "移除"
|
"message": "移除"
|
||||||
@ -2512,7 +2512,7 @@
|
|||||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||||
},
|
},
|
||||||
"sendPasswordDescV3": {
|
"sendPasswordDescV3": {
|
||||||
"message": "Add an optional password for recipients to access this Send.",
|
"message": "添加一个用于收件人访问此 Send 的可选密码。",
|
||||||
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
|
||||||
},
|
},
|
||||||
"sendNotesDesc": {
|
"sendNotesDesc": {
|
||||||
@ -2875,10 +2875,10 @@
|
|||||||
"message": "生成用户名"
|
"message": "生成用户名"
|
||||||
},
|
},
|
||||||
"generateEmail": {
|
"generateEmail": {
|
||||||
"message": "Generate email"
|
"message": "生成邮件地址"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Value must be between $MIN$ and $MAX$",
|
"message": "值必须在 $MIN$ 和 $MAX$ 之间",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
@ -2932,11 +2932,11 @@
|
|||||||
"message": "使用外部转发服务生成一个电子邮件别名。"
|
"message": "使用外部转发服务生成一个电子邮件别名。"
|
||||||
},
|
},
|
||||||
"forwarderDomainName": {
|
"forwarderDomainName": {
|
||||||
"message": "Email domain",
|
"message": "邮件域名",
|
||||||
"description": "Labels the domain name email forwarder service option"
|
"description": "Labels the domain name email forwarder service option"
|
||||||
},
|
},
|
||||||
"forwarderDomainNameHint": {
|
"forwarderDomainNameHint": {
|
||||||
"message": "Choose a domain that is supported by the selected service",
|
"message": "选择一个所选服务支持的域名",
|
||||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||||
},
|
},
|
||||||
"forwarderError": {
|
"forwarderError": {
|
||||||
@ -4636,156 +4636,156 @@
|
|||||||
"message": "正在验证"
|
"message": "正在验证"
|
||||||
},
|
},
|
||||||
"fillGeneratedPassword": {
|
"fillGeneratedPassword": {
|
||||||
"message": "Fill generated password",
|
"message": "填充已生成的密码",
|
||||||
"description": "Heading for the password generator within the inline menu"
|
"description": "Heading for the password generator within the inline menu"
|
||||||
},
|
},
|
||||||
"passwordRegenerated": {
|
"passwordRegenerated": {
|
||||||
"message": "Password regenerated",
|
"message": "密码已重新生成",
|
||||||
"description": "Notification message for when a password has been regenerated"
|
"description": "Notification message for when a password has been regenerated"
|
||||||
},
|
},
|
||||||
"saveLoginToBitwarden": {
|
"saveLoginToBitwarden": {
|
||||||
"message": "Save login to Bitwarden?",
|
"message": "将登录保存到 Bitwarden 吗?",
|
||||||
"description": "Confirmation message for saving a login to Bitwarden"
|
"description": "Confirmation message for saving a login to Bitwarden"
|
||||||
},
|
},
|
||||||
"spaceCharacterDescriptor": {
|
"spaceCharacterDescriptor": {
|
||||||
"message": "Space",
|
"message": "空格",
|
||||||
"description": "Represents the space key in screen reader content as a readable word"
|
"description": "Represents the space key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"tildeCharacterDescriptor": {
|
"tildeCharacterDescriptor": {
|
||||||
"message": "Tilde",
|
"message": "波浪号",
|
||||||
"description": "Represents the ~ key in screen reader content as a readable word"
|
"description": "Represents the ~ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"backtickCharacterDescriptor": {
|
"backtickCharacterDescriptor": {
|
||||||
"message": "Backtick",
|
"message": "反引号",
|
||||||
"description": "Represents the ` key in screen reader content as a readable word"
|
"description": "Represents the ` key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"exclamationCharacterDescriptor": {
|
"exclamationCharacterDescriptor": {
|
||||||
"message": "Exclamation mark",
|
"message": "感叹号",
|
||||||
"description": "Represents the ! key in screen reader content as a readable word"
|
"description": "Represents the ! key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"atSignCharacterDescriptor": {
|
"atSignCharacterDescriptor": {
|
||||||
"message": "At sign",
|
"message": "艾特号",
|
||||||
"description": "Represents the @ key in screen reader content as a readable word"
|
"description": "Represents the @ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hashSignCharacterDescriptor": {
|
"hashSignCharacterDescriptor": {
|
||||||
"message": "Hash sign",
|
"message": "井号",
|
||||||
"description": "Represents the # key in screen reader content as a readable word"
|
"description": "Represents the # key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"dollarSignCharacterDescriptor": {
|
"dollarSignCharacterDescriptor": {
|
||||||
"message": "Dollar sign",
|
"message": "美元符号",
|
||||||
"description": "Represents the $ key in screen reader content as a readable word"
|
"description": "Represents the $ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"percentSignCharacterDescriptor": {
|
"percentSignCharacterDescriptor": {
|
||||||
"message": "Percent sign",
|
"message": "百分号",
|
||||||
"description": "Represents the % key in screen reader content as a readable word"
|
"description": "Represents the % key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"caretCharacterDescriptor": {
|
"caretCharacterDescriptor": {
|
||||||
"message": "Caret",
|
"message": "脱字符",
|
||||||
"description": "Represents the ^ key in screen reader content as a readable word"
|
"description": "Represents the ^ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"ampersandCharacterDescriptor": {
|
"ampersandCharacterDescriptor": {
|
||||||
"message": "Ampersand",
|
"message": "与和符",
|
||||||
"description": "Represents the & key in screen reader content as a readable word"
|
"description": "Represents the & key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"asteriskCharacterDescriptor": {
|
"asteriskCharacterDescriptor": {
|
||||||
"message": "Asterisk",
|
"message": "星号",
|
||||||
"description": "Represents the * key in screen reader content as a readable word"
|
"description": "Represents the * key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"parenLeftCharacterDescriptor": {
|
"parenLeftCharacterDescriptor": {
|
||||||
"message": "Left parenthesis",
|
"message": "左括号",
|
||||||
"description": "Represents the ( key in screen reader content as a readable word"
|
"description": "Represents the ( key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"parenRightCharacterDescriptor": {
|
"parenRightCharacterDescriptor": {
|
||||||
"message": "Right parenthesis",
|
"message": "右括号",
|
||||||
"description": "Represents the ) key in screen reader content as a readable word"
|
"description": "Represents the ) key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"hyphenCharacterDescriptor": {
|
"hyphenCharacterDescriptor": {
|
||||||
"message": "Underscore",
|
"message": "下划线",
|
||||||
"description": "Represents the _ key in screen reader content as a readable word"
|
"description": "Represents the _ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"underscoreCharacterDescriptor": {
|
"underscoreCharacterDescriptor": {
|
||||||
"message": "Hyphen",
|
"message": "连字符",
|
||||||
"description": "Represents the - key in screen reader content as a readable word"
|
"description": "Represents the - key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"plusCharacterDescriptor": {
|
"plusCharacterDescriptor": {
|
||||||
"message": "Plus",
|
"message": "加号",
|
||||||
"description": "Represents the + key in screen reader content as a readable word"
|
"description": "Represents the + key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"equalsCharacterDescriptor": {
|
"equalsCharacterDescriptor": {
|
||||||
"message": "Equals",
|
"message": "等号",
|
||||||
"description": "Represents the = key in screen reader content as a readable word"
|
"description": "Represents the = key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"braceLeftCharacterDescriptor": {
|
"braceLeftCharacterDescriptor": {
|
||||||
"message": "Left brace",
|
"message": "左大括号",
|
||||||
"description": "Represents the { key in screen reader content as a readable word"
|
"description": "Represents the { key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"braceRightCharacterDescriptor": {
|
"braceRightCharacterDescriptor": {
|
||||||
"message": "Right brace",
|
"message": "右大括号",
|
||||||
"description": "Represents the } key in screen reader content as a readable word"
|
"description": "Represents the } key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"bracketLeftCharacterDescriptor": {
|
"bracketLeftCharacterDescriptor": {
|
||||||
"message": "Left bracket",
|
"message": "左中括号",
|
||||||
"description": "Represents the [ key in screen reader content as a readable word"
|
"description": "Represents the [ key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"bracketRightCharacterDescriptor": {
|
"bracketRightCharacterDescriptor": {
|
||||||
"message": "Right bracket",
|
"message": "右中括号",
|
||||||
"description": "Represents the ] key in screen reader content as a readable word"
|
"description": "Represents the ] key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"pipeCharacterDescriptor": {
|
"pipeCharacterDescriptor": {
|
||||||
"message": "Pipe",
|
"message": "竖线",
|
||||||
"description": "Represents the | key in screen reader content as a readable word"
|
"description": "Represents the | key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"backSlashCharacterDescriptor": {
|
"backSlashCharacterDescriptor": {
|
||||||
"message": "Back slash",
|
"message": "反斜杠",
|
||||||
"description": "Represents the back slash key in screen reader content as a readable word"
|
"description": "Represents the back slash key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"colonCharacterDescriptor": {
|
"colonCharacterDescriptor": {
|
||||||
"message": "Colon",
|
"message": "冒号",
|
||||||
"description": "Represents the : key in screen reader content as a readable word"
|
"description": "Represents the : key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"semicolonCharacterDescriptor": {
|
"semicolonCharacterDescriptor": {
|
||||||
"message": "Semicolon",
|
"message": "分号",
|
||||||
"description": "Represents the ; key in screen reader content as a readable word"
|
"description": "Represents the ; key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"doubleQuoteCharacterDescriptor": {
|
"doubleQuoteCharacterDescriptor": {
|
||||||
"message": "Double quote",
|
"message": "双引号",
|
||||||
"description": "Represents the double quote key in screen reader content as a readable word"
|
"description": "Represents the double quote key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"singleQuoteCharacterDescriptor": {
|
"singleQuoteCharacterDescriptor": {
|
||||||
"message": "Single quote",
|
"message": "单引号",
|
||||||
"description": "Represents the ' key in screen reader content as a readable word"
|
"description": "Represents the ' key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"lessThanCharacterDescriptor": {
|
"lessThanCharacterDescriptor": {
|
||||||
"message": "Less than",
|
"message": "小于号",
|
||||||
"description": "Represents the < key in screen reader content as a readable word"
|
"description": "Represents the < key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"greaterThanCharacterDescriptor": {
|
"greaterThanCharacterDescriptor": {
|
||||||
"message": "Greater than",
|
"message": "大于号",
|
||||||
"description": "Represents the > key in screen reader content as a readable word"
|
"description": "Represents the > key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"commaCharacterDescriptor": {
|
"commaCharacterDescriptor": {
|
||||||
"message": "Comma",
|
"message": "逗号",
|
||||||
"description": "Represents the , key in screen reader content as a readable word"
|
"description": "Represents the , key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"periodCharacterDescriptor": {
|
"periodCharacterDescriptor": {
|
||||||
"message": "Period",
|
"message": "句号",
|
||||||
"description": "Represents the . key in screen reader content as a readable word"
|
"description": "Represents the . key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"questionCharacterDescriptor": {
|
"questionCharacterDescriptor": {
|
||||||
"message": "Question mark",
|
"message": "问号",
|
||||||
"description": "Represents the ? key in screen reader content as a readable word"
|
"description": "Represents the ? key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"forwardSlashCharacterDescriptor": {
|
"forwardSlashCharacterDescriptor": {
|
||||||
"message": "Forward slash",
|
"message": "正斜杠",
|
||||||
"description": "Represents the / key in screen reader content as a readable word"
|
"description": "Represents the / key in screen reader content as a readable word"
|
||||||
},
|
},
|
||||||
"lowercaseAriaLabel": {
|
"lowercaseAriaLabel": {
|
||||||
"message": "Lowercase"
|
"message": "小写"
|
||||||
},
|
},
|
||||||
"uppercaseAriaLabel": {
|
"uppercaseAriaLabel": {
|
||||||
"message": "Uppercase"
|
"message": "大写"
|
||||||
},
|
},
|
||||||
"generatedPassword": {
|
"generatedPassword": {
|
||||||
"message": "Generated password"
|
"message": "生成密码"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<ng-container slot="end">
|
<ng-container slot="end">
|
||||||
<app-pop-out></app-pop-out>
|
<app-pop-out></app-pop-out>
|
||||||
<app-current-account *ngIf="showAcctSwitcher"></app-current-account>
|
<app-current-account *ngIf="showAcctSwitcher && hasLoggedInAccount"></app-current-account>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</popup-header>
|
</popup-header>
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp
|
|||||||
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||||
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||||
import { CurrentAccountComponent } from "../account-switching/current-account.component";
|
import { CurrentAccountComponent } from "../account-switching/current-account.component";
|
||||||
|
import { AccountSwitcherService } from "../account-switching/services/account-switcher.service";
|
||||||
|
|
||||||
import { ExtensionBitwardenLogo } from "./extension-bitwarden-logo.icon";
|
import { ExtensionBitwardenLogo } from "./extension-bitwarden-logo.icon";
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
protected pageIcon: Icon;
|
protected pageIcon: Icon;
|
||||||
protected showReadonlyHostname: boolean;
|
protected showReadonlyHostname: boolean;
|
||||||
protected maxWidth: "md" | "3xl";
|
protected maxWidth: "md" | "3xl";
|
||||||
|
protected hasLoggedInAccount: boolean = false;
|
||||||
|
|
||||||
protected theme: string;
|
protected theme: string;
|
||||||
protected logo = ExtensionBitwardenLogo;
|
protected logo = ExtensionBitwardenLogo;
|
||||||
@ -59,6 +61,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private extensionAnonLayoutWrapperDataService: AnonLayoutWrapperDataService,
|
private extensionAnonLayoutWrapperDataService: AnonLayoutWrapperDataService,
|
||||||
|
private accountSwitcherService: AccountSwitcherService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@ -68,6 +71,12 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy {
|
|||||||
// Listen for page changes and update the page data appropriately
|
// Listen for page changes and update the page data appropriately
|
||||||
this.listenForPageDataChanges();
|
this.listenForPageDataChanges();
|
||||||
this.listenForServiceDataChanges();
|
this.listenForServiceDataChanges();
|
||||||
|
|
||||||
|
this.accountSwitcherService.availableAccounts$
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe((accounts) => {
|
||||||
|
this.hasLoggedInAccount = accounts.some((account) => account.id !== "addAccount");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private listenForPageDataChanges() {
|
private listenForPageDataChanges() {
|
||||||
|
@ -27,6 +27,7 @@ import { ButtonModule, I18nMockService } from "@bitwarden/components";
|
|||||||
|
|
||||||
import { RegistrationCheckEmailIcon } from "../../../../../../libs/auth/src/angular/icons/registration-check-email.icon";
|
import { RegistrationCheckEmailIcon } from "../../../../../../libs/auth/src/angular/icons/registration-check-email.icon";
|
||||||
import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popup-router-cache.service";
|
import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popup-router-cache.service";
|
||||||
|
import { AccountSwitcherService } from "../account-switching/services/account-switcher.service";
|
||||||
|
|
||||||
import { ExtensionAnonLayoutWrapperDataService } from "./extension-anon-layout-wrapper-data.service";
|
import { ExtensionAnonLayoutWrapperDataService } from "./extension-anon-layout-wrapper-data.service";
|
||||||
import {
|
import {
|
||||||
@ -45,6 +46,7 @@ const decorators = (options: {
|
|||||||
applicationVersion?: string;
|
applicationVersion?: string;
|
||||||
clientType?: ClientType;
|
clientType?: ClientType;
|
||||||
hostName?: string;
|
hostName?: string;
|
||||||
|
accounts?: any[];
|
||||||
}) => {
|
}) => {
|
||||||
return [
|
return [
|
||||||
componentWrapperDecorator(
|
componentWrapperDecorator(
|
||||||
@ -83,6 +85,13 @@ const decorators = (options: {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: AccountSwitcherService,
|
||||||
|
useValue: {
|
||||||
|
availableAccounts$: of(options.accounts || []),
|
||||||
|
SPECIAL_ADD_ACCOUNT_ID: "addAccount",
|
||||||
|
} as Partial<AccountSwitcherService>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: AuthService,
|
provide: AuthService,
|
||||||
useValue: {
|
useValue: {
|
||||||
@ -300,3 +309,64 @@ export const DynamicContentExample: Story = {
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const HasLoggedInAccountExample: Story = {
|
||||||
|
render: (args) => ({
|
||||||
|
props: args,
|
||||||
|
template: "<router-outlet></router-outlet>",
|
||||||
|
}),
|
||||||
|
decorators: decorators({
|
||||||
|
components: [DefaultPrimaryOutletExampleComponent],
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: "**",
|
||||||
|
redirectTo: "has-logged-in-account",
|
||||||
|
pathMatch: "full",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: ExtensionAnonLayoutWrapperComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "has-logged-in-account",
|
||||||
|
data: {
|
||||||
|
hasLoggedInAccount: true,
|
||||||
|
showAcctSwitcher: true,
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: DefaultPrimaryOutletExampleComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: DefaultSecondaryOutletExampleComponent,
|
||||||
|
outlet: "secondary",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: DefaultEnvSelectorOutletExampleComponent,
|
||||||
|
outlet: "environment-selector",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
name: "Test User",
|
||||||
|
email: "testuser@bitwarden.com",
|
||||||
|
id: "123e4567-e89b-12d3-a456-426614174000",
|
||||||
|
server: "bitwarden.com",
|
||||||
|
status: 2,
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "addAccount",
|
||||||
|
id: "addAccount",
|
||||||
|
isActive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, Validators } from "@angular/forms";
|
||||||
import { Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Subject, firstValueFrom, switchMap, takeUntil } from "rxjs";
|
import { Subject, firstValueFrom, switchMap, takeUntil, tap } from "rxjs";
|
||||||
|
|
||||||
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component";
|
||||||
import { LoginEmailServiceAbstraction, RegisterRouteService } from "@bitwarden/auth/common";
|
import { LoginEmailServiceAbstraction, RegisterRouteService } from "@bitwarden/auth/common";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
@ -38,9 +40,13 @@ export class HomeComponent implements OnInit, OnDestroy {
|
|||||||
private accountSwitcherService: AccountSwitcherService,
|
private accountSwitcherService: AccountSwitcherService,
|
||||||
private registerRouteService: RegisterRouteService,
|
private registerRouteService: RegisterRouteService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.listenForUnauthUiRefreshFlagChanges();
|
||||||
|
|
||||||
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
const email = await firstValueFrom(this.loginEmailService.loginEmail$);
|
||||||
const rememberEmail = this.loginEmailService.getRememberEmail();
|
const rememberEmail = this.loginEmailService.getRememberEmail();
|
||||||
|
|
||||||
@ -70,6 +76,29 @@ export class HomeComponent implements OnInit, OnDestroy {
|
|||||||
this.destroyed$.complete();
|
this.destroyed$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private listenForUnauthUiRefreshFlagChanges() {
|
||||||
|
this.configService
|
||||||
|
.getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh)
|
||||||
|
.pipe(
|
||||||
|
tap(async (flag) => {
|
||||||
|
// If the flag is turned ON, we must force a reload to ensure the correct UI is shown
|
||||||
|
if (flag) {
|
||||||
|
const uniqueQueryParams = {
|
||||||
|
...this.route.queryParams,
|
||||||
|
// adding a unique timestamp to the query params to force a reload
|
||||||
|
t: new Date().getTime().toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.router.navigate(["/login"], {
|
||||||
|
queryParams: uniqueQueryParams,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroyed$),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
get availableAccounts$() {
|
get availableAccounts$() {
|
||||||
return this.accountSwitcherService.availableAccounts$;
|
return this.accountSwitcherService.availableAccounts$;
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,6 @@ export type OverlayBackgroundExtensionMessageHandlers = {
|
|||||||
getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number;
|
getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number;
|
||||||
updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||||
triggerSubFrameFocusInRebuild: ({ sender }: BackgroundSenderParam) => void;
|
triggerSubFrameFocusInRebuild: ({ sender }: BackgroundSenderParam) => void;
|
||||||
shouldRepositionSubFrameInlineMenuOnScroll: ({ sender }: BackgroundSenderParam) => void;
|
|
||||||
destroyAutofillInlineMenuListeners: ({
|
destroyAutofillInlineMenuListeners: ({
|
||||||
message,
|
message,
|
||||||
sender,
|
sender,
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
} from "@bitwarden/common/spec";
|
} from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
|
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||||
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -106,6 +107,7 @@ describe("OverlayBackground", () => {
|
|||||||
let selectedThemeMock$: BehaviorSubject<ThemeType>;
|
let selectedThemeMock$: BehaviorSubject<ThemeType>;
|
||||||
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
let inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
|
||||||
let themeStateService: MockProxy<ThemeStateService>;
|
let themeStateService: MockProxy<ThemeStateService>;
|
||||||
|
let totpService: MockProxy<TotpService>;
|
||||||
let overlayBackground: OverlayBackground;
|
let overlayBackground: OverlayBackground;
|
||||||
let portKeyForTabSpy: Record<number, string>;
|
let portKeyForTabSpy: Record<number, string>;
|
||||||
let pageDetailsForTabSpy: PageDetailsForTab;
|
let pageDetailsForTabSpy: PageDetailsForTab;
|
||||||
@ -184,6 +186,7 @@ describe("OverlayBackground", () => {
|
|||||||
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
inlineMenuFieldQualificationService = new InlineMenuFieldQualificationService();
|
||||||
themeStateService = mock<ThemeStateService>();
|
themeStateService = mock<ThemeStateService>();
|
||||||
themeStateService.selectedTheme$ = selectedThemeMock$;
|
themeStateService.selectedTheme$ = selectedThemeMock$;
|
||||||
|
totpService = mock<TotpService>();
|
||||||
overlayBackground = new OverlayBackground(
|
overlayBackground = new OverlayBackground(
|
||||||
logService,
|
logService,
|
||||||
cipherService,
|
cipherService,
|
||||||
@ -198,6 +201,7 @@ describe("OverlayBackground", () => {
|
|||||||
fido2ActiveRequestManager,
|
fido2ActiveRequestManager,
|
||||||
inlineMenuFieldQualificationService,
|
inlineMenuFieldQualificationService,
|
||||||
themeStateService,
|
themeStateService,
|
||||||
|
totpService,
|
||||||
generatedPasswordCallbackMock,
|
generatedPasswordCallbackMock,
|
||||||
addPasswordCallbackMock,
|
addPasswordCallbackMock,
|
||||||
);
|
);
|
||||||
@ -629,9 +633,7 @@ describe("OverlayBackground", () => {
|
|||||||
|
|
||||||
it("skips updating the inline menu list if the user has the inline menu set to open on button click", async () => {
|
it("skips updating the inline menu list if the user has the inline menu set to open on button click", async () => {
|
||||||
inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick);
|
inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick);
|
||||||
jest
|
overlayBackground["inlineMenuListPort"] = null;
|
||||||
.spyOn(overlayBackground as any, "checkIsInlineMenuListVisible")
|
|
||||||
.mockReturnValue(false);
|
|
||||||
tabsSendMessageSpy.mockImplementation((_tab, message, _options) => {
|
tabsSendMessageSpy.mockImplementation((_tab, message, _options) => {
|
||||||
if (message.command === "checkFocusedFieldHasValue") {
|
if (message.command === "checkFocusedFieldHasValue") {
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@ -2267,7 +2269,7 @@ describe("OverlayBackground", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("closes the list if the user has the inline menu set to show on button click and the list is open", async () => {
|
it("closes the list if the user has the inline menu set to show on button click and the list is open", async () => {
|
||||||
overlayBackground["isInlineMenuListVisible"] = true;
|
overlayBackground["inlineMenuListPort"] = listPortSpy;
|
||||||
inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick);
|
inlineMenuVisibilityMock$.next(AutofillOverlayVisibility.OnButtonClick);
|
||||||
|
|
||||||
sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender);
|
sendMockExtensionMessage({ command: "openAutofillInlineMenu" }, sender);
|
||||||
|
@ -33,6 +33,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
|
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon";
|
import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon";
|
||||||
@ -168,8 +169,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender),
|
getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender),
|
||||||
updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender),
|
updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender),
|
||||||
triggerSubFrameFocusInRebuild: ({ sender }) => this.triggerSubFrameFocusInRebuild(sender),
|
triggerSubFrameFocusInRebuild: ({ sender }) => this.triggerSubFrameFocusInRebuild(sender),
|
||||||
shouldRepositionSubFrameInlineMenuOnScroll: ({ sender }) =>
|
|
||||||
this.shouldRepositionSubFrameInlineMenuOnScroll(sender),
|
|
||||||
destroyAutofillInlineMenuListeners: ({ message, sender }) =>
|
destroyAutofillInlineMenuListeners: ({ message, sender }) =>
|
||||||
this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId),
|
this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId),
|
||||||
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
|
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
|
||||||
@ -219,6 +218,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
private fido2ActiveRequestManager: Fido2ActiveRequestManager,
|
private fido2ActiveRequestManager: Fido2ActiveRequestManager,
|
||||||
private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService,
|
private inlineMenuFieldQualificationService: InlineMenuFieldQualificationService,
|
||||||
private themeStateService: ThemeStateService,
|
private themeStateService: ThemeStateService,
|
||||||
|
private totpService: TotpService,
|
||||||
private generatePasswordCallback: () => Promise<string>,
|
private generatePasswordCallback: () => Promise<string>,
|
||||||
private addPasswordCallback: (password: string) => Promise<void>,
|
private addPasswordCallback: (password: string) => Promise<void>,
|
||||||
) {
|
) {
|
||||||
@ -1010,7 +1010,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.checkIsInlineMenuListVisible() &&
|
!this.inlineMenuListPort &&
|
||||||
(await this.getInlineMenuVisibility()) === AutofillOverlayVisibility.OnButtonClick
|
(await this.getInlineMenuVisibility()) === AutofillOverlayVisibility.OnButtonClick
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@ -1060,7 +1060,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId);
|
const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId);
|
||||||
|
|
||||||
if (usePasskey && cipher.login?.hasFido2Credentials) {
|
if (usePasskey && cipher.login?.hasFido2Credentials) {
|
||||||
await this.authenticatePasskeyCredential(
|
await this.authenticatePasskeyCredential(
|
||||||
sender,
|
sender,
|
||||||
@ -1068,6 +1067,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
);
|
);
|
||||||
this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher);
|
this.updateLastUsedInlineMenuCipher(inlineMenuCipherId, cipher);
|
||||||
|
|
||||||
|
if (cipher.login?.totp) {
|
||||||
|
this.platformUtilsService.copyToClipboard(
|
||||||
|
await this.totpService.getCode(cipher.login.totp),
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1819,7 +1823,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isInlineMenuListVisible) {
|
if (this.inlineMenuListPort) {
|
||||||
this.closeInlineMenu(sender, {
|
this.closeInlineMenu(sender, {
|
||||||
forceCloseInlineMenu: true,
|
forceCloseInlineMenu: true,
|
||||||
overlayElement: AutofillOverlayElement.List,
|
overlayElement: AutofillOverlayElement.List,
|
||||||
@ -2600,20 +2604,6 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
this.repositionInlineMenu$.next(sender);
|
this.repositionInlineMenu$.next(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggers on scroll of a frame within the tab. Will reposition the inline menu
|
|
||||||
* if the focused field is within a sub-frame and the inline menu is visible.
|
|
||||||
*
|
|
||||||
* @param sender - The sender of the message
|
|
||||||
*/
|
|
||||||
private shouldRepositionSubFrameInlineMenuOnScroll(sender: chrome.runtime.MessageSender) {
|
|
||||||
if (!this.isInlineMenuButtonVisible || sender.tab.id !== this.focusedFieldData?.tabId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.focusedFieldData.frameId > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles determining if the inline menu should be repositioned or closed, and initiates
|
* Handles determining if the inline menu should be repositioned or closed, and initiates
|
||||||
* the process of calculating the new position of the inline menu.
|
* the process of calculating the new position of the inline menu.
|
||||||
|
@ -219,8 +219,12 @@ export class AutofillComponent implements OnInit {
|
|||||||
: AutofillOverlayVisibility.Off;
|
: AutofillOverlayVisibility.Off;
|
||||||
|
|
||||||
await this.autofillSettingsService.setInlineMenuVisibility(newInlineMenuVisibilityValue);
|
await this.autofillSettingsService.setInlineMenuVisibility(newInlineMenuVisibilityValue);
|
||||||
|
|
||||||
|
// No need to initiate browser permission request if a feature is being turned off
|
||||||
|
if (newInlineMenuVisibilityValue !== AutofillOverlayVisibility.Off) {
|
||||||
await this.requestPrivacyPermission();
|
await this.requestPrivacyPermission();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async updateAutofillOnPageLoad() {
|
async updateAutofillOnPageLoad() {
|
||||||
await this.autofillSettingsService.setAutofillOnPageLoad(this.enableAutofillOnPageLoad);
|
await this.autofillSettingsService.setAutofillOnPageLoad(this.enableAutofillOnPageLoad);
|
||||||
|
@ -104,6 +104,7 @@ export class CreditCardAutoFillConstants {
|
|||||||
];
|
];
|
||||||
|
|
||||||
static readonly CardHolderFieldNames: string[] = [
|
static readonly CardHolderFieldNames: string[] = [
|
||||||
|
"accountholdername",
|
||||||
"cc-name",
|
"cc-name",
|
||||||
"card-name",
|
"card-name",
|
||||||
"cardholder-name",
|
"cardholder-name",
|
||||||
@ -113,6 +114,7 @@ export class CreditCardAutoFillConstants {
|
|||||||
];
|
];
|
||||||
|
|
||||||
static readonly CardHolderFieldNameValues: string[] = [
|
static readonly CardHolderFieldNameValues: string[] = [
|
||||||
|
"accountholdername",
|
||||||
"cc-name",
|
"cc-name",
|
||||||
"card-name",
|
"card-name",
|
||||||
"cardholder-name",
|
"cardholder-name",
|
||||||
|
@ -1703,6 +1703,10 @@ describe("AutofillOverlayContentService", () => {
|
|||||||
const repositionEvents = [EVENTS.SCROLL, EVENTS.RESIZE];
|
const repositionEvents = [EVENTS.SCROLL, EVENTS.RESIZE];
|
||||||
repositionEvents.forEach((repositionEvent) => {
|
repositionEvents.forEach((repositionEvent) => {
|
||||||
it(`sends a message trigger overlay reposition message to the background when a ${repositionEvent} event occurs`, async () => {
|
it(`sends a message trigger overlay reposition message to the background when a ${repositionEvent} event occurs`, async () => {
|
||||||
|
Object.defineProperty(globalThis, "scrollY", {
|
||||||
|
value: 10,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
sendExtensionMessageSpy.mockResolvedValueOnce(true);
|
sendExtensionMessageSpy.mockResolvedValueOnce(true);
|
||||||
globalThis.dispatchEvent(new Event(repositionEvent));
|
globalThis.dispatchEvent(new Event(repositionEvent));
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
@ -1568,41 +1568,46 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
* the overlay elements on scroll or resize.
|
* the overlay elements on scroll or resize.
|
||||||
*/
|
*/
|
||||||
private setOverlayRepositionEventListeners() {
|
private setOverlayRepositionEventListeners() {
|
||||||
|
let currentScrollY = globalThis.scrollY;
|
||||||
|
let currentScrollX = globalThis.scrollX;
|
||||||
|
let mostRecentTargetScrollY = 0;
|
||||||
const repositionHandler = this.useEventHandlersMemo(
|
const repositionHandler = this.useEventHandlersMemo(
|
||||||
throttle(this.handleOverlayRepositionEvent, 250),
|
throttle(this.handleOverlayRepositionEvent, 250),
|
||||||
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
||||||
);
|
);
|
||||||
|
|
||||||
const eventTargetContainsFocusedField = (eventTarget: Element | Document) => {
|
const eventTargetContainsFocusedField = (eventTarget: Element) => {
|
||||||
if (!eventTarget || !this.mostRecentlyFocusedField) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeElement = (eventTarget as Document).activeElement;
|
|
||||||
if (activeElement) {
|
|
||||||
return (
|
|
||||||
activeElement === this.mostRecentlyFocusedField ||
|
|
||||||
activeElement.contains(this.mostRecentlyFocusedField) ||
|
|
||||||
this.inlineMenuContentService?.isElementInlineMenu(activeElement as HTMLElement)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof eventTarget.contains !== "function") {
|
if (typeof eventTarget.contains !== "function") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
|
const targetScrollY = eventTarget.scrollTop;
|
||||||
|
if (targetScrollY === mostRecentTargetScrollY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
eventTarget === this.mostRecentlyFocusedField ||
|
eventTarget === this.mostRecentlyFocusedField ||
|
||||||
eventTarget.contains(this.mostRecentlyFocusedField)
|
eventTarget.contains(this.mostRecentlyFocusedField)
|
||||||
);
|
) {
|
||||||
|
mostRecentTargetScrollY = targetScrollY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
const scrollHandler = this.useEventHandlersMemo(
|
const scrollHandler = this.useEventHandlersMemo(
|
||||||
throttle(async (event) => {
|
throttle(async (event) => {
|
||||||
if (
|
if (
|
||||||
eventTargetContainsFocusedField(event.target) ||
|
currentScrollY !== globalThis.scrollY ||
|
||||||
(await this.shouldRepositionSubFrameInlineMenuOnScroll())
|
currentScrollX !== globalThis.scrollX ||
|
||||||
|
eventTargetContainsFocusedField(event.target)
|
||||||
) {
|
) {
|
||||||
repositionHandler(event);
|
repositionHandler(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentScrollY = globalThis.scrollY;
|
||||||
|
currentScrollX = globalThis.scrollX;
|
||||||
}, 50),
|
}, 50),
|
||||||
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
||||||
);
|
);
|
||||||
|
@ -980,7 +980,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ
|
|||||||
const queueLength = this.mutationsQueue.length;
|
const queueLength = this.mutationsQueue.length;
|
||||||
|
|
||||||
if (!this.domQueryService.pageContainsShadowDomElements()) {
|
if (!this.domQueryService.pageContainsShadowDomElements()) {
|
||||||
this.domQueryService.checkPageContainsShadowDom();
|
this.checkPageContainsShadowDom();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let queueIndex = 0; queueIndex < queueLength; queueIndex++) {
|
for (let queueIndex = 0; queueIndex < queueLength; queueIndex++) {
|
||||||
@ -999,6 +999,29 @@ export class CollectAutofillContentService implements CollectAutofillContentServ
|
|||||||
this.mutationsQueue = [];
|
this.mutationsQueue = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles checking if the current page contains a ShadowDOM element and
|
||||||
|
* flags that a re-collection of page details is required if it does.
|
||||||
|
*/
|
||||||
|
private checkPageContainsShadowDom() {
|
||||||
|
this.domQueryService.checkPageContainsShadowDom();
|
||||||
|
if (this.domQueryService.pageContainsShadowDomElements()) {
|
||||||
|
this.flagPageDetailsUpdateIsRequired();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers several flags that indicate that a collection of page details should
|
||||||
|
* occur again on a subsequent call after a mutation has been observed in the DOM.
|
||||||
|
*/
|
||||||
|
private flagPageDetailsUpdateIsRequired() {
|
||||||
|
this.domRecentlyMutated = true;
|
||||||
|
if (this.autofillOverlayContentService) {
|
||||||
|
this.autofillOverlayContentService.pageDetailsUpdateRequired = true;
|
||||||
|
}
|
||||||
|
this.noFieldsFound = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes all mutation records encountered by the mutation observer.
|
* Processes all mutation records encountered by the mutation observer.
|
||||||
*
|
*
|
||||||
@ -1023,11 +1046,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ
|
|||||||
(this.isAutofillElementNodeMutated(mutation.removedNodes, true) ||
|
(this.isAutofillElementNodeMutated(mutation.removedNodes, true) ||
|
||||||
this.isAutofillElementNodeMutated(mutation.addedNodes))
|
this.isAutofillElementNodeMutated(mutation.addedNodes))
|
||||||
) {
|
) {
|
||||||
this.domRecentlyMutated = true;
|
this.flagPageDetailsUpdateIsRequired();
|
||||||
if (this.autofillOverlayContentService) {
|
|
||||||
this.autofillOverlayContentService.pageDetailsUpdateRequired = true;
|
|
||||||
}
|
|
||||||
this.noFieldsFound = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,12 +257,9 @@ import { BrowserPlatformUtilsService } from "../platform/services/platform-utils
|
|||||||
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
|
import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service";
|
||||||
import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory";
|
import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory";
|
||||||
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
|
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
|
||||||
import { ForegroundTaskSchedulerService } from "../platform/services/task-scheduler/foreground-task-scheduler.service";
|
|
||||||
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
|
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
|
||||||
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
|
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
|
||||||
import { ForegroundMemoryStorageService } from "../platform/storage/foreground-memory-storage.service";
|
|
||||||
import { OffscreenStorageService } from "../platform/storage/offscreen-storage.service";
|
import { OffscreenStorageService } from "../platform/storage/offscreen-storage.service";
|
||||||
import { ForegroundSyncService } from "../platform/sync/foreground-sync.service";
|
|
||||||
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
|
import { SyncServiceListener } from "../platform/sync/sync-service.listener";
|
||||||
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
|
import { fromChromeRuntimeMessaging } from "../platform/utils/from-chrome-runtime-messaging";
|
||||||
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
|
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
|
||||||
@ -401,7 +398,7 @@ export default class MainBackground {
|
|||||||
|
|
||||||
private popupViewCacheBackgroundService: PopupViewCacheBackgroundService;
|
private popupViewCacheBackgroundService: PopupViewCacheBackgroundService;
|
||||||
|
|
||||||
constructor(public popupOnlyContext: boolean = false) {
|
constructor() {
|
||||||
// Services
|
// Services
|
||||||
const lockedCallback = async (userId?: string) => {
|
const lockedCallback = async (userId?: string) => {
|
||||||
if (this.notificationsService != null) {
|
if (this.notificationsService != null) {
|
||||||
@ -460,6 +457,18 @@ export default class MainBackground {
|
|||||||
this.offscreenDocumentService,
|
this.offscreenDocumentService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used
|
||||||
|
|
||||||
|
if (BrowserApi.isManifestVersion(3)) {
|
||||||
|
// manifest v3 can reuse the same storage. They are split for v2 due to lacking a good sync mechanism, which isn't true for v3
|
||||||
|
this.memoryStorageForStateProviders = new BrowserMemoryStorageService(); // mv3 stores to storage.session
|
||||||
|
this.memoryStorageService = this.memoryStorageForStateProviders;
|
||||||
|
} else {
|
||||||
|
this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(); // mv2 stores to memory
|
||||||
|
this.memoryStorageService = this.memoryStorageForStateProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BrowserApi.isManifestVersion(3)) {
|
||||||
// Creates a session key for mv3 storage of large memory items
|
// Creates a session key for mv3 storage of large memory items
|
||||||
const sessionKey = new Lazy(async () => {
|
const sessionKey = new Lazy(async () => {
|
||||||
// Key already in session storage
|
// Key already in session storage
|
||||||
@ -482,42 +491,20 @@ export default class MainBackground {
|
|||||||
return derivedKey;
|
return derivedKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
const mv3MemoryStorageCreator = () => {
|
this.largeObjectMemoryStorageForStateProviders = new LocalBackedSessionStorageService(
|
||||||
if (this.popupOnlyContext) {
|
sessionKey,
|
||||||
return new ForegroundMemoryStorageService();
|
this.storageService,
|
||||||
}
|
|
||||||
|
|
||||||
// For local backed session storage, we expect that the encrypted data on disk will persist longer than the encryption key in memory
|
// For local backed session storage, we expect that the encrypted data on disk will persist longer than the encryption key in memory
|
||||||
// and failures to decrypt because of that are completely expected. For this reason, we pass in `false` to the `EncryptServiceImplementation`
|
// and failures to decrypt because of that are completely expected. For this reason, we pass in `false` to the `EncryptServiceImplementation`
|
||||||
// so that MAC failures are not logged.
|
// so that MAC failures are not logged.
|
||||||
return new LocalBackedSessionStorageService(
|
|
||||||
sessionKey,
|
|
||||||
this.storageService,
|
|
||||||
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
|
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
|
||||||
this.platformUtilsService,
|
this.platformUtilsService,
|
||||||
this.logService,
|
this.logService,
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used
|
|
||||||
|
|
||||||
if (BrowserApi.isManifestVersion(3)) {
|
|
||||||
// manifest v3 can reuse the same storage. They are split for v2 due to lacking a good sync mechanism, which isn't true for v3
|
|
||||||
this.memoryStorageForStateProviders = new BrowserMemoryStorageService(); // mv3 stores to storage.session
|
|
||||||
this.memoryStorageService = this.memoryStorageForStateProviders;
|
|
||||||
} else {
|
} else {
|
||||||
if (popupOnlyContext) {
|
// mv2 stores to the same location
|
||||||
this.memoryStorageForStateProviders = new ForegroundMemoryStorageService();
|
this.largeObjectMemoryStorageForStateProviders = this.memoryStorageForStateProviders;
|
||||||
this.memoryStorageService = new ForegroundMemoryStorageService();
|
|
||||||
} else {
|
|
||||||
this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(); // mv2 stores to memory
|
|
||||||
this.memoryStorageService = this.memoryStorageForStateProviders;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.largeObjectMemoryStorageForStateProviders = BrowserApi.isManifestVersion(3)
|
|
||||||
? mv3MemoryStorageCreator() // mv3 stores to local-backed session storage
|
|
||||||
: this.memoryStorageForStateProviders; // mv2 stores to the same location
|
|
||||||
|
|
||||||
const localStorageStorageService = BrowserApi.isManifestVersion(3)
|
const localStorageStorageService = BrowserApi.isManifestVersion(3)
|
||||||
? new OffscreenStorageService(this.offscreenDocumentService)
|
? new OffscreenStorageService(this.offscreenDocumentService)
|
||||||
@ -575,9 +562,10 @@ export default class MainBackground {
|
|||||||
this.derivedStateProvider,
|
this.derivedStateProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.taskSchedulerService = this.popupOnlyContext
|
this.taskSchedulerService = new BackgroundTaskSchedulerService(
|
||||||
? new ForegroundTaskSchedulerService(this.logService, this.stateProvider)
|
this.logService,
|
||||||
: new BackgroundTaskSchedulerService(this.logService, this.stateProvider);
|
this.stateProvider,
|
||||||
|
);
|
||||||
this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.scheduleNextSyncInterval, () =>
|
this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.scheduleNextSyncInterval, () =>
|
||||||
this.fullSync(),
|
this.fullSync(),
|
||||||
);
|
);
|
||||||
@ -632,6 +620,7 @@ export default class MainBackground {
|
|||||||
this.stateService,
|
this.stateService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
|
this.logService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||||
@ -872,7 +861,6 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
|
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
|
||||||
|
|
||||||
if (!this.popupOnlyContext) {
|
|
||||||
this.vaultTimeoutService = new VaultTimeoutService(
|
this.vaultTimeoutService = new VaultTimeoutService(
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.masterPasswordService,
|
this.masterPasswordService,
|
||||||
@ -891,7 +879,6 @@ export default class MainBackground {
|
|||||||
lockedCallback,
|
lockedCallback,
|
||||||
logoutCallback,
|
logoutCallback,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
this.containerService = new ContainerService(this.keyService, this.encryptService);
|
this.containerService = new ContainerService(this.keyService, this.encryptService);
|
||||||
|
|
||||||
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
||||||
@ -912,24 +899,6 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.providerService = new ProviderService(this.stateProvider);
|
this.providerService = new ProviderService(this.stateProvider);
|
||||||
|
|
||||||
if (this.popupOnlyContext) {
|
|
||||||
this.syncService = new ForegroundSyncService(
|
|
||||||
this.stateService,
|
|
||||||
this.folderService,
|
|
||||||
this.folderApiService,
|
|
||||||
this.messagingService,
|
|
||||||
this.logService,
|
|
||||||
this.cipherService,
|
|
||||||
this.collectionService,
|
|
||||||
this.apiService,
|
|
||||||
this.accountService,
|
|
||||||
this.authService,
|
|
||||||
this.sendService,
|
|
||||||
this.sendApiService,
|
|
||||||
messageListener,
|
|
||||||
this.stateProvider,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.syncService = new DefaultSyncService(
|
this.syncService = new DefaultSyncService(
|
||||||
this.masterPasswordService,
|
this.masterPasswordService,
|
||||||
this.accountService,
|
this.accountService,
|
||||||
@ -964,7 +933,7 @@ export default class MainBackground {
|
|||||||
this.messagingService,
|
this.messagingService,
|
||||||
this.logService,
|
this.logService,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
this.eventUploadService = new EventUploadService(
|
this.eventUploadService = new EventUploadService(
|
||||||
this.apiService,
|
this.apiService,
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
@ -1111,7 +1080,7 @@ export default class MainBackground {
|
|||||||
this.isSafari = this.platformUtilsService.isSafari();
|
this.isSafari = this.platformUtilsService.isSafari();
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
if (!this.popupOnlyContext) {
|
|
||||||
this.fido2Background = new Fido2Background(
|
this.fido2Background = new Fido2Background(
|
||||||
this.logService,
|
this.logService,
|
||||||
this.fido2ActiveRequestManager,
|
this.fido2ActiveRequestManager,
|
||||||
@ -1202,7 +1171,14 @@ export default class MainBackground {
|
|||||||
|
|
||||||
const contextMenuClickedHandler = new ContextMenuClickedHandler(
|
const contextMenuClickedHandler = new ContextMenuClickedHandler(
|
||||||
(options) => this.platformUtilsService.copyToClipboard(options.text),
|
(options) => this.platformUtilsService.copyToClipboard(options.text),
|
||||||
async () => this.generatePasswordToClipboard(),
|
async (_tab) => {
|
||||||
|
const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
|
||||||
|
const password = await this.passwordGenerationService.generatePassword(options);
|
||||||
|
this.platformUtilsService.copyToClipboard(password);
|
||||||
|
// 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.passwordGenerationService.addHistory(password);
|
||||||
|
},
|
||||||
async (tab, cipher) => {
|
async (tab, cipher) => {
|
||||||
this.loginToAutoFill = cipher;
|
this.loginToAutoFill = cipher;
|
||||||
if (tab == null) {
|
if (tab == null) {
|
||||||
@ -1226,7 +1202,6 @@ export default class MainBackground {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);
|
this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);
|
||||||
}
|
|
||||||
|
|
||||||
this.idleBackground = new IdleBackground(
|
this.idleBackground = new IdleBackground(
|
||||||
this.vaultTimeoutService,
|
this.vaultTimeoutService,
|
||||||
@ -1245,7 +1220,6 @@ export default class MainBackground {
|
|||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!this.popupOnlyContext) {
|
|
||||||
this.mainContextMenuHandler = new MainContextMenuHandler(
|
this.mainContextMenuHandler = new MainContextMenuHandler(
|
||||||
this.stateService,
|
this.stateService,
|
||||||
this.autofillSettingsService,
|
this.autofillSettingsService,
|
||||||
@ -1268,7 +1242,6 @@ export default class MainBackground {
|
|||||||
chrome.webRequest,
|
chrome.webRequest,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.keyService);
|
this.userAutoUnlockKeyService = new UserAutoUnlockKeyService(this.keyService);
|
||||||
|
|
||||||
@ -1282,7 +1255,7 @@ export default class MainBackground {
|
|||||||
this.containerService.attachToGlobal(self);
|
this.containerService.attachToGlobal(self);
|
||||||
|
|
||||||
// Only the "true" background should run migrations
|
// Only the "true" background should run migrations
|
||||||
await this.stateService.init({ runMigrations: !this.popupOnlyContext });
|
await this.stateService.init({ runMigrations: true });
|
||||||
|
|
||||||
// This is here instead of in in the InitService b/c we don't plan for
|
// This is here instead of in in the InitService b/c we don't plan for
|
||||||
// side effects to run in the Browser InitService.
|
// side effects to run in the Browser InitService.
|
||||||
@ -1304,10 +1277,6 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.popupViewCacheBackgroundService.startObservingTabChanges();
|
this.popupViewCacheBackgroundService.startObservingTabChanges();
|
||||||
|
|
||||||
if (this.popupOnlyContext) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.vaultTimeoutService.init(true);
|
await this.vaultTimeoutService.init(true);
|
||||||
this.fido2Background.init();
|
this.fido2Background.init();
|
||||||
await this.runtimeBackground.init();
|
await this.runtimeBackground.init();
|
||||||
@ -1636,7 +1605,6 @@ export default class MainBackground {
|
|||||||
*/
|
*/
|
||||||
async initOverlayAndTabsBackground() {
|
async initOverlayAndTabsBackground() {
|
||||||
if (
|
if (
|
||||||
this.popupOnlyContext ||
|
|
||||||
this.overlayBackground ||
|
this.overlayBackground ||
|
||||||
this.tabsBackground ||
|
this.tabsBackground ||
|
||||||
(await firstValueFrom(this.authService.activeAccountStatus$)) ===
|
(await firstValueFrom(this.authService.activeAccountStatus$)) ===
|
||||||
@ -1677,6 +1645,7 @@ export default class MainBackground {
|
|||||||
this.fido2ActiveRequestManager,
|
this.fido2ActiveRequestManager,
|
||||||
inlineMenuFieldQualificationService,
|
inlineMenuFieldQualificationService,
|
||||||
this.themeStateService,
|
this.themeStateService,
|
||||||
|
this.totpService,
|
||||||
() => this.generatePassword(),
|
() => this.generatePassword(),
|
||||||
(password) => this.addPasswordToHistory(password),
|
(password) => this.addPasswordToHistory(password),
|
||||||
);
|
);
|
||||||
|
@ -274,7 +274,11 @@ export class NativeMessagingBackground {
|
|||||||
let message = rawMessage as ReceiveMessage;
|
let message = rawMessage as ReceiveMessage;
|
||||||
if (!this.platformUtilsService.isSafari()) {
|
if (!this.platformUtilsService.isSafari()) {
|
||||||
message = JSON.parse(
|
message = JSON.parse(
|
||||||
await this.encryptService.decryptToUtf8(rawMessage as EncString, this.sharedSecret),
|
await this.encryptService.decryptToUtf8(
|
||||||
|
rawMessage as EncString,
|
||||||
|
this.sharedSecret,
|
||||||
|
"ipc-desktop-ipc-channel-key",
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "2024.10.1",
|
"version": "2024.11.0",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Bitwarden Inc.",
|
"author": "Bitwarden Inc.",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"minimum_chrome_version": "102.0",
|
"minimum_chrome_version": "102.0",
|
||||||
"name": "__MSG_extName__",
|
"name": "__MSG_extName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "2024.10.1",
|
"version": "2024.11.0",
|
||||||
"description": "__MSG_extDesc__",
|
"description": "__MSG_extDesc__",
|
||||||
"default_locale": "en",
|
"default_locale": "en",
|
||||||
"author": "Bitwarden Inc.",
|
"author": "Bitwarden Inc.",
|
||||||
|
@ -2,29 +2,6 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-l
|
|||||||
|
|
||||||
import MainBackground from "../background/main.background";
|
import MainBackground from "../background/main.background";
|
||||||
|
|
||||||
import { BrowserApi } from "./browser/browser-api";
|
|
||||||
|
|
||||||
const logService = new ConsoleLogService(false);
|
const logService = new ConsoleLogService(false);
|
||||||
if (BrowserApi.isManifestVersion(3)) {
|
|
||||||
startHeartbeat().catch((error) => logService.error(error));
|
|
||||||
}
|
|
||||||
const bitwardenMain = ((self as any).bitwardenMain = new MainBackground());
|
const bitwardenMain = ((self as any).bitwardenMain = new MainBackground());
|
||||||
bitwardenMain.bootstrap().catch((error) => logService.error(error));
|
bitwardenMain.bootstrap().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));
|
|
||||||
}
|
|
||||||
|
@ -58,11 +58,33 @@ export class BrowserApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async createWindow(options: chrome.windows.CreateData): Promise<chrome.windows.Window> {
|
static async createWindow(options: chrome.windows.CreateData): Promise<chrome.windows.Window> {
|
||||||
return new Promise((resolve) =>
|
return new Promise((resolve) => {
|
||||||
chrome.windows.create(options, (window) => {
|
chrome.windows.create(options, async (newWindow) => {
|
||||||
resolve(window);
|
if (!BrowserApi.isSafariApi) {
|
||||||
}),
|
return resolve(newWindow);
|
||||||
);
|
}
|
||||||
|
// Safari doesn't close the default extension popup when a new window is created so we need to
|
||||||
|
// manually trigger the close by focusing the main window after the new window is created
|
||||||
|
const allWindows = await new Promise<chrome.windows.Window[]>((resolve) => {
|
||||||
|
chrome.windows.getAll({ windowTypes: ["normal"] }, (windows) => resolve(windows));
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainWindow = allWindows.find((window) => window.id !== newWindow.id);
|
||||||
|
|
||||||
|
// No main window found, resolve the new window
|
||||||
|
if (mainWindow == null || !mainWindow.id) {
|
||||||
|
return resolve(newWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus the main window to close the extension popup
|
||||||
|
chrome.windows.update(mainWindow.id, { focused: true }, () => {
|
||||||
|
// Refocus the newly created window
|
||||||
|
chrome.windows.update(newWindow.id, { focused: true }, () => {
|
||||||
|
resolve(newWindow);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,8 +24,8 @@ import {
|
|||||||
LockComponentService,
|
LockComponentService,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { LockService, LoginEmailService, PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { LockService, LoginEmailService, PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
|
||||||
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
@ -92,9 +92,15 @@ import { InlineDerivedStateProvider } from "@bitwarden/common/platform/state/imp
|
|||||||
import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service";
|
import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service";
|
||||||
import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service";
|
import { WindowStorageService } from "@bitwarden/common/platform/storage/window-storage.service";
|
||||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||||
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
|
import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
|
import { VaultTimeoutStringType } from "@bitwarden/common/types/vault-timeout.type";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderService as FolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
|
||||||
|
import {
|
||||||
|
FolderService as FolderServiceAbstraction,
|
||||||
|
InternalFolderService,
|
||||||
|
} from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
|
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||||
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
@ -107,7 +113,6 @@ import { ExtensionAnonLayoutWrapperDataService } from "../../auth/popup/extensio
|
|||||||
import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service";
|
import { ExtensionLoginComponentService } from "../../auth/popup/login/extension-login-component.service";
|
||||||
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
import { AutofillService as AutofillServiceAbstraction } from "../../autofill/services/abstractions/autofill.service";
|
||||||
import AutofillService from "../../autofill/services/autofill.service";
|
import AutofillService from "../../autofill/services/autofill.service";
|
||||||
import MainBackground from "../../background/main.background";
|
|
||||||
import { ForegroundBrowserBiometricsService } from "../../key-management/biometrics/foreground-browser-biometrics";
|
import { ForegroundBrowserBiometricsService } from "../../key-management/biometrics/foreground-browser-biometrics";
|
||||||
import { BrowserKeyService } from "../../key-management/browser-key.service";
|
import { BrowserKeyService } from "../../key-management/browser-key.service";
|
||||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||||
@ -117,12 +122,12 @@ import { ChromeMessageSender } from "../../platform/messaging/chrome-message.sen
|
|||||||
/* eslint-enable no-restricted-imports */
|
/* eslint-enable no-restricted-imports */
|
||||||
import { OffscreenDocumentService } from "../../platform/offscreen-document/abstractions/offscreen-document";
|
import { OffscreenDocumentService } from "../../platform/offscreen-document/abstractions/offscreen-document";
|
||||||
import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service";
|
import { DefaultOffscreenDocumentService } from "../../platform/offscreen-document/offscreen-document.service";
|
||||||
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
|
|
||||||
import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service";
|
import { BrowserFileDownloadService } from "../../platform/popup/services/browser-file-download.service";
|
||||||
import { PopupViewCacheService } from "../../platform/popup/view-cache/popup-view-cache.service";
|
import { PopupViewCacheService } from "../../platform/popup/view-cache/popup-view-cache.service";
|
||||||
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
|
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
|
||||||
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
|
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
|
||||||
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
|
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
|
||||||
|
import BrowserMemoryStorageService from "../../platform/services/browser-memory-storage.service";
|
||||||
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
|
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
|
||||||
import I18nService from "../../platform/services/i18n.service";
|
import I18nService from "../../platform/services/i18n.service";
|
||||||
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
|
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
|
||||||
@ -130,6 +135,7 @@ import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk
|
|||||||
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
|
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
|
||||||
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
|
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
|
||||||
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
|
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
|
||||||
|
import { ForegroundSyncService } from "../../platform/sync/foreground-sync.service";
|
||||||
import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging";
|
import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging";
|
||||||
import { ExtensionLockComponentService } from "../../services/extension-lock-component.service";
|
import { ExtensionLockComponentService } from "../../services/extension-lock-component.service";
|
||||||
import { ForegroundVaultTimeoutService } from "../../services/vault-timeout/foreground-vault-timeout.service";
|
import { ForegroundVaultTimeoutService } from "../../services/vault-timeout/foreground-vault-timeout.service";
|
||||||
@ -151,26 +157,6 @@ const DISK_BACKUP_LOCAL_STORAGE = new SafeInjectionToken<
|
|||||||
AbstractStorageService & ObservableStorageService
|
AbstractStorageService & ObservableStorageService
|
||||||
>("DISK_BACKUP_LOCAL_STORAGE");
|
>("DISK_BACKUP_LOCAL_STORAGE");
|
||||||
|
|
||||||
const needsBackgroundInit = BrowserPopupUtils.backgroundInitializationRequired();
|
|
||||||
const mainBackground: MainBackground = needsBackgroundInit
|
|
||||||
? createLocalBgService()
|
|
||||||
: BrowserApi.getBackgroundPage().bitwardenMain;
|
|
||||||
|
|
||||||
function createLocalBgService() {
|
|
||||||
const localBgService = new MainBackground(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
|
|
||||||
localBgService.bootstrap();
|
|
||||||
return localBgService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated This method needs to be removed as part of MV3 conversion. Please do not add more and actively try to remove usages */
|
|
||||||
function getBgService<T>(service: keyof MainBackground) {
|
|
||||||
return (): T => {
|
|
||||||
return mainBackground ? (mainBackground[service] as any as T) : null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider definitions used in the ngModule.
|
* Provider definitions used in the ngModule.
|
||||||
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
|
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
|
||||||
@ -307,8 +293,23 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: SyncService,
|
provide: SyncService,
|
||||||
useFactory: getBgService<SyncService>("syncService"),
|
useClass: ForegroundSyncService,
|
||||||
deps: [],
|
deps: [
|
||||||
|
StateService,
|
||||||
|
InternalFolderService,
|
||||||
|
FolderApiServiceAbstraction,
|
||||||
|
MessageSender,
|
||||||
|
LogService,
|
||||||
|
CipherService,
|
||||||
|
CollectionService,
|
||||||
|
ApiService,
|
||||||
|
AccountServiceAbstraction,
|
||||||
|
AuthService,
|
||||||
|
InternalSendService,
|
||||||
|
SendApiService,
|
||||||
|
MessageListener,
|
||||||
|
StateProvider,
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: DomainSettingsService,
|
provide: DomainSettingsService,
|
||||||
@ -358,11 +359,6 @@ const safeProviders: SafeProvider[] = [
|
|||||||
useClass: ForegroundVaultTimeoutService,
|
useClass: ForegroundVaultTimeoutService,
|
||||||
deps: [MessagingServiceAbstraction],
|
deps: [MessagingServiceAbstraction],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: NotificationsService,
|
|
||||||
useFactory: getBgService<NotificationsService>("notificationsService"),
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: VaultFilterService,
|
provide: VaultFilterService,
|
||||||
useClass: VaultFilterService,
|
useClass: VaultFilterService,
|
||||||
@ -382,8 +378,8 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: MEMORY_STORAGE,
|
provide: MEMORY_STORAGE,
|
||||||
useFactory: getBgService<AbstractStorageService>("memoryStorageService"),
|
useFactory: (memoryStorage: AbstractStorageService) => memoryStorage,
|
||||||
deps: [],
|
deps: [OBSERVABLE_MEMORY_STORAGE],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: OBSERVABLE_MEMORY_STORAGE,
|
provide: OBSERVABLE_MEMORY_STORAGE,
|
||||||
@ -392,9 +388,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
return new ForegroundMemoryStorageService();
|
return new ForegroundMemoryStorageService();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBgService<AbstractStorageService & ObservableStorageService>(
|
return new BrowserMemoryStorageService();
|
||||||
"memoryStorageForStateProviders",
|
|
||||||
)();
|
|
||||||
},
|
},
|
||||||
deps: [],
|
deps: [],
|
||||||
}),
|
}),
|
||||||
@ -407,9 +401,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
return regularMemoryStorageService;
|
return regularMemoryStorageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getBgService<AbstractStorageService & ObservableStorageService>(
|
return new ForegroundMemoryStorageService();
|
||||||
"largeObjectMemoryStorageForStateProviders",
|
|
||||||
)();
|
|
||||||
},
|
},
|
||||||
deps: [OBSERVABLE_MEMORY_STORAGE],
|
deps: [OBSERVABLE_MEMORY_STORAGE],
|
||||||
}),
|
}),
|
||||||
@ -494,15 +486,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: INTRAPROCESS_MESSAGING_SUBJECT,
|
provide: INTRAPROCESS_MESSAGING_SUBJECT,
|
||||||
useFactory: () => {
|
useFactory: () => new Subject<Message<Record<string, unknown>>>(),
|
||||||
if (BrowserPopupUtils.backgroundInitializationRequired()) {
|
|
||||||
// There is no persistent main background which means we have one in memory,
|
|
||||||
// we need the same instance that our in memory background is utilizing.
|
|
||||||
return getBgService("intraprocessMessagingSubject")();
|
|
||||||
} else {
|
|
||||||
return new Subject<Message<Record<string, unknown>>>();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deps: [],
|
deps: [],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
@ -514,23 +498,6 @@ const safeProviders: SafeProvider[] = [
|
|||||||
),
|
),
|
||||||
deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService],
|
deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
|
||||||
provide: INTRAPROCESS_MESSAGING_SUBJECT,
|
|
||||||
useFactory: () => {
|
|
||||||
if (needsBackgroundInit) {
|
|
||||||
// We will have created a popup within this context, in that case
|
|
||||||
// we want to make sure we have the same subject as that context so we
|
|
||||||
// can message with it.
|
|
||||||
return getBgService("intraprocessMessagingSubject")();
|
|
||||||
} else {
|
|
||||||
// There isn't a locally created background so we will communicate with
|
|
||||||
// the true background through chrome apis, in that case, we can just create
|
|
||||||
// one for ourself.
|
|
||||||
return new Subject<Message<Record<string, unknown>>>();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deps: [],
|
|
||||||
}),
|
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: DISK_BACKUP_LOCAL_STORAGE,
|
provide: DISK_BACKUP_LOCAL_STORAGE,
|
||||||
useFactory: (diskStorage: AbstractStorageService & ObservableStorageService) =>
|
useFactory: (diskStorage: AbstractStorageService & ObservableStorageService) =>
|
||||||
@ -572,13 +539,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: ForegroundTaskSchedulerService,
|
provide: ForegroundTaskSchedulerService,
|
||||||
useFactory: (logService: LogService, stateProvider: StateProvider) => {
|
useClass: ForegroundTaskSchedulerService,
|
||||||
if (needsBackgroundInit) {
|
|
||||||
return getBgService<ForegroundTaskSchedulerService>("taskSchedulerService")();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ForegroundTaskSchedulerService(logService, stateProvider);
|
|
||||||
},
|
|
||||||
deps: [LogService, StateProvider],
|
deps: [LogService, StateProvider],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
@ -24,9 +24,9 @@ import {
|
|||||||
SendFormConfig,
|
SendFormConfig,
|
||||||
SendFormConfigService,
|
SendFormConfigService,
|
||||||
SendFormMode,
|
SendFormMode,
|
||||||
|
SendFormModule,
|
||||||
} from "@bitwarden/send-ui";
|
} from "@bitwarden/send-ui";
|
||||||
|
|
||||||
import { SendFormModule } from "../../../../../../../libs/tools/send/send-ui/src/send-form/send-form.module";
|
|
||||||
import { PopupFooterComponent } from "../../../../platform/popup/layout/popup-footer.component";
|
import { PopupFooterComponent } from "../../../../platform/popup/layout/popup-footer.component";
|
||||||
import { PopupHeaderComponent } from "../../../../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../../../../platform/popup/layout/popup-header.component";
|
||||||
import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component";
|
import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page.component";
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Name" xml:space="preserve">
|
<data name="Name" xml:space="preserve">
|
||||||
<value>Bitwarden Password Manager</value>
|
<value>Bitwarden passordbehandler</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Summary" xml:space="preserve">
|
<data name="Summary" xml:space="preserve">
|
||||||
<value>At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.</value>
|
<value>At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information.</value>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/cli",
|
"name": "@bitwarden/cli",
|
||||||
"description": "A secure and free password manager for all of your devices.",
|
"description": "A secure and free password manager for all of your devices.",
|
||||||
"version": "2024.10.0",
|
"version": "2024.11.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitwarden",
|
"bitwarden",
|
||||||
"password",
|
"password",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/multer": "3.0.2",
|
"@koa/multer": "3.0.2",
|
||||||
"@koa/router": "13.1.0",
|
"@koa/router": "13.1.0",
|
||||||
"argon2": "0.40.1",
|
"argon2": "0.41.1",
|
||||||
"big-integer": "1.6.52",
|
"big-integer": "1.6.52",
|
||||||
"browser-hrtime": "1.1.8",
|
"browser-hrtime": "1.1.8",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
"proper-lockfile": "4.1.2",
|
"proper-lockfile": "4.1.2",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.1",
|
||||||
"tldts": "6.1.56",
|
"tldts": "6.1.58",
|
||||||
"zxcvbn": "4.4.2"
|
"zxcvbn": "4.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ export class UnlockCommand {
|
|||||||
return Response.error(e.message);
|
return Response.error(e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
|
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
|
||||||
await this.keyService.setUserKey(userKey, userId);
|
await this.keyService.setUserKey(userKey, userId);
|
||||||
|
|
||||||
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
if (await this.keyConnectorService.getConvertAccountRequired()) {
|
||||||
|
@ -404,6 +404,7 @@ export class ServiceContainer {
|
|||||||
this.stateService,
|
this.stateService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
|
this.logService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
this.kdfConfigService = new KdfConfigService(this.stateProvider);
|
||||||
|
204
apps/desktop/desktop_native/Cargo.lock
generated
204
apps/desktop/desktop_native/Cargo.lock
generated
@ -4,18 +4,18 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.22.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler2"
|
||||||
version = "1.0.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes"
|
name = "aes"
|
||||||
@ -39,9 +39,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.86"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
@ -216,17 +216,17 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.73"
|
version = "0.3.74"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -289,9 +289,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.2"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cbc"
|
name = "cbc"
|
||||||
@ -304,9 +304,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.28"
|
version = "1.1.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
checksum = "67b9470d453346108f93a59222a9a1a5724db32d0a4727b7ab7ace4b4d822dc9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@ -439,9 +439,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.128"
|
version = "1.0.129"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4"
|
checksum = "cbdc8cca144dce1c4981b5c9ab748761619979e515c3d53b5df385c677d1d007"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cxxbridge-flags",
|
"cxxbridge-flags",
|
||||||
@ -451,9 +451,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx-build"
|
name = "cxx-build"
|
||||||
version = "1.0.128"
|
version = "1.0.129"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c77953e99f01508f89f55c494bfa867171ef3a6c8cea03d26975368f2121a5c1"
|
checksum = "c5764c3142ab44fcf857101d12c0ddf09c34499900557c764f5ad0597159d1fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
@ -466,15 +466,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-flags"
|
name = "cxxbridge-flags"
|
||||||
version = "1.0.128"
|
version = "1.0.129"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6"
|
checksum = "d422aff542b4fa28c2ce8e5cc202d42dbf24702345c1fba3087b2d3f8a1b90ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxxbridge-macro"
|
name = "cxxbridge-macro"
|
||||||
version = "1.0.128"
|
version = "1.0.129"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60"
|
checksum = "a1719100f31492cd6adeeab9a0f46cdbc846e615fdb66d7b398aa46ec7fdd06f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -546,6 +546,7 @@ dependencies = [
|
|||||||
"napi-derive",
|
"napi-derive",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
"windows-registry",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -758,9 +759,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
version = "2.3.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -843,9 +844,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.29.0"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio"
|
name = "gio"
|
||||||
@ -936,9 +937,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@ -964,15 +965,6 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "home"
|
|
||||||
version = "0.5.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@ -1141,29 +1133,30 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.4"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.11"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.9",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi"
|
name = "napi"
|
||||||
version = "2.16.11"
|
version = "2.16.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53575dfa17f208dd1ce3a2da2da4659aae393b256a472f2738a8586a6c4107fd"
|
checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"ctor",
|
"ctor",
|
||||||
@ -1258,16 +1251,6 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_cpus"
|
|
||||||
version = "1.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.9",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_threads"
|
name = "num_threads"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@ -1458,9 +1441,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
@ -1526,9 +1509,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.87"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -1609,9 +1592,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.11.0"
|
version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -1653,9 +1636,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.34"
|
version = "0.38.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
@ -1713,18 +1696,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.210"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.210"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1832,9 +1815,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1862,9 +1845,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.12.0"
|
version = "3.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
@ -1884,18 +1867,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.61"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.61"
|
version = "1.0.68"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1937,26 +1920,25 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.38.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.3.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2043,12 +2025,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tree_magic_mini"
|
name = "tree_magic_mini"
|
||||||
version = "3.1.5"
|
version = "3.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469a727cac55b41448315cc10427c069c618ac59bb6a4480283fcd811749bdc2"
|
checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"home",
|
|
||||||
"memchr",
|
"memchr",
|
||||||
"nom",
|
"nom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -2124,9 +2105,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-client"
|
name = "wayland-client"
|
||||||
version = "0.31.6"
|
version = "0.31.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d"
|
checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"rustix",
|
"rustix",
|
||||||
@ -2236,7 +2217,7 @@ checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
"windows-result",
|
"windows-result 0.1.2",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2262,6 +2243,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a"
|
||||||
|
dependencies = [
|
||||||
|
"windows-result 0.2.0",
|
||||||
|
"windows-strings",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -2271,6 +2263,24 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "978d65aedf914c664c510d9de43c8fd85ca745eaff1ed53edf409b479e441663"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.48.0"
|
||||||
@ -2477,9 +2487,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "4.3.1"
|
version = "4.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "851238c133804e0aa888edf4a0229481c753544ca12a60fd1c3230c8a500fe40"
|
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
@ -2515,9 +2525,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus_macros"
|
name = "zbus_macros"
|
||||||
version = "4.3.1"
|
version = "4.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d5a3f12c20bd473be3194af6b49d50d7bb804ef3192dc70eddedb26b85d9da7"
|
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -2573,9 +2583,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant"
|
name = "zvariant"
|
||||||
version = "4.1.2"
|
version = "4.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9"
|
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"endi",
|
"endi",
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
@ -2586,9 +2596,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_derive"
|
name = "zvariant_derive"
|
||||||
version = "4.1.2"
|
version = "4.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859"
|
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -2599,9 +2609,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zvariant_utils"
|
name = "zvariant_utils"
|
||||||
version = "2.0.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786"
|
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -23,7 +23,7 @@ sys = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = "=0.8.4"
|
aes = "=0.8.4"
|
||||||
anyhow = "=1.0.86"
|
anyhow = "=1.0.93"
|
||||||
arboard = { version = "=3.4.1", default-features = false, features = [
|
arboard = { version = "=3.4.1", default-features = false, features = [
|
||||||
"wayland-data-control",
|
"wayland-data-control",
|
||||||
] }
|
] }
|
||||||
@ -38,8 +38,8 @@ rand = "=0.8.5"
|
|||||||
retry = "=2.0.0"
|
retry = "=2.0.0"
|
||||||
scopeguard = "=1.2.0"
|
scopeguard = "=1.2.0"
|
||||||
sha2 = "=0.10.8"
|
sha2 = "=0.10.8"
|
||||||
thiserror = "=1.0.61"
|
thiserror = "=1.0.68"
|
||||||
tokio = { version = "=1.38.0", features = ["io-util", "sync", "macros"] }
|
tokio = { version = "=1.41.0", features = ["io-util", "sync", "macros"] }
|
||||||
tokio-util = "=0.7.12"
|
tokio-util = "=0.7.12"
|
||||||
typenum = "=1.17.0"
|
typenum = "=1.17.0"
|
||||||
|
|
||||||
@ -68,5 +68,5 @@ security-framework-sys = { version = "=2.11.0", optional = true }
|
|||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
gio = { version = "=0.19.5", optional = true }
|
gio = { version = "=0.19.5", optional = true }
|
||||||
libsecret = { version = "=0.5.0", optional = true }
|
libsecret = { version = "=0.5.0", optional = true }
|
||||||
zbus = { version = "=4.3.1", optional = true }
|
zbus = { version = "=4.4.0", optional = true }
|
||||||
zbus_polkit = { version = "=4.0.0", optional = true }
|
zbus_polkit = { version = "=4.0.0", optional = true }
|
||||||
|
@ -31,7 +31,7 @@ pub fn path(name: &str) -> std::path::PathBuf {
|
|||||||
format!(r"\\.\pipe\{hash_b64}.app.{name}").into()
|
format!(r"\\.\pipe\{hash_b64}.app.{name}").into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(all(target_os = "macos", not(debug_assertions)))]
|
||||||
{
|
{
|
||||||
let mut home = dirs::home_dir().unwrap();
|
let mut home = dirs::home_dir().unwrap();
|
||||||
|
|
||||||
@ -53,6 +53,13 @@ pub fn path(name: &str) -> std::path::PathBuf {
|
|||||||
tmp.join(format!("app.{name}"))
|
tmp.join(format!("app.{name}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_os = "macos", debug_assertions))]
|
||||||
|
{
|
||||||
|
// When running in debug mode, we use the tmp dir because the app is not sandboxed
|
||||||
|
let dir = std::env::temp_dir();
|
||||||
|
dir.join(format!("app.{name}"))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
// On Linux, we use the user's cache directory.
|
// On Linux, we use the user's cache directory.
|
||||||
|
@ -14,12 +14,15 @@ default = []
|
|||||||
manual_test = []
|
manual_test = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "=1.0.86"
|
anyhow = "=1.0.93"
|
||||||
desktop_core = { path = "../core" }
|
desktop_core = { path = "../core" }
|
||||||
napi = { version = "=2.16.11", features = ["async"] }
|
napi = { version = "=2.16.13", features = ["async"] }
|
||||||
napi-derive = "=2.16.12"
|
napi-derive = "=2.16.12"
|
||||||
tokio = { version = "1.38.0" }
|
tokio = { version = "1.38.0" }
|
||||||
tokio-util = "0.7.11"
|
tokio-util = "0.7.11"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows-registry = "=0.3.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "=2.1.3"
|
napi-build = "=2.1.3"
|
||||||
|
4
apps/desktop/desktop_native/napi/index.d.ts
vendored
4
apps/desktop/desktop_native/napi/index.d.ts
vendored
@ -51,6 +51,10 @@ export declare namespace powermonitors {
|
|||||||
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
||||||
export function isLockMonitorAvailable(): Promise<boolean>
|
export function isLockMonitorAvailable(): Promise<boolean>
|
||||||
}
|
}
|
||||||
|
export declare namespace windows_registry {
|
||||||
|
export function createKey(key: string, subkey: string, value: string): Promise<void>
|
||||||
|
export function deleteKey(key: string, subkey: string): Promise<void>
|
||||||
|
}
|
||||||
export declare namespace ipc {
|
export declare namespace ipc {
|
||||||
export interface IpcMessage {
|
export interface IpcMessage {
|
||||||
clientId: number
|
clientId: number
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
||||||
|
|
||||||
|
mod registry;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub mod passwords {
|
pub mod passwords {
|
||||||
/// Fetch the stored password from the keychain.
|
/// Fetch the stored password from the keychain.
|
||||||
@ -190,6 +193,21 @@ pub mod powermonitors {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub mod windows_registry {
|
||||||
|
#[napi]
|
||||||
|
pub async fn create_key(key: String, subkey: String, value: String) -> napi::Result<()> {
|
||||||
|
crate::registry::create_key(&key, &subkey, &value)
|
||||||
|
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub async fn delete_key(key: String, subkey: String) -> napi::Result<()> {
|
||||||
|
crate::registry::delete_key(&key, &subkey)
|
||||||
|
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub mod ipc {
|
pub mod ipc {
|
||||||
use desktop_core::ipc::server::{Message, MessageType};
|
use desktop_core::ipc::server::{Message, MessageType};
|
||||||
|
9
apps/desktop/desktop_native/napi/src/registry/dummy.rs
Normal file
9
apps/desktop/desktop_native/napi/src/registry/dummy.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
pub fn create_key(_key: &str, _subkey: &str, _value: &str) -> Result<()> {
|
||||||
|
bail!("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_key(_key: &str, _subkey: &str) -> Result<()> {
|
||||||
|
bail!("Not implemented")
|
||||||
|
}
|
4
apps/desktop/desktop_native/napi/src/registry/mod.rs
Normal file
4
apps/desktop/desktop_native/napi/src/registry/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
||||||
|
#[cfg_attr(not(target_os = "windows"), path = "dummy.rs")]
|
||||||
|
mod internal;
|
||||||
|
pub use internal::*;
|
29
apps/desktop/desktop_native/napi/src/registry/windows.rs
Normal file
29
apps/desktop/desktop_native/napi/src/registry/windows.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
fn convert_key(key: &str) -> Result<&'static windows_registry::Key> {
|
||||||
|
Ok(match key.to_uppercase().as_str() {
|
||||||
|
"HKEY_CURRENT_USER" | "HKCU" => windows_registry::CURRENT_USER,
|
||||||
|
"HKEY_LOCAL_MACHINE" | "HKLM" => windows_registry::LOCAL_MACHINE,
|
||||||
|
"HKEY_CLASSES_ROOT" | "HKCR" => windows_registry::CLASSES_ROOT,
|
||||||
|
_ => bail!("Invalid key"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_key(key: &str, subkey: &str, value: &str) -> Result<()> {
|
||||||
|
let key = convert_key(key)?;
|
||||||
|
|
||||||
|
let subkey = key.create(subkey)?;
|
||||||
|
|
||||||
|
const DEFAULT: &str = "";
|
||||||
|
subkey.set_string(DEFAULT, value)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_key(key: &str, subkey: &str) -> Result<()> {
|
||||||
|
let key = convert_key(key)?;
|
||||||
|
|
||||||
|
key.remove_tree(subkey)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -7,7 +7,7 @@ version = "0.0.0"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "=1.0.86"
|
anyhow = "=1.0.93"
|
||||||
desktop_core = { path = "../core", default-features = false }
|
desktop_core = { path = "../core", default-features = false }
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
|
@ -90,13 +90,6 @@
|
|||||||
"electronUpdaterCompatibility": ">=0.0.1",
|
"electronUpdaterCompatibility": ">=0.0.1",
|
||||||
"target": ["portable", "nsis-web", "appx"],
|
"target": ["portable", "nsis-web", "appx"],
|
||||||
"sign": "./sign.js",
|
"sign": "./sign.js",
|
||||||
"extraResources": [
|
|
||||||
{
|
|
||||||
"from": "../../node_modules/regedit/vbs",
|
|
||||||
"to": "regedit/vbs",
|
|
||||||
"filter": ["**/*"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"extraFiles": [
|
"extraFiles": [
|
||||||
{
|
{
|
||||||
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe",
|
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe",
|
||||||
|
@ -125,9 +125,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.12.1",
|
"version": "8.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
|
@ -219,7 +219,11 @@ export default class NativeMessageService {
|
|||||||
key: string,
|
key: string,
|
||||||
): Promise<DecryptedCommandData> {
|
): Promise<DecryptedCommandData> {
|
||||||
const sharedKey = await this.getSharedKeyForKey(key);
|
const sharedKey = await this.getSharedKeyForKey(key);
|
||||||
const decrypted = await this.encryptService.decryptToUtf8(payload, sharedKey);
|
const decrypted = await this.encryptService.decryptToUtf8(
|
||||||
|
payload,
|
||||||
|
sharedKey,
|
||||||
|
"native-messaging-session",
|
||||||
|
);
|
||||||
|
|
||||||
return JSON.parse(decrypted);
|
return JSON.parse(decrypted);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"description": "A secure and free password manager for all of your devices.",
|
"description": "A secure and free password manager for all of your devices.",
|
||||||
"version": "2024.10.3",
|
"version": "2024.11.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitwarden",
|
"bitwarden",
|
||||||
"password",
|
"password",
|
||||||
|
@ -58,20 +58,24 @@ async function run(context) {
|
|||||||
id = identities[0].id;
|
id = identities[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Signing proxy binary before the main bundle, using identity '${id}'`);
|
console.log(
|
||||||
|
`Signing proxy binary before the main bundle, using identity '${id}', for build ${context.electronPlatformName}`,
|
||||||
|
);
|
||||||
|
|
||||||
const appName = context.packager.appInfo.productFilename;
|
const appName = context.packager.appInfo.productFilename;
|
||||||
const appPath = `${context.appOutDir}/${appName}.app`;
|
const appPath = `${context.appOutDir}/${appName}.app`;
|
||||||
const proxyPath = path.join(appPath, "Contents", "MacOS", "desktop_proxy");
|
const proxyPath = path.join(appPath, "Contents", "MacOS", "desktop_proxy");
|
||||||
|
const inheritProxyPath = path.join(appPath, "Contents", "MacOS", "desktop_proxy.inherit");
|
||||||
|
|
||||||
const packageId = "com.bitwarden.desktop";
|
const packageId = "com.bitwarden.desktop";
|
||||||
|
|
||||||
|
if (is_mas) {
|
||||||
const entitlementsName = "entitlements.desktop_proxy.plist";
|
const entitlementsName = "entitlements.desktop_proxy.plist";
|
||||||
const entitlementsPath = path.join(__dirname, "..", "resources", entitlementsName);
|
const entitlementsPath = path.join(__dirname, "..", "resources", entitlementsName);
|
||||||
child_process.execSync(
|
child_process.execSync(
|
||||||
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${entitlementsPath} ${proxyPath}`,
|
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${entitlementsPath} ${proxyPath}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const inheritProxyPath = path.join(appPath, "Contents", "MacOS", "desktop_proxy.inherit");
|
|
||||||
const inheritEntitlementsName = "entitlements.desktop_proxy.inherit.plist";
|
const inheritEntitlementsName = "entitlements.desktop_proxy.inherit.plist";
|
||||||
const inheritEntitlementsPath = path.join(
|
const inheritEntitlementsPath = path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
@ -82,6 +86,18 @@ async function run(context) {
|
|||||||
child_process.execSync(
|
child_process.execSync(
|
||||||
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${inheritEntitlementsPath} ${inheritProxyPath}`,
|
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${inheritEntitlementsPath} ${inheritProxyPath}`,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// For non-Appstore builds, we don't need the inherit binary as they are not sandboxed,
|
||||||
|
// but we sign and include it anyway for consistency. It should be removed once DDG supports the proxy directly.
|
||||||
|
const entitlementsName = "entitlements.mac.plist";
|
||||||
|
const entitlementsPath = path.join(__dirname, "..", "resources", entitlementsName);
|
||||||
|
child_process.execSync(
|
||||||
|
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${entitlementsPath} ${proxyPath}`,
|
||||||
|
);
|
||||||
|
child_process.execSync(
|
||||||
|
`codesign -s '${id}' -i ${packageId} -f --timestamp --options runtime --entitlements ${entitlementsPath} ${inheritProxyPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +626,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
async saveBrowserIntegration() {
|
async saveBrowserIntegration() {
|
||||||
if (
|
if (
|
||||||
ipc.platform.deviceType === DeviceType.MacOsDesktop &&
|
ipc.platform.deviceType === DeviceType.MacOsDesktop &&
|
||||||
!this.platformUtilsService.isMacAppStore()
|
!this.platformUtilsService.isMacAppStore() &&
|
||||||
|
!ipc.platform.isDev
|
||||||
) {
|
) {
|
||||||
await this.dialogService.openSimpleDialog({
|
await this.dialogService.openSimpleDialog({
|
||||||
title: { key: "browserIntegrationUnsupportedTitle" },
|
title: { key: "browserIntegrationUnsupportedTitle" },
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, NgZone, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, NgZone, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Subject, takeUntil } from "rxjs";
|
import { Subject, takeUntil, tap } from "rxjs";
|
||||||
|
|
||||||
import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component";
|
import { LoginComponentV1 as BaseLoginComponent } from "@bitwarden/angular/auth/components/login-v1.component";
|
||||||
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
|
import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service";
|
||||||
@ -14,8 +14,10 @@ import {
|
|||||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||||
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
|
||||||
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@ -76,6 +78,7 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
|
|||||||
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
||||||
registerRouteService: RegisterRouteService,
|
registerRouteService: RegisterRouteService,
|
||||||
toastService: ToastService,
|
toastService: ToastService,
|
||||||
|
private configService: ConfigService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
devicesApiService,
|
devicesApiService,
|
||||||
@ -105,6 +108,8 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
this.listenForUnauthUiRefreshFlagChanges();
|
||||||
|
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
await this.getLoginWithDevice(this.loggedEmail);
|
await this.getLoginWithDevice(this.loggedEmail);
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
@ -137,6 +142,29 @@ export class LoginComponentV1 extends BaseLoginComponent implements OnInit, OnDe
|
|||||||
this.componentDestroyed$.complete();
|
this.componentDestroyed$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private listenForUnauthUiRefreshFlagChanges() {
|
||||||
|
this.configService
|
||||||
|
.getFeatureFlag$(FeatureFlag.UnauthenticatedExtensionUIRefresh)
|
||||||
|
.pipe(
|
||||||
|
tap(async (flag) => {
|
||||||
|
// If the flag is turned ON, we must force a reload to ensure the correct UI is shown
|
||||||
|
if (flag) {
|
||||||
|
const uniqueQueryParams = {
|
||||||
|
...this.route.queryParams,
|
||||||
|
// adding a unique timestamp to the query params to force a reload
|
||||||
|
t: new Date().getTime().toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.router.navigate(["/"], {
|
||||||
|
queryParams: uniqueQueryParams,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
async settings() {
|
async settings() {
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
EnvironmentComponent,
|
EnvironmentComponent,
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"welcomeBack": {
|
"welcomeBack": {
|
||||||
"message": "Welcome back"
|
"message": "Добродошли назад"
|
||||||
},
|
},
|
||||||
"moveToOrgDesc": {
|
"moveToOrgDesc": {
|
||||||
"message": "Изаберите организацију у коју желите да преместите ову ставку. Пребацивање у организацију преноси власништво над ставком тој организацији. Више нећете бити директни власник ове ставке када буде премештена."
|
"message": "Изаберите организацију у коју желите да преместите ову ставку. Пребацивање у организацију преноси власништво над ставком тој организацији. Више нећете бити директни власник ове ставке када буде премештена."
|
||||||
@ -263,7 +263,7 @@
|
|||||||
"message": "Генерисање лозинке"
|
"message": "Генерисање лозинке"
|
||||||
},
|
},
|
||||||
"generatePassphrase": {
|
"generatePassphrase": {
|
||||||
"message": "Generate passphrase"
|
"message": "Генеришите приступну фразу"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"message": "Тип"
|
"message": "Тип"
|
||||||
@ -401,7 +401,7 @@
|
|||||||
"message": "Копирај лозинку"
|
"message": "Копирај лозинку"
|
||||||
},
|
},
|
||||||
"copyPassphrase": {
|
"copyPassphrase": {
|
||||||
"message": "Copy passphrase",
|
"message": "Копирај приступну фразу",
|
||||||
"description": "Copy passphrase to clipboard"
|
"description": "Copy passphrase to clipboard"
|
||||||
},
|
},
|
||||||
"copyUri": {
|
"copyUri": {
|
||||||
@ -558,7 +558,7 @@
|
|||||||
"message": "Креирај налог"
|
"message": "Креирај налог"
|
||||||
},
|
},
|
||||||
"newToBitwarden": {
|
"newToBitwarden": {
|
||||||
"message": "New to Bitwarden?"
|
"message": "Нови сте у Bitwarden-у?"
|
||||||
},
|
},
|
||||||
"setAStrongPassword": {
|
"setAStrongPassword": {
|
||||||
"message": "Поставите јаку лозинку"
|
"message": "Поставите јаку лозинку"
|
||||||
@ -570,16 +570,16 @@
|
|||||||
"message": "Пријавите се"
|
"message": "Пријавите се"
|
||||||
},
|
},
|
||||||
"logInToBitwarden": {
|
"logInToBitwarden": {
|
||||||
"message": "Log in to Bitwarden"
|
"message": "Пријавите се на Bitwarden"
|
||||||
},
|
},
|
||||||
"logInWithPasskey": {
|
"logInWithPasskey": {
|
||||||
"message": "Log in with passkey"
|
"message": "Пријавите се са приступним кључем"
|
||||||
},
|
},
|
||||||
"loginWithDevice": {
|
"loginWithDevice": {
|
||||||
"message": "Log in with device"
|
"message": "Пријавите се са уређајем"
|
||||||
},
|
},
|
||||||
"useSingleSignOn": {
|
"useSingleSignOn": {
|
||||||
"message": "Use single sign-on"
|
"message": "Употребити једнократну пријаву"
|
||||||
},
|
},
|
||||||
"submit": {
|
"submit": {
|
||||||
"message": "Пошаљи"
|
"message": "Пошаљи"
|
||||||
@ -854,7 +854,7 @@
|
|||||||
"message": "УРЛ Сервера"
|
"message": "УРЛ Сервера"
|
||||||
},
|
},
|
||||||
"selfHostBaseUrl": {
|
"selfHostBaseUrl": {
|
||||||
"message": "Self-host server URL",
|
"message": "УРЛ сервера који се самостално хостује",
|
||||||
"description": "Label for field requesting a self-hosted integration service URL"
|
"description": "Label for field requesting a self-hosted integration service URL"
|
||||||
},
|
},
|
||||||
"apiUrl": {
|
"apiUrl": {
|
||||||
@ -1248,7 +1248,7 @@
|
|||||||
"description": "Copy credit card number"
|
"description": "Copy credit card number"
|
||||||
},
|
},
|
||||||
"copyEmail": {
|
"copyEmail": {
|
||||||
"message": "Copy email"
|
"message": "Копирати имејл"
|
||||||
},
|
},
|
||||||
"copySecurityCode": {
|
"copySecurityCode": {
|
||||||
"message": "Копирај сигурносни код",
|
"message": "Копирај сигурносни код",
|
||||||
@ -1684,10 +1684,10 @@
|
|||||||
"message": "Брисање налога је трајно. Не може се поништити."
|
"message": "Брисање налога је трајно. Не може се поништити."
|
||||||
},
|
},
|
||||||
"cannotDeleteAccount": {
|
"cannotDeleteAccount": {
|
||||||
"message": "Cannot delete account"
|
"message": "Није могуће избрисати налог"
|
||||||
},
|
},
|
||||||
"cannotDeleteAccountDesc": {
|
"cannotDeleteAccountDesc": {
|
||||||
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
|
"message": "Ова радња се не може довршити јер је ваш налог у власништву организације. Обратите се администратору своје организације за додатне детаље."
|
||||||
},
|
},
|
||||||
"accountDeleted": {
|
"accountDeleted": {
|
||||||
"message": "Налог обрисан"
|
"message": "Налог обрисан"
|
||||||
@ -2391,10 +2391,10 @@
|
|||||||
"message": "Генериши име"
|
"message": "Генериши име"
|
||||||
},
|
},
|
||||||
"generateEmail": {
|
"generateEmail": {
|
||||||
"message": "Generate email"
|
"message": "Генеришите имејл"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Value must be between $MIN$ and $MAX$",
|
"message": "Вредност мора бити између $MIN$ и $MAX$",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
@ -2451,11 +2451,11 @@
|
|||||||
"message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања."
|
"message": "Генеришите псеудоним е-поште помоћу екстерне услуге прослеђивања."
|
||||||
},
|
},
|
||||||
"forwarderDomainName": {
|
"forwarderDomainName": {
|
||||||
"message": "Email domain",
|
"message": "Домен имејла",
|
||||||
"description": "Labels the domain name email forwarder service option"
|
"description": "Labels the domain name email forwarder service option"
|
||||||
},
|
},
|
||||||
"forwarderDomainNameHint": {
|
"forwarderDomainNameHint": {
|
||||||
"message": "Choose a domain that is supported by the selected service",
|
"message": "Изаберите домен који подржава изабрана услуга",
|
||||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||||
},
|
},
|
||||||
"forwarderError": {
|
"forwarderError": {
|
||||||
@ -2944,10 +2944,10 @@
|
|||||||
"message": "Приступачни кључ"
|
"message": "Приступачни кључ"
|
||||||
},
|
},
|
||||||
"passkeyNotCopied": {
|
"passkeyNotCopied": {
|
||||||
"message": "Приступачни кључ се неће копирати"
|
"message": "Приступни кључ неће бити копиран"
|
||||||
},
|
},
|
||||||
"passkeyNotCopiedAlert": {
|
"passkeyNotCopiedAlert": {
|
||||||
"message": "Приступачни кључ неће бити копиран на клонирану ставку. Да ли желите да наставите са клонирањем ставке?"
|
"message": "Приступни кључ неће бити копиран на клонирану ставку. Да ли желите да наставите са клонирањем ставке?"
|
||||||
},
|
},
|
||||||
"aliasDomain": {
|
"aliasDomain": {
|
||||||
"message": "Домен алијаса"
|
"message": "Домен алијаса"
|
||||||
@ -3168,10 +3168,10 @@
|
|||||||
"message": "Омогућите хардверско убрзање и поново покрените"
|
"message": "Омогућите хардверско убрзање и поново покрените"
|
||||||
},
|
},
|
||||||
"removePasskey": {
|
"removePasskey": {
|
||||||
"message": "Уклонити приступачни кључ"
|
"message": "Уклонити приступни кључ"
|
||||||
},
|
},
|
||||||
"passkeyRemoved": {
|
"passkeyRemoved": {
|
||||||
"message": "Приступачни кључ је уклоњен"
|
"message": "Приступни кључ је уклоњен"
|
||||||
},
|
},
|
||||||
"errorAssigningTargetCollection": {
|
"errorAssigningTargetCollection": {
|
||||||
"message": "Грешка при додељивању циљне колекције."
|
"message": "Грешка при додељивању циљне колекције."
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"welcomeBack": {
|
"welcomeBack": {
|
||||||
"message": "Welcome back"
|
"message": "欢迎回来"
|
||||||
},
|
},
|
||||||
"moveToOrgDesc": {
|
"moveToOrgDesc": {
|
||||||
"message": "选择一个您想将此项目移至的组织。移动到组织会将该项目的所有权转让给该组织。移动后,您将不再是此项目的直接所有者。"
|
"message": "选择一个您想将此项目移至的组织。移动到组织会将该项目的所有权转让给该组织。移动后,您将不再是此项目的直接所有者。"
|
||||||
@ -263,7 +263,7 @@
|
|||||||
"message": "生成密码"
|
"message": "生成密码"
|
||||||
},
|
},
|
||||||
"generatePassphrase": {
|
"generatePassphrase": {
|
||||||
"message": "Generate passphrase"
|
"message": "生成密码短语"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"message": "类型"
|
"message": "类型"
|
||||||
@ -401,7 +401,7 @@
|
|||||||
"message": "复制密码"
|
"message": "复制密码"
|
||||||
},
|
},
|
||||||
"copyPassphrase": {
|
"copyPassphrase": {
|
||||||
"message": "Copy passphrase",
|
"message": "复制密码短语",
|
||||||
"description": "Copy passphrase to clipboard"
|
"description": "Copy passphrase to clipboard"
|
||||||
},
|
},
|
||||||
"copyUri": {
|
"copyUri": {
|
||||||
@ -558,7 +558,7 @@
|
|||||||
"message": "创建账户"
|
"message": "创建账户"
|
||||||
},
|
},
|
||||||
"newToBitwarden": {
|
"newToBitwarden": {
|
||||||
"message": "New to Bitwarden?"
|
"message": "Bitwarden 新手吗?"
|
||||||
},
|
},
|
||||||
"setAStrongPassword": {
|
"setAStrongPassword": {
|
||||||
"message": "设置强密码"
|
"message": "设置强密码"
|
||||||
@ -570,16 +570,16 @@
|
|||||||
"message": "登录"
|
"message": "登录"
|
||||||
},
|
},
|
||||||
"logInToBitwarden": {
|
"logInToBitwarden": {
|
||||||
"message": "Log in to Bitwarden"
|
"message": "登录到 Bitwarden"
|
||||||
},
|
},
|
||||||
"logInWithPasskey": {
|
"logInWithPasskey": {
|
||||||
"message": "Log in with passkey"
|
"message": "使用通行密钥登录"
|
||||||
},
|
},
|
||||||
"loginWithDevice": {
|
"loginWithDevice": {
|
||||||
"message": "Log in with device"
|
"message": "使用设备登录"
|
||||||
},
|
},
|
||||||
"useSingleSignOn": {
|
"useSingleSignOn": {
|
||||||
"message": "Use single sign-on"
|
"message": "使用单点登录"
|
||||||
},
|
},
|
||||||
"submit": {
|
"submit": {
|
||||||
"message": "提交"
|
"message": "提交"
|
||||||
@ -854,7 +854,7 @@
|
|||||||
"message": "服务器 URL"
|
"message": "服务器 URL"
|
||||||
},
|
},
|
||||||
"selfHostBaseUrl": {
|
"selfHostBaseUrl": {
|
||||||
"message": "Self-host server URL",
|
"message": "自托管服务器 URL",
|
||||||
"description": "Label for field requesting a self-hosted integration service URL"
|
"description": "Label for field requesting a self-hosted integration service URL"
|
||||||
},
|
},
|
||||||
"apiUrl": {
|
"apiUrl": {
|
||||||
@ -1248,7 +1248,7 @@
|
|||||||
"description": "Copy credit card number"
|
"description": "Copy credit card number"
|
||||||
},
|
},
|
||||||
"copyEmail": {
|
"copyEmail": {
|
||||||
"message": "Copy email"
|
"message": "复制电子邮件地址"
|
||||||
},
|
},
|
||||||
"copySecurityCode": {
|
"copySecurityCode": {
|
||||||
"message": "复制安全码",
|
"message": "复制安全码",
|
||||||
@ -1684,10 +1684,10 @@
|
|||||||
"message": "删除账户是永久性操作,无法撤销!"
|
"message": "删除账户是永久性操作,无法撤销!"
|
||||||
},
|
},
|
||||||
"cannotDeleteAccount": {
|
"cannotDeleteAccount": {
|
||||||
"message": "Cannot delete account"
|
"message": "无法删除账户"
|
||||||
},
|
},
|
||||||
"cannotDeleteAccountDesc": {
|
"cannotDeleteAccountDesc": {
|
||||||
"message": "This action cannot be completed because your account is owned by an organization. Contact your organization administrator for additional details."
|
"message": "此操作无法完成,因为您的账户归组织所有。请联系您的组织管理员获取详细信息。"
|
||||||
},
|
},
|
||||||
"accountDeleted": {
|
"accountDeleted": {
|
||||||
"message": "账户已删除"
|
"message": "账户已删除"
|
||||||
@ -2391,10 +2391,10 @@
|
|||||||
"message": "生成用户名"
|
"message": "生成用户名"
|
||||||
},
|
},
|
||||||
"generateEmail": {
|
"generateEmail": {
|
||||||
"message": "Generate email"
|
"message": "生成邮件地址"
|
||||||
},
|
},
|
||||||
"generatorBoundariesHint": {
|
"generatorBoundariesHint": {
|
||||||
"message": "Value must be between $MIN$ and $MAX$",
|
"message": "值必须在 $MIN$ 和 $MAX$ 之间",
|
||||||
"description": "Explains spin box minimum and maximum values to the user",
|
"description": "Explains spin box minimum and maximum values to the user",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"min": {
|
"min": {
|
||||||
@ -2451,11 +2451,11 @@
|
|||||||
"message": "使用外部转发服务生成一个电子邮件别名。"
|
"message": "使用外部转发服务生成一个电子邮件别名。"
|
||||||
},
|
},
|
||||||
"forwarderDomainName": {
|
"forwarderDomainName": {
|
||||||
"message": "Email domain",
|
"message": "邮件域名",
|
||||||
"description": "Labels the domain name email forwarder service option"
|
"description": "Labels the domain name email forwarder service option"
|
||||||
},
|
},
|
||||||
"forwarderDomainNameHint": {
|
"forwarderDomainNameHint": {
|
||||||
"message": "Choose a domain that is supported by the selected service",
|
"message": "选择一个所选服务支持的域名",
|
||||||
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
"description": "Guidance provided for email forwarding services that support multiple email domains."
|
||||||
},
|
},
|
||||||
"forwarderError": {
|
"forwarderError": {
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { existsSync, promises as fs } from "fs";
|
import { existsSync, promises as fs } from "fs";
|
||||||
import { homedir, userInfo } from "os";
|
import { homedir, userInfo } from "os";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as util from "util";
|
|
||||||
|
|
||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
|
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { ipc } from "@bitwarden/desktop-napi";
|
import { ipc, windows_registry } from "@bitwarden/desktop-napi";
|
||||||
|
|
||||||
import { isDev } from "../utils";
|
import { isDev } from "../utils";
|
||||||
|
|
||||||
@ -132,18 +131,7 @@ export class NativeMessagingMain {
|
|||||||
};
|
};
|
||||||
const chromeJson = {
|
const chromeJson = {
|
||||||
...baseJson,
|
...baseJson,
|
||||||
...{
|
allowed_origins: await this.loadChromeIds(),
|
||||||
allowed_origins: [
|
|
||||||
// Chrome extension
|
|
||||||
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
|
|
||||||
// Chrome beta extension
|
|
||||||
"chrome-extension://hccnnhgbibccigepcmlgppchkpfdophk/",
|
|
||||||
// Edge extension
|
|
||||||
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
|
|
||||||
// Opera extension
|
|
||||||
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
@ -153,12 +141,12 @@ export class NativeMessagingMain {
|
|||||||
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
|
||||||
|
|
||||||
const nmhs = this.getWindowsNMHS();
|
const nmhs = this.getWindowsNMHS();
|
||||||
for (const [key, value] of Object.entries(nmhs)) {
|
for (const [name, [key, subkey]] of Object.entries(nmhs)) {
|
||||||
let manifestPath = path.join(destination, "chrome.json");
|
let manifestPath = path.join(destination, "chrome.json");
|
||||||
if (key === "Firefox") {
|
if (name === "Firefox") {
|
||||||
manifestPath = path.join(destination, "firefox.json");
|
manifestPath = path.join(destination, "firefox.json");
|
||||||
}
|
}
|
||||||
await this.createWindowsRegistry(value, manifestPath);
|
await windows_registry.createKey(key, subkey, manifestPath);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -180,35 +168,26 @@ export class NativeMessagingMain {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "linux":
|
case "linux": {
|
||||||
if (existsSync(`${this.homedir()}/.mozilla/`)) {
|
for (const [key, value] of Object.entries(this.getLinuxNMHS())) {
|
||||||
|
if (existsSync(value)) {
|
||||||
|
if (key === "Firefox") {
|
||||||
await this.writeManifest(
|
await this.writeManifest(
|
||||||
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
path.join(value, "native-messaging-hosts", "com.8bit.bitwarden.json"),
|
||||||
firefoxJson,
|
firefoxJson,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (existsSync(`${this.homedir()}/.config/google-chrome/`)) {
|
|
||||||
await this.writeManifest(
|
await this.writeManifest(
|
||||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"),
|
||||||
chromeJson,
|
chromeJson,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) {
|
this.logService.warning(`${key} not found, skipping.`);
|
||||||
await this.writeManifest(
|
|
||||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
|
||||||
chromeJson,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsSync(`${this.homedir()}/.config/chromium/`)) {
|
|
||||||
await this.writeManifest(
|
|
||||||
`${this.homedir()}/.config/chromium/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
|
||||||
chromeJson,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -245,8 +224,8 @@ export class NativeMessagingMain {
|
|||||||
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));
|
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));
|
||||||
|
|
||||||
const nmhs = this.getWindowsNMHS();
|
const nmhs = this.getWindowsNMHS();
|
||||||
for (const [, value] of Object.entries(nmhs)) {
|
for (const [, [key, subkey]] of Object.entries(nmhs)) {
|
||||||
await this.deleteWindowsRegistry(value);
|
await windows_registry.deleteKey(key, subkey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -260,15 +239,18 @@ export class NativeMessagingMain {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "linux": {
|
case "linux": {
|
||||||
|
for (const [key, value] of Object.entries(this.getLinuxNMHS())) {
|
||||||
|
if (key === "Firefox") {
|
||||||
await this.removeIfExists(
|
await this.removeIfExists(
|
||||||
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
|
path.join(value, "native-messaging-hosts", "com.8bit.bitwarden.json"),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
await this.removeIfExists(
|
await this.removeIfExists(
|
||||||
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json"),
|
||||||
);
|
|
||||||
await this.removeIfExists(
|
|
||||||
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -291,11 +273,14 @@ export class NativeMessagingMain {
|
|||||||
|
|
||||||
private getWindowsNMHS() {
|
private getWindowsNMHS() {
|
||||||
return {
|
return {
|
||||||
Firefox: "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
|
Firefox: ["HKCU", "SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||||
Chrome: "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
|
Chrome: ["HKCU", "SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||||
Chromium: "HKCU\\SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden",
|
Chromium: ["HKCU", "SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden"],
|
||||||
// Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well.
|
// Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well.
|
||||||
"Microsoft Edge": "HKCU\\SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
|
"Microsoft Edge": [
|
||||||
|
"HKCU",
|
||||||
|
"SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +302,15 @@ export class NativeMessagingMain {
|
|||||||
/* eslint-enable no-useless-escape */
|
/* eslint-enable no-useless-escape */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getLinuxNMHS() {
|
||||||
|
return {
|
||||||
|
Firefox: `${this.homedir()}/.mozilla/`,
|
||||||
|
Chrome: `${this.homedir()}/.config/google-chrome/`,
|
||||||
|
Chromium: `${this.homedir()}/.config/chromium/`,
|
||||||
|
"Microsoft Edge": `${this.homedir()}/.config/microsoft-edge/`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async writeManifest(destination: string, manifest: object) {
|
private async writeManifest(destination: string, manifest: object) {
|
||||||
this.logService.debug(`Writing manifest: ${destination}`);
|
this.logService.debug(`Writing manifest: ${destination}`);
|
||||||
|
|
||||||
@ -327,6 +321,83 @@ export class NativeMessagingMain {
|
|||||||
await fs.writeFile(destination, JSON.stringify(manifest, null, 2));
|
await fs.writeFile(destination, JSON.stringify(manifest, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async loadChromeIds(): Promise<string[]> {
|
||||||
|
const ids: Set<string> = new Set([
|
||||||
|
// Chrome extension
|
||||||
|
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
|
||||||
|
// Chrome beta extension
|
||||||
|
"chrome-extension://hccnnhgbibccigepcmlgppchkpfdophk/",
|
||||||
|
// Edge extension
|
||||||
|
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
|
||||||
|
// Opera extension
|
||||||
|
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isDev()) {
|
||||||
|
return Array.from(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dev builds of the extension have a different random ID per user, so to make development easier
|
||||||
|
// we try to find the extension IDs from the user's Chrome profiles when we're running in dev mode.
|
||||||
|
let chromePaths: string[];
|
||||||
|
switch (process.platform) {
|
||||||
|
case "darwin": {
|
||||||
|
chromePaths = Object.entries(this.getDarwinNMHS())
|
||||||
|
.filter(([key]) => key !== "Firefox")
|
||||||
|
.map(([, value]) => value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "linux": {
|
||||||
|
chromePaths = Object.entries(this.getLinuxNMHS())
|
||||||
|
.filter(([key]) => key !== "Firefox")
|
||||||
|
.map(([, value]) => value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "win32": {
|
||||||
|
// TODO: Add more supported browsers for Windows?
|
||||||
|
chromePaths = [
|
||||||
|
path.join(process.env.LOCALAPPDATA, "Microsoft", "Edge", "User Data"),
|
||||||
|
path.join(process.env.LOCALAPPDATA, "Google", "Chrome", "User Data"),
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const chromePath of chromePaths) {
|
||||||
|
try {
|
||||||
|
// The chrome profile directories are named "Default", "Profile 1", "Profile 2", etc.
|
||||||
|
const profiles = (await fs.readdir(chromePath)).filter((f) => {
|
||||||
|
const lower = f.toLowerCase();
|
||||||
|
return lower == "default" || lower.startsWith("profile ");
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const profile of profiles) {
|
||||||
|
try {
|
||||||
|
// Read the profile Preferences file and find the extension commands section
|
||||||
|
const prefs = JSON.parse(
|
||||||
|
await fs.readFile(path.join(chromePath, profile, "Preferences"), "utf8"),
|
||||||
|
);
|
||||||
|
const commands: Map<string, any> = prefs.extensions.commands;
|
||||||
|
|
||||||
|
// If one of the commands is autofill_login or generate_password, we know it's probably the Bitwarden extension
|
||||||
|
for (const { command_name, extension } of Object.values(commands)) {
|
||||||
|
if (command_name === "autofill_login" || command_name === "generate_password") {
|
||||||
|
ids.add(`chrome-extension://${extension}/`);
|
||||||
|
this.logService.info(`Found extension from ${chromePath}: ${extension}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.info(`Error reading preferences: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Browser is not installed, we can just skip it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(ids);
|
||||||
|
}
|
||||||
|
|
||||||
private binaryPath() {
|
private binaryPath() {
|
||||||
const ext = process.platform === "win32" ? ".exe" : "";
|
const ext = process.platform === "win32" ? ".exe" : "";
|
||||||
|
|
||||||
@ -350,52 +421,6 @@ export class NativeMessagingMain {
|
|||||||
return path.join(path.dirname(this.exePath), `desktop_proxy${ext}`);
|
return path.join(path.dirname(this.exePath), `desktop_proxy${ext}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRegeditInstance() {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const regedit = require("regedit");
|
|
||||||
regedit.setExternalVBSLocation(path.join(path.dirname(this.exePath), "resources/regedit/vbs"));
|
|
||||||
|
|
||||||
return regedit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createWindowsRegistry(location: string, jsonFile: string) {
|
|
||||||
const regedit = this.getRegeditInstance();
|
|
||||||
|
|
||||||
const createKey = util.promisify(regedit.createKey);
|
|
||||||
const putValue = util.promisify(regedit.putValue);
|
|
||||||
|
|
||||||
this.logService.debug(`Adding registry: ${location}`);
|
|
||||||
|
|
||||||
await createKey(location);
|
|
||||||
|
|
||||||
// Insert path to manifest
|
|
||||||
const obj: any = {};
|
|
||||||
obj[location] = {
|
|
||||||
default: {
|
|
||||||
value: jsonFile,
|
|
||||||
type: "REG_DEFAULT",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return putValue(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async deleteWindowsRegistry(key: string) {
|
|
||||||
const regedit = this.getRegeditInstance();
|
|
||||||
|
|
||||||
const list = util.promisify(regedit.list);
|
|
||||||
const deleteKey = util.promisify(regedit.deleteKey);
|
|
||||||
|
|
||||||
this.logService.debug(`Removing registry: ${key}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await list(key);
|
|
||||||
await deleteKey(key);
|
|
||||||
} catch {
|
|
||||||
this.logService.error(`Unable to delete registry key: ${key}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private homedir() {
|
private homedir() {
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
return userInfo().homedir;
|
return userInfo().homedir;
|
||||||
|
27
apps/desktop/src/package-lock.json
generated
27
apps/desktop/src/package-lock.json
generated
@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2024.10.3",
|
"version": "2024.11.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2024.10.3",
|
"version": "2024.11.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
|
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
|
||||||
"argon2": "0.40.1"
|
"argon2": "0.41.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"../desktop_native/napi": {
|
"../desktop_native/napi": {
|
||||||
@ -35,25 +35,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/argon2": {
|
"node_modules/argon2": {
|
||||||
"version": "0.40.1",
|
"version": "0.41.1",
|
||||||
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz",
|
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz",
|
||||||
"integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==",
|
"integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@phc/format": "^1.0.0",
|
"@phc/format": "^1.0.0",
|
||||||
"node-addon-api": "^7.1.0",
|
"node-addon-api": "^8.1.0",
|
||||||
"node-gyp-build": "^4.8.0"
|
"node-gyp-build": "^4.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.17.0"
|
"node": ">=16.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-addon-api": {
|
"node_modules/node-addon-api": {
|
||||||
"version": "7.1.1",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.1.tgz",
|
||||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
"integrity": "sha512-vmEOvxwiH8tlOcv4SyE8RH34rI5/nWVaigUeAUPawC6f0+HoDthwI0vkMu4tbtsZrXq6QXFfrkhjofzKEs5tpA==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18 || ^20 || >= 21"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-gyp-build": {
|
"node_modules/node-gyp-build": {
|
||||||
"version": "4.8.2",
|
"version": "4.8.2",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"productName": "Bitwarden",
|
"productName": "Bitwarden",
|
||||||
"description": "A secure and free password manager for all of your devices.",
|
"description": "A secure and free password manager for all of your devices.",
|
||||||
"version": "2024.10.3",
|
"version": "2024.11.0",
|
||||||
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
"author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)",
|
||||||
"homepage": "https://bitwarden.com",
|
"homepage": "https://bitwarden.com",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
@ -13,6 +13,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
|
"@bitwarden/desktop-napi": "file:../desktop_native/napi",
|
||||||
"argon2": "0.40.1"
|
"argon2": "0.41.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,7 @@ export class NativeMessageHandlerService {
|
|||||||
let decryptedResult = await this.encryptService.decryptToUtf8(
|
let decryptedResult = await this.encryptService.decryptToUtf8(
|
||||||
message.encryptedCommand as EncString,
|
message.encryptedCommand as EncString,
|
||||||
this.ddgSharedSecret,
|
this.ddgSharedSecret,
|
||||||
|
"ddg-shared-key",
|
||||||
);
|
);
|
||||||
|
|
||||||
decryptedResult = this.trimNullCharsFromMessage(decryptedResult);
|
decryptedResult = this.trimNullCharsFromMessage(decryptedResult);
|
||||||
|
@ -114,6 +114,7 @@ export class NativeMessagingService {
|
|||||||
await this.encryptService.decryptToUtf8(
|
await this.encryptService.decryptToUtf8(
|
||||||
rawMessage as EncString,
|
rawMessage as EncString,
|
||||||
SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)),
|
SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)),
|
||||||
|
`native-messaging-session-${appId}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/web-vault",
|
"name": "@bitwarden/web-vault",
|
||||||
"version": "2024.10.5",
|
"version": "2024.11.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:oss": "webpack",
|
"build:oss": "webpack",
|
||||||
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
||||||
|
@ -40,9 +40,9 @@
|
|||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
</bit-nav-group>
|
</bit-nav-group>
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
*ngIf="isAccessIntelligenceFeatureEnabled"
|
*ngIf="isRiskInsightsFeatureEnabled"
|
||||||
[text]="'accessIntelligence' | i18n"
|
[text]="'riskInsights' | i18n"
|
||||||
route="access-intelligence"
|
route="risk-insights"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
<bit-nav-group
|
<bit-nav-group
|
||||||
icon="bwi-billing"
|
icon="bwi-billing"
|
||||||
|
@ -51,7 +51,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
|||||||
showPaymentAndHistory$: Observable<boolean>;
|
showPaymentAndHistory$: Observable<boolean>;
|
||||||
hideNewOrgButton$: Observable<boolean>;
|
hideNewOrgButton$: Observable<boolean>;
|
||||||
organizationIsUnmanaged$: Observable<boolean>;
|
organizationIsUnmanaged$: Observable<boolean>;
|
||||||
isAccessIntelligenceFeatureEnabled = false;
|
isRiskInsightsFeatureEnabled = false;
|
||||||
|
|
||||||
private _destroy = new Subject<void>();
|
private _destroy = new Subject<void>();
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
document.body.classList.remove("layout_frontend");
|
document.body.classList.remove("layout_frontend");
|
||||||
|
|
||||||
this.isAccessIntelligenceFeatureEnabled = await this.configService.getFeatureFlag(
|
this.isRiskInsightsFeatureEnabled = await this.configService.getFeatureFlag(
|
||||||
FeatureFlag.AccessIntelligence,
|
FeatureFlag.AccessIntelligence,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -23,15 +23,14 @@
|
|||||||
<bit-tab [label]="'role' | i18n">
|
<bit-tab [label]="'role' | i18n">
|
||||||
<ng-container *ngIf="!editMode">
|
<ng-container *ngIf="!editMode">
|
||||||
<p bitTypography="body1">{{ "inviteUserDesc" | i18n }}</p>
|
<p bitTypography="body1">{{ "inviteUserDesc" | i18n }}</p>
|
||||||
<bit-form-field *ngIf="remainingSeats$ | async as remainingSeats">
|
<bit-form-field>
|
||||||
<bit-label>{{ "email" | i18n }}</bit-label>
|
<bit-label>{{ "email" | i18n }}</bit-label>
|
||||||
<input id="emails" type="text" appAutoFocus bitInput formControlName="emails" />
|
<input id="emails" type="text" appAutoFocus bitInput formControlName="emails" />
|
||||||
<bit-hint *ngIf="remainingSeats > 1; else singleSeat">{{
|
<bit-hint>{{
|
||||||
"inviteMultipleEmailDesc" | i18n: remainingSeats
|
"inviteMultipleEmailDesc"
|
||||||
|
| i18n
|
||||||
|
: (organization.productTierType === ProductTierType.TeamsStarter ? "10" : "20")
|
||||||
}}</bit-hint>
|
}}</bit-hint>
|
||||||
<ng-template #singleSeat>
|
|
||||||
<bit-hint>{{ "inviteSingleEmailDesc" | i18n: remainingSeats }}</bit-hint>
|
|
||||||
</ng-template>
|
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<bit-radio-group formControlName="type">
|
<bit-radio-group formControlName="type">
|
||||||
@ -265,6 +264,16 @@
|
|||||||
<button
|
<button
|
||||||
*ngIf="editMode"
|
*ngIf="editMode"
|
||||||
type="button"
|
type="button"
|
||||||
|
bitIconButton="bwi-close"
|
||||||
|
buttonType="danger"
|
||||||
|
bitFormButton
|
||||||
|
[appA11yTitle]="'remove' | i18n"
|
||||||
|
[bitAction]="remove"
|
||||||
|
[disabled]="loading"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
*ngIf="editMode && params.managedByOrganization === true"
|
||||||
|
type="button"
|
||||||
bitIconButton="bwi-trash"
|
bitIconButton="bwi-trash"
|
||||||
buttonType="danger"
|
buttonType="danger"
|
||||||
bitFormButton
|
bitFormButton
|
||||||
|
@ -65,6 +65,7 @@ export interface MemberDialogParams {
|
|||||||
isOnSecretsManagerStandalone: boolean;
|
isOnSecretsManagerStandalone: boolean;
|
||||||
initialTab?: MemberDialogTab;
|
initialTab?: MemberDialogTab;
|
||||||
numConfirmedMembers: number;
|
numConfirmedMembers: number;
|
||||||
|
managedByOrganization?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MemberDialogResult {
|
export enum MemberDialogResult {
|
||||||
@ -89,7 +90,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
PermissionMode = PermissionMode;
|
PermissionMode = PermissionMode;
|
||||||
showNoMasterPasswordWarning = false;
|
showNoMasterPasswordWarning = false;
|
||||||
isOnSecretsManagerStandalone: boolean;
|
isOnSecretsManagerStandalone: boolean;
|
||||||
remainingSeats$: Observable<number>;
|
|
||||||
|
|
||||||
protected organization$: Observable<Organization>;
|
protected organization$: Observable<Organization>;
|
||||||
protected collectionAccessItems: AccessItemView[] = [];
|
protected collectionAccessItems: AccessItemView[] = [];
|
||||||
@ -251,10 +251,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.remainingSeats$ = this.organization$.pipe(
|
|
||||||
map((organization) => organization.seats - this.params.numConfirmedMembers),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setFormValidators(organization: Organization) {
|
private setFormValidators(organization: Organization) {
|
||||||
@ -469,7 +465,7 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
this.close(MemberDialogResult.Saved);
|
this.close(MemberDialogResult.Saved);
|
||||||
};
|
};
|
||||||
|
|
||||||
delete = async () => {
|
remove = async () => {
|
||||||
if (!this.editMode) {
|
if (!this.editMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -566,6 +562,39 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
this.close(MemberDialogResult.Restored);
|
this.close(MemberDialogResult.Restored);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
delete = async () => {
|
||||||
|
if (!this.editMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
title: {
|
||||||
|
key: "deleteOrganizationUser",
|
||||||
|
placeholders: [this.params.name],
|
||||||
|
},
|
||||||
|
content: { key: "deleteOrganizationUserWarning" },
|
||||||
|
type: "warning",
|
||||||
|
acceptButtonText: { key: "delete" },
|
||||||
|
cancelButtonText: { key: "cancel" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.organizationUserApiService.deleteOrganizationUser(
|
||||||
|
this.params.organizationId,
|
||||||
|
this.params.organizationUserId,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("organizationUserDeleted", this.params.name),
|
||||||
|
});
|
||||||
|
this.close(MemberDialogResult.Deleted);
|
||||||
|
};
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
|
@ -320,6 +320,17 @@
|
|||||||
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "remove" | i18n }}
|
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "remove" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
*ngIf="u.managedByOrganization === true"
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="deleteUser(u)"
|
||||||
|
>
|
||||||
|
<span class="tw-text-danger">
|
||||||
|
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||||
|
{{ "delete" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</bit-menu>
|
</bit-menu>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -486,7 +486,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
const enableUpgradePasswordManagerSub = await firstValueFrom(
|
const enableUpgradePasswordManagerSub = await firstValueFrom(
|
||||||
this.enableUpgradePasswordManagerSub$,
|
this.enableUpgradePasswordManagerSub$,
|
||||||
);
|
);
|
||||||
if (enableUpgradePasswordManagerSub) {
|
if (enableUpgradePasswordManagerSub && this.organization.canEditSubscription) {
|
||||||
const reference = openChangePlanDialog(this.dialogService, {
|
const reference = openChangePlanDialog(this.dialogService, {
|
||||||
data: {
|
data: {
|
||||||
organizationId: this.organization.id,
|
organizationId: this.organization.id,
|
||||||
@ -518,6 +518,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||||
initialTab: initialTab,
|
initialTab: initialTab,
|
||||||
numConfirmedMembers: this.dataSource.confirmedUserCount,
|
numConfirmedMembers: this.dataSource.confirmedUserCount,
|
||||||
|
managedByOrganization: user?.managedByOrganization,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -725,6 +726,40 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteUser(user: OrganizationUserView) {
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
title: {
|
||||||
|
key: "deleteOrganizationUser",
|
||||||
|
placeholders: [this.userNamePipe.transform(user)],
|
||||||
|
},
|
||||||
|
content: { key: "deleteOrganizationUserWarning" },
|
||||||
|
type: "warning",
|
||||||
|
acceptButtonText: { key: "delete" },
|
||||||
|
cancelButtonText: { key: "cancel" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actionPromise = this.organizationUserApiService.deleteOrganizationUser(
|
||||||
|
this.organization.id,
|
||||||
|
user.id,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await this.actionPromise;
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("organizationUserDeleted", this.userNamePipe.transform(user)),
|
||||||
|
});
|
||||||
|
this.dataSource.removeUser(user);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
this.actionPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
||||||
return this.dialogService.openSimpleDialog({
|
return this.dialogService.openSimpleDialog({
|
||||||
title: {
|
title: {
|
||||||
|
@ -63,10 +63,10 @@ const routes: Routes = [
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "access-intelligence",
|
path: "risk-insights",
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import("../../tools/access-intelligence/access-intelligence.module").then(
|
import("../../tools/risk-insights/risk-insights.module").then(
|
||||||
(m) => m.AccessIntelligenceModule,
|
(m) => m.RiskInsightsModule,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -123,21 +123,23 @@ export class AccountComponent implements OnInit, OnDestroy {
|
|||||||
this.canEditSubscription = organization.canEditSubscription;
|
this.canEditSubscription = organization.canEditSubscription;
|
||||||
this.canUseApi = organization.useApi;
|
this.canUseApi = organization.useApi;
|
||||||
|
|
||||||
// Update disabled states - reactive forms prefers not using disabled attribute
|
|
||||||
// Disabling these fields for self hosted orgs is deprecated
|
// Disabling these fields for self hosted orgs is deprecated
|
||||||
// This block can be completely removed as part of
|
// This block can be completely removed as part of
|
||||||
// https://bitwarden.atlassian.net/browse/PM-10863
|
// https://bitwarden.atlassian.net/browse/PM-10863
|
||||||
if (!this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
|
if (!this.limitCollectionCreationDeletionSplitFeatureFlagIsEnabled) {
|
||||||
if (!this.selfHosted) {
|
if (!this.selfHosted) {
|
||||||
this.formGroup.get("orgName").enable();
|
|
||||||
this.collectionManagementFormGroup.get("limitCollectionCreationDeletion").enable();
|
this.collectionManagementFormGroup.get("limitCollectionCreationDeletion").enable();
|
||||||
this.collectionManagementFormGroup.get("allowAdminAccessToAllCollectionItems").enable();
|
this.collectionManagementFormGroup.get("allowAdminAccessToAllCollectionItems").enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.selfHosted && this.canEditSubscription) {
|
// Update disabled states - reactive forms prefers not using disabled attribute
|
||||||
|
if (!this.selfHosted) {
|
||||||
|
this.formGroup.get("orgName").enable();
|
||||||
|
if (this.canEditSubscription) {
|
||||||
this.formGroup.get("billingEmail").enable();
|
this.formGroup.get("billingEmail").enable();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Org Response
|
// Org Response
|
||||||
this.org = orgResponse;
|
this.org = orgResponse;
|
||||||
|
@ -21,7 +21,13 @@
|
|||||||
>
|
>
|
||||||
{{ "purgeVault" | i18n }}
|
{{ "purgeVault" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton buttonType="danger" [bitAction]="deleteAccount">
|
<button
|
||||||
|
*ngIf="showDeleteAccount$ | async"
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
buttonType="danger"
|
||||||
|
[bitAction]="deleteAccount"
|
||||||
|
>
|
||||||
{{ "deleteAccount" | i18n }}
|
{{ "deleteAccount" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</app-danger-zone>
|
</app-danger-zone>
|
||||||
|
@ -24,6 +24,7 @@ export class AccountComponent implements OnInit {
|
|||||||
|
|
||||||
showChangeEmail$: Observable<boolean>;
|
showChangeEmail$: Observable<boolean>;
|
||||||
showPurgeVault$: Observable<boolean>;
|
showPurgeVault$: Observable<boolean>;
|
||||||
|
showDeleteAccount$: Observable<boolean>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private modalService: ModalService,
|
private modalService: ModalService,
|
||||||
@ -64,6 +65,16 @@ export class AccountComponent implements OnInit {
|
|||||||
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.showDeleteAccount$ = combineLatest([
|
||||||
|
isAccountDeprovisioningEnabled$,
|
||||||
|
userIsManagedByOrganization$,
|
||||||
|
]).pipe(
|
||||||
|
map(
|
||||||
|
([isAccountDeprovisioningEnabled, userIsManagedByOrganization]) =>
|
||||||
|
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deauthorizeSessions() {
|
async deauthorizeSessions() {
|
||||||
|
@ -1,22 +1,33 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import { importProvidersFrom } from "@angular/core";
|
import { importProvidersFrom } from "@angular/core";
|
||||||
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
|
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { ButtonModule } from "@bitwarden/components";
|
import { ButtonModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { PreloadedEnglishI18nModule } from "../../../core/tests";
|
import { PreloadedEnglishI18nModule } from "../../../core/tests";
|
||||||
|
|
||||||
import { DangerZoneComponent } from "./danger-zone.component";
|
import { DangerZoneComponent } from "./danger-zone.component";
|
||||||
|
|
||||||
|
class MockConfigService implements Partial<ConfigService> {}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Web/Danger Zone",
|
title: "Web/Danger Zone",
|
||||||
component: DangerZoneComponent,
|
component: DangerZoneComponent,
|
||||||
decorators: [
|
decorators: [
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [ButtonModule, JslibModule],
|
imports: [ButtonModule, JslibModule, CommonModule],
|
||||||
}),
|
}),
|
||||||
applicationConfig({
|
applicationConfig({
|
||||||
providers: [importProvidersFrom(PreloadedEnglishI18nModule)],
|
providers: [
|
||||||
|
importProvidersFrom(PreloadedEnglishI18nModule),
|
||||||
|
{
|
||||||
|
provide: ConfigService,
|
||||||
|
useClass: MockConfigService,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
} as Meta;
|
} as Meta;
|
||||||
|
@ -194,7 +194,7 @@ export class ChangePasswordComponent
|
|||||||
HashPurpose.LocalAuthorization,
|
HashPurpose.LocalAuthorization,
|
||||||
);
|
);
|
||||||
|
|
||||||
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey);
|
const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId);
|
||||||
if (userKey == null) {
|
if (userKey == null) {
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "error",
|
variant: "error",
|
||||||
|
@ -48,16 +48,7 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</dd>
|
</dd>
|
||||||
<dt>{{ "nextCharge" | i18n }}</dt>
|
<dt>{{ "nextCharge" | i18n }}</dt>
|
||||||
<dd *ngIf="!enableTimeThreshold">
|
<dd>
|
||||||
{{
|
|
||||||
nextInvoice
|
|
||||||
? (nextInvoice.date | date: "mediumDate") +
|
|
||||||
", " +
|
|
||||||
(nextInvoice.amount | currency: "$")
|
|
||||||
: "-"
|
|
||||||
}}
|
|
||||||
</dd>
|
|
||||||
<dd *ngIf="enableTimeThreshold">
|
|
||||||
{{
|
{{
|
||||||
nextInvoice
|
nextInvoice
|
||||||
? (sub.subscription.periodEndDate | date: "mediumDate") +
|
? (sub.subscription.periodEndDate | date: "mediumDate") +
|
||||||
|
@ -38,13 +38,9 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
sub: SubscriptionResponse;
|
sub: SubscriptionResponse;
|
||||||
selfHosted = false;
|
selfHosted = false;
|
||||||
cloudWebVaultUrl: string;
|
cloudWebVaultUrl: string;
|
||||||
enableTimeThreshold: boolean;
|
|
||||||
|
|
||||||
cancelPromise: Promise<any>;
|
cancelPromise: Promise<any>;
|
||||||
reinstatePromise: Promise<any>;
|
reinstatePromise: Promise<any>;
|
||||||
protected enableTimeThreshold$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.EnableTimeThreshold,
|
|
||||||
);
|
|
||||||
|
|
||||||
protected deprecateStripeSourcesAPI$ = this.configService.getFeatureFlag$(
|
protected deprecateStripeSourcesAPI$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
|
FeatureFlag.AC2476_DeprecateStripeSourcesAPI,
|
||||||
@ -69,7 +65,6 @@ export class UserSubscriptionComponent implements OnInit {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$);
|
||||||
await this.load();
|
await this.load();
|
||||||
this.enableTimeThreshold = await firstValueFrom(this.enableTimeThreshold$);
|
|
||||||
this.firstLoaded = true;
|
this.firstLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,10 +48,7 @@
|
|||||||
<dt [ngClass]="{ 'tw-text-danger': isExpired }">
|
<dt [ngClass]="{ 'tw-text-danger': isExpired }">
|
||||||
{{ "subscriptionExpiration" | i18n }}
|
{{ "subscriptionExpiration" | i18n }}
|
||||||
</dt>
|
</dt>
|
||||||
<dd [ngClass]="{ 'tw-text-danger': isExpired }" *ngIf="!enableTimeThreshold">
|
<dd [ngClass]="{ 'tw-text-danger': isExpired }">
|
||||||
{{ nextInvoice ? (nextInvoice.date | date: "mediumDate") : "-" }}
|
|
||||||
</dd>
|
|
||||||
<dd [ngClass]="{ 'tw-text-danger': isExpired }" *ngIf="enableTimeThreshold">
|
|
||||||
{{ nextInvoice ? (sub.subscription.periodEndDate | date: "mediumDate") : "-" }}
|
{{ nextInvoice ? (sub.subscription.periodEndDate | date: "mediumDate") : "-" }}
|
||||||
</dd>
|
</dd>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -52,7 +52,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
loading = true;
|
loading = true;
|
||||||
locale: string;
|
locale: string;
|
||||||
showUpdatedSubscriptionStatusSection$: Observable<boolean>;
|
showUpdatedSubscriptionStatusSection$: Observable<boolean>;
|
||||||
enableTimeThreshold: boolean;
|
|
||||||
preSelectedProductTier: ProductTierType = ProductTierType.Free;
|
preSelectedProductTier: ProductTierType = ProductTierType.Free;
|
||||||
showSubscription = true;
|
showSubscription = true;
|
||||||
showSelfHost = false;
|
showSelfHost = false;
|
||||||
@ -65,10 +64,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
FeatureFlag.EnableConsolidatedBilling,
|
FeatureFlag.EnableConsolidatedBilling,
|
||||||
);
|
);
|
||||||
|
|
||||||
protected enableTimeThreshold$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.EnableTimeThreshold,
|
|
||||||
);
|
|
||||||
|
|
||||||
protected enableUpgradePasswordManagerSub$ = this.configService.getFeatureFlag$(
|
protected enableUpgradePasswordManagerSub$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.EnableUpgradePasswordManagerSub,
|
FeatureFlag.EnableUpgradePasswordManagerSub,
|
||||||
);
|
);
|
||||||
@ -117,7 +112,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
this.showUpdatedSubscriptionStatusSection$ = this.configService.getFeatureFlag$(
|
this.showUpdatedSubscriptionStatusSection$ = this.configService.getFeatureFlag$(
|
||||||
FeatureFlag.AC1795_UpdatedSubscriptionStatusSection,
|
FeatureFlag.AC1795_UpdatedSubscriptionStatusSection,
|
||||||
);
|
);
|
||||||
this.enableTimeThreshold = await firstValueFrom(this.enableTimeThreshold$);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -298,9 +292,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
return this.i18nService.t("subscriptionUpgrade", this.sub.seats.toString());
|
return this.i18nService.t("subscriptionUpgrade", this.sub.seats.toString());
|
||||||
}
|
}
|
||||||
} else if (this.sub.maxAutoscaleSeats === this.sub.seats && this.sub.seats != null) {
|
} else if (this.sub.maxAutoscaleSeats === this.sub.seats && this.sub.seats != null) {
|
||||||
if (!this.enableTimeThreshold) {
|
|
||||||
return this.i18nService.t("subscriptionMaxReached", this.sub.seats.toString());
|
|
||||||
}
|
|
||||||
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
||||||
? "annualSubscriptionUserSeatsMessage"
|
? "annualSubscriptionUserSeatsMessage"
|
||||||
: "monthlySubscriptionUserSeatsMessage";
|
: "monthlySubscriptionUserSeatsMessage";
|
||||||
@ -311,21 +302,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
|||||||
} else if (this.userOrg.productTierType === ProductTierType.TeamsStarter) {
|
} else if (this.userOrg.productTierType === ProductTierType.TeamsStarter) {
|
||||||
return this.i18nService.t("subscriptionUserSeatsWithoutAdditionalSeatsOption", 10);
|
return this.i18nService.t("subscriptionUserSeatsWithoutAdditionalSeatsOption", 10);
|
||||||
} else if (this.sub.maxAutoscaleSeats == null) {
|
} else if (this.sub.maxAutoscaleSeats == null) {
|
||||||
if (!this.enableTimeThreshold) {
|
|
||||||
return this.i18nService.t("subscriptionUserSeatsUnlimitedAutoscale");
|
|
||||||
}
|
|
||||||
|
|
||||||
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
||||||
? "annualSubscriptionUserSeatsMessage"
|
? "annualSubscriptionUserSeatsMessage"
|
||||||
: "monthlySubscriptionUserSeatsMessage";
|
: "monthlySubscriptionUserSeatsMessage";
|
||||||
return this.i18nService.t(seatAdjustmentMessage);
|
return this.i18nService.t(seatAdjustmentMessage);
|
||||||
} else {
|
} else {
|
||||||
if (!this.enableTimeThreshold) {
|
|
||||||
return this.i18nService.t(
|
|
||||||
"subscriptionUserSeatsLimitedAutoscale",
|
|
||||||
this.sub.maxAutoscaleSeats.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
const seatAdjustmentMessage = this.sub.plan.isAnnual
|
||||||
? "annualSubscriptionUserSeatsMessage"
|
? "annualSubscriptionUserSeatsMessage"
|
||||||
: "monthlySubscriptionUserSeatsMessage";
|
: "monthlySubscriptionUserSeatsMessage";
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import { NgModule } from "@angular/core";
|
|
||||||
|
|
||||||
import { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
|
|
||||||
import { AccessIntelligenceComponent } from "./access-intelligence.component";
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [AccessIntelligenceComponent, AccessIntelligenceRoutingModule],
|
|
||||||
})
|
|
||||||
export class AccessIntelligenceModule {}
|
|
@ -1,11 +0,0 @@
|
|||||||
<!-- <bit-table [dataSource]="dataSource"> -->
|
|
||||||
<ng-container header>
|
|
||||||
<tr>
|
|
||||||
<th bitCell>{{ "application" | i18n }}</th>
|
|
||||||
<th bitCell>{{ "atRiskPasswords" | i18n }}</th>
|
|
||||||
<th bitCell>{{ "totalPasswords" | i18n }}</th>
|
|
||||||
<th bitCell>{{ "atRiskMembers" | i18n }}</th>
|
|
||||||
<th bitCell>{{ "totalMembers" | i18n }}</th>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
<!-- </bit-table> -->
|
|
@ -1,19 +0,0 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
|
||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
|
||||||
import { TableDataSource, TableModule } from "@bitwarden/components";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
standalone: true,
|
|
||||||
selector: "tools-application-table",
|
|
||||||
templateUrl: "./application-table.component.html",
|
|
||||||
imports: [CommonModule, JslibModule, TableModule],
|
|
||||||
})
|
|
||||||
export class ApplicationTableComponent {
|
|
||||||
protected dataSource = new TableDataSource<any>();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.dataSource.data = [];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<bit-no-items [icon]="noItemsIcon" class="tw-text-main">
|
|
||||||
<ng-container slot="title">
|
|
||||||
<h2 class="tw-font-semibold mt-4">
|
|
||||||
{{ "noPriorityApplicationsTitle" | i18n }}
|
|
||||||
</h2>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container slot="description">
|
|
||||||
<p class="tw-text-muted">
|
|
||||||
{{ "noPriorityApplicationsDescription" | i18n }}
|
|
||||||
</p>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container slot="button">
|
|
||||||
<button bitButton buttonType="primary" type="button">{{ "markPriorityApps" | i18n }}</button>
|
|
||||||
</ng-container>
|
|
||||||
</bit-no-items>
|
|
@ -1,15 +0,0 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
|
||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
|
||||||
import { ButtonModule, NoItemsModule, Icons } from "@bitwarden/components";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
standalone: true,
|
|
||||||
selector: "tools-no-priority-apps",
|
|
||||||
templateUrl: "no-priority-apps.component.html",
|
|
||||||
imports: [ButtonModule, CommonModule, JslibModule, NoItemsModule],
|
|
||||||
})
|
|
||||||
export class NoPriorityAppsComponent {
|
|
||||||
noItemsIcon = Icons.Security;
|
|
||||||
}
|
|
@ -1,123 +0,0 @@
|
|||||||
<p>{{ "passwordsReportDesc" | i18n }}</p>
|
|
||||||
<div *ngIf="loading">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="tw-mt-4" *ngIf="!dataSource.data.length">
|
|
||||||
<tools-no-priority-apps></tools-no-priority-apps>
|
|
||||||
</div>
|
|
||||||
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
|
|
||||||
<div class="tw-flex tw-gap-6">
|
|
||||||
<tools-card
|
|
||||||
class="tw-flex-1"
|
|
||||||
[title]="'atRiskMembers' | i18n"
|
|
||||||
[value]="totalMembersMap.size - 3"
|
|
||||||
[maxValue]="totalMembersMap.size"
|
|
||||||
>
|
|
||||||
</tools-card>
|
|
||||||
<tools-card
|
|
||||||
class="tw-flex-1"
|
|
||||||
[title]="'atRiskApplications' | i18n"
|
|
||||||
[value]="totalMembersMap.size - 1"
|
|
||||||
[maxValue]="totalMembersMap.size"
|
|
||||||
>
|
|
||||||
</tools-card>
|
|
||||||
</div>
|
|
||||||
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
|
|
||||||
<bit-search class="tw-grow" [formControl]="searchControl"></bit-search>
|
|
||||||
<button class="tw-rounded-lg" type="button" buttonType="secondary" bitButton>
|
|
||||||
<i class="bwi bwi-star-f tw-mr-2"></i>
|
|
||||||
{{ "markAppAsCritical" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
|
|
||||||
<div class="tw-flex tw-gap-6">
|
|
||||||
<tools-card
|
|
||||||
class="tw-flex-1"
|
|
||||||
[title]="'atRiskMembers' | i18n"
|
|
||||||
[value]="totalMembersMap.size - 3"
|
|
||||||
[maxValue]="totalMembersMap.size"
|
|
||||||
>
|
|
||||||
</tools-card>
|
|
||||||
<tools-card
|
|
||||||
class="tw-flex-1"
|
|
||||||
[title]="'atRiskApplications' | i18n"
|
|
||||||
[value]="totalMembersMap.size - 1"
|
|
||||||
[maxValue]="totalMembersMap.size"
|
|
||||||
>
|
|
||||||
</tools-card>
|
|
||||||
</div>
|
|
||||||
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
|
|
||||||
<bit-search class="tw-grow" [formControl]="searchControl"></bit-search>
|
|
||||||
<button
|
|
||||||
class="tw-rounded-lg"
|
|
||||||
type="button"
|
|
||||||
buttonType="secondary"
|
|
||||||
[disabled]="!selectedIds.size"
|
|
||||||
bitButton
|
|
||||||
[bitAction]="markAppsAsCritical"
|
|
||||||
appA11yTitle="{{ 'markAppAsCritical' | i18n }}"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-star-f tw-mr-2"></i>
|
|
||||||
{{ "markAppAsCritical" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<bit-table [dataSource]="dataSource">
|
|
||||||
<ng-container header>
|
|
||||||
<tr bitRow>
|
|
||||||
<th bitCell></th>
|
|
||||||
<th bitCell bitSortable="name">{{ "name" | i18n }}</th>
|
|
||||||
<th bitCell class="tw-text-right">{{ "weakness" | i18n }}</th>
|
|
||||||
<th bitCell class="tw-text-right">{{ "timesReused" | i18n }}</th>
|
|
||||||
<th bitCell class="tw-text-right">{{ "timesExposed" | i18n }}</th>
|
|
||||||
<th bitCell class="tw-text-right">{{ "totalMembers" | i18n }}</th>
|
|
||||||
</tr>
|
|
||||||
</ng-container>
|
|
||||||
<ng-template body let-rows$>
|
|
||||||
<tr bitRow *ngFor="let r of rows$ | async; trackBy: trackByFunction">
|
|
||||||
<td bitCell>
|
|
||||||
<input
|
|
||||||
bitCheckbox
|
|
||||||
type="checkbox"
|
|
||||||
[checked]="selectedIds.has(r.id)"
|
|
||||||
(change)="onCheckboxChange(r.id, $event)"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td bitCell>
|
|
||||||
<ng-container>
|
|
||||||
<span>{{ r.name }}</span>
|
|
||||||
</ng-container>
|
|
||||||
<br />
|
|
||||||
<small>{{ r.subTitle }}</small>
|
|
||||||
</td>
|
|
||||||
<td bitCell class="tw-text-right">
|
|
||||||
<span
|
|
||||||
bitBadge
|
|
||||||
*ngIf="passwordStrengthMap.has(r.id)"
|
|
||||||
[variant]="passwordStrengthMap.get(r.id)[1]"
|
|
||||||
>
|
|
||||||
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td bitCell class="tw-text-right">
|
|
||||||
<span bitBadge *ngIf="passwordUseMap.has(r.login.password)" variant="warning">
|
|
||||||
{{ "reusedXTimes" | i18n: passwordUseMap.get(r.login.password) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td bitCell class="tw-text-right">
|
|
||||||
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
|
|
||||||
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td bitCell class="tw-text-right" data-testid="total-membership">
|
|
||||||
{{ totalMembersMap.get(r.id) || 0 }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
</bit-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -0,0 +1,113 @@
|
|||||||
|
<div *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tw-mt-4" *ngIf="!dataSource.data.length">
|
||||||
|
<bit-no-items [icon]="noItemsIcon" class="tw-text-main">
|
||||||
|
<ng-container slot="title">
|
||||||
|
<h2 class="tw-font-semibold mt-4">
|
||||||
|
{{ "noAppsInOrgTitle" | i18n: organization.name }}
|
||||||
|
</h2>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container slot="description">
|
||||||
|
<div class="tw-flex tw-flex-col tw-mb-2">
|
||||||
|
<span class="tw-text-muted">
|
||||||
|
{{ "noAppsInOrgDescription" | i18n }}
|
||||||
|
</span>
|
||||||
|
<a class="text-primary" routerLink="/login">{{ "learnMore" | i18n }}</a>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container slot="button">
|
||||||
|
<button (click)="goToCreateNewLoginItem()" bitButton buttonType="primary" type="button">
|
||||||
|
{{ "createNewLoginItem" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-no-items>
|
||||||
|
</div>
|
||||||
|
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
|
||||||
|
<h2 class="tw-mb-6" bitTypography="h2">{{ "allApplications" | i18n }}</h2>
|
||||||
|
<div class="tw-flex tw-gap-6">
|
||||||
|
<tools-card
|
||||||
|
class="tw-flex-1"
|
||||||
|
[title]="'atRiskMembers' | i18n"
|
||||||
|
[value]="mockAtRiskMembersCount"
|
||||||
|
[maxValue]="mockTotalMembersCount"
|
||||||
|
>
|
||||||
|
</tools-card>
|
||||||
|
<tools-card
|
||||||
|
class="tw-flex-1"
|
||||||
|
[title]="'atRiskApplications' | i18n"
|
||||||
|
[value]="mockAtRiskAppsCount"
|
||||||
|
[maxValue]="mockTotalAppsCount"
|
||||||
|
>
|
||||||
|
</tools-card>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
|
||||||
|
<bit-search
|
||||||
|
[placeholder]="'searchApps' | i18n"
|
||||||
|
class="tw-grow"
|
||||||
|
[formControl]="searchControl"
|
||||||
|
></bit-search>
|
||||||
|
<button
|
||||||
|
class="tw-rounded-lg"
|
||||||
|
type="button"
|
||||||
|
buttonType="secondary"
|
||||||
|
bitButton
|
||||||
|
[disabled]="!selectedIds.size"
|
||||||
|
[loading]="markingAsCritical"
|
||||||
|
(click)="markAppsAsCritical()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-star-f tw-mr-2"></i>
|
||||||
|
{{ "markAppAsCritical" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<bit-table [dataSource]="dataSource">
|
||||||
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th bitSortable="name" bitCell>{{ "application" | i18n }}</th>
|
||||||
|
<th bitSortable="atRiskPasswords" bitCell>{{ "atRiskPasswords" | i18n }}</th>
|
||||||
|
<th bitSortable="totalPasswords" bitCell>{{ "totalPasswords" | i18n }}</th>
|
||||||
|
<th bitSortable="atRiskMembers" bitCell>{{ "atRiskMembers" | i18n }}</th>
|
||||||
|
<th bitSortable="totalMembers" bitCell>{{ "totalMembers" | i18n }}</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr bitRow *ngFor="let r of rows$ | async; trackBy: trackByFunction">
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
bitCheckbox
|
||||||
|
type="checkbox"
|
||||||
|
[checked]="selectedIds.has(r.id)"
|
||||||
|
(change)="onCheckboxChange(r.id, $event)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>{{ r.name }}</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.atRiskPasswords }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.totalPasswords }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.atRiskMembers }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell data-testid="total-membership">
|
||||||
|
{{ r.totalMembers }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
</div>
|
@ -0,0 +1,118 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { FormControl } from "@angular/forms";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
import { debounceTime, firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||||
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
|
import {
|
||||||
|
Icons,
|
||||||
|
NoItemsModule,
|
||||||
|
SearchModule,
|
||||||
|
TableDataSource,
|
||||||
|
ToastService,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { CardComponent } from "@bitwarden/tools-card";
|
||||||
|
|
||||||
|
import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
|
import { SharedModule } from "../../shared";
|
||||||
|
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
||||||
|
|
||||||
|
import { applicationTableMockData } from "./application-table.mock";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "tools-all-applications",
|
||||||
|
templateUrl: "./all-applications.component.html",
|
||||||
|
imports: [HeaderModule, CardComponent, SearchModule, PipesModule, NoItemsModule, SharedModule],
|
||||||
|
})
|
||||||
|
export class AllApplicationsComponent implements OnInit {
|
||||||
|
protected dataSource = new TableDataSource<any>();
|
||||||
|
protected selectedIds: Set<number> = new Set<number>();
|
||||||
|
protected searchControl = new FormControl("", { nonNullable: true });
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
protected loading = false;
|
||||||
|
protected organization: Organization;
|
||||||
|
noItemsIcon = Icons.Security;
|
||||||
|
protected markingAsCritical = false;
|
||||||
|
|
||||||
|
// MOCK DATA
|
||||||
|
protected mockData = applicationTableMockData;
|
||||||
|
protected mockAtRiskMembersCount = 0;
|
||||||
|
protected mockAtRiskAppsCount = 0;
|
||||||
|
protected mockTotalMembersCount = 0;
|
||||||
|
protected mockTotalAppsCount = 0;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.activatedRoute.paramMap
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
map(async (params) => {
|
||||||
|
const organizationId = params.get("organizationId");
|
||||||
|
this.organization = await firstValueFrom(this.organizationService.get$(organizationId));
|
||||||
|
// TODO: use organizationId to fetch data
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected cipherService: CipherService,
|
||||||
|
protected passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||||
|
protected auditService: AuditService,
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected activatedRoute: ActivatedRoute,
|
||||||
|
protected toastService: ToastService,
|
||||||
|
protected organizationService: OrganizationService,
|
||||||
|
) {
|
||||||
|
this.dataSource.data = applicationTableMockData;
|
||||||
|
this.searchControl.valueChanges
|
||||||
|
.pipe(debounceTime(200), takeUntilDestroyed())
|
||||||
|
.subscribe((v) => (this.dataSource.filter = v));
|
||||||
|
}
|
||||||
|
|
||||||
|
goToCreateNewLoginItem = async () => {
|
||||||
|
// TODO: implement
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "warning",
|
||||||
|
title: null,
|
||||||
|
message: "Not yet implemented",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
markAppsAsCritical = async () => {
|
||||||
|
// TODO: Send to API once implemented
|
||||||
|
this.markingAsCritical = true;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.selectedIds.clear();
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "success",
|
||||||
|
title: null,
|
||||||
|
message: this.i18nService.t("appsMarkedAsCritical"),
|
||||||
|
});
|
||||||
|
resolve(true);
|
||||||
|
this.markingAsCritical = false;
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
trackByFunction(_: number, item: CipherView) {
|
||||||
|
return item.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCheckboxChange(id: number, event: Event) {
|
||||||
|
const isChecked = (event.target as HTMLInputElement).checked;
|
||||||
|
if (isChecked) {
|
||||||
|
this.selectedIds.add(id);
|
||||||
|
} else {
|
||||||
|
this.selectedIds.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
export const applicationTableMockData = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "google.com",
|
||||||
|
atRiskPasswords: 4,
|
||||||
|
totalPasswords: 10,
|
||||||
|
atRiskMembers: 2,
|
||||||
|
totalMembers: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "facebook.com",
|
||||||
|
atRiskPasswords: 3,
|
||||||
|
totalPasswords: 8,
|
||||||
|
atRiskMembers: 1,
|
||||||
|
totalMembers: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "twitter.com",
|
||||||
|
atRiskPasswords: 2,
|
||||||
|
totalPasswords: 6,
|
||||||
|
atRiskMembers: 0,
|
||||||
|
totalMembers: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "linkedin.com",
|
||||||
|
atRiskPasswords: 1,
|
||||||
|
totalPasswords: 4,
|
||||||
|
atRiskMembers: 0,
|
||||||
|
totalMembers: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "instagram.com",
|
||||||
|
atRiskPasswords: 0,
|
||||||
|
totalPasswords: 2,
|
||||||
|
atRiskMembers: 0,
|
||||||
|
totalMembers: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "tiktok.com",
|
||||||
|
atRiskPasswords: 0,
|
||||||
|
totalPasswords: 1,
|
||||||
|
atRiskMembers: 0,
|
||||||
|
totalMembers: 0,
|
||||||
|
},
|
||||||
|
];
|
@ -0,0 +1,99 @@
|
|||||||
|
<div *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tw-mt-4" *ngIf="!dataSource.data.length">
|
||||||
|
<bit-no-items [icon]="noItemsIcon" class="tw-text-main">
|
||||||
|
<ng-container slot="title">
|
||||||
|
<h2 class="tw-font-semibold mt-4">
|
||||||
|
{{ "noCriticalAppsTitle" | i18n }}
|
||||||
|
</h2>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container slot="description">
|
||||||
|
<p class="tw-text-muted">
|
||||||
|
{{ "noCriticalAppsDescription" | i18n }}
|
||||||
|
</p>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container slot="button">
|
||||||
|
<button (click)="goToAllAppsTab()" bitButton buttonType="primary" type="button">
|
||||||
|
{{ "markCriticalApps" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-no-items>
|
||||||
|
</div>
|
||||||
|
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
|
||||||
|
<div class="tw-flex tw-justify-between tw-mb-4">
|
||||||
|
<h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2>
|
||||||
|
<button bitButton buttonType="primary" type="button">
|
||||||
|
<i class="bwi bwi-envelope tw-mr-2"></i>
|
||||||
|
{{ "requestPasswordChange" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-gap-6">
|
||||||
|
<tools-card
|
||||||
|
class="tw-flex-1"
|
||||||
|
[title]="'atRiskMembers' | i18n"
|
||||||
|
[value]="mockAtRiskMembersCount"
|
||||||
|
[maxValue]="mockTotalMembersCount"
|
||||||
|
>
|
||||||
|
</tools-card>
|
||||||
|
<tools-card
|
||||||
|
class="tw-flex-1"
|
||||||
|
[title]="'atRiskApplications' | i18n"
|
||||||
|
[value]="mockAtRiskAppsCount"
|
||||||
|
[maxValue]="mockTotalAppsCount"
|
||||||
|
>
|
||||||
|
</tools-card>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4">
|
||||||
|
<bit-search
|
||||||
|
[placeholder]="'searchApps' | i18n"
|
||||||
|
class="tw-grow"
|
||||||
|
[formControl]="searchControl"
|
||||||
|
></bit-search>
|
||||||
|
</div>
|
||||||
|
<bit-table [dataSource]="dataSource">
|
||||||
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th bitSortable="name" bitCell>{{ "application" | i18n }}</th>
|
||||||
|
<th bitSortable="atRiskPasswords" bitCell>{{ "atRiskPasswords" | i18n }}</th>
|
||||||
|
<th bitSortable="totalPasswords" bitCell>{{ "totalPasswords" | i18n }}</th>
|
||||||
|
<th bitSortable="atRiskMembers" bitCell>{{ "atRiskMembers" | i18n }}</th>
|
||||||
|
<th bitSortable="totalMembers" bitCell>{{ "totalMembers" | i18n }}</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr bitRow *ngFor="let r of rows$ | async">
|
||||||
|
<td>
|
||||||
|
<i class="bwi bwi-star-f"></i>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>{{ r.name }}</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.atRiskPasswords }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.totalPasswords }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<span>
|
||||||
|
{{ r.atRiskMembers }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell data-testid="total-membership">
|
||||||
|
{{ r.totalMembers }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
</div>
|
@ -0,0 +1,68 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { FormControl } from "@angular/forms";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { debounceTime, map } from "rxjs";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { SearchModule, TableDataSource, NoItemsModule, Icons } from "@bitwarden/components";
|
||||||
|
import { CardComponent } from "@bitwarden/tools-card";
|
||||||
|
|
||||||
|
import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
|
import { SharedModule } from "../../shared";
|
||||||
|
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
||||||
|
|
||||||
|
import { applicationTableMockData } from "./application-table.mock";
|
||||||
|
import { RiskInsightsTabType } from "./risk-insights.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "tools-critical-applications",
|
||||||
|
templateUrl: "./critical-applications.component.html",
|
||||||
|
imports: [CardComponent, HeaderModule, SearchModule, NoItemsModule, PipesModule, SharedModule],
|
||||||
|
})
|
||||||
|
export class CriticalApplicationsComponent implements OnInit {
|
||||||
|
protected dataSource = new TableDataSource<any>();
|
||||||
|
protected selectedIds: Set<number> = new Set<number>();
|
||||||
|
protected searchControl = new FormControl("", { nonNullable: true });
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
protected loading = false;
|
||||||
|
protected organizationId: string;
|
||||||
|
noItemsIcon = Icons.Security;
|
||||||
|
// MOCK DATA
|
||||||
|
protected mockData = applicationTableMockData;
|
||||||
|
protected mockAtRiskMembersCount = 0;
|
||||||
|
protected mockAtRiskAppsCount = 0;
|
||||||
|
protected mockTotalMembersCount = 0;
|
||||||
|
protected mockTotalAppsCount = 0;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.activatedRoute.paramMap
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
map(async (params) => {
|
||||||
|
this.organizationId = params.get("organizationId");
|
||||||
|
// TODO: use organizationId to fetch data
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
goToAllAppsTab = async () => {
|
||||||
|
await this.router.navigate([`organizations/${this.organizationId}/risk-insights`], {
|
||||||
|
queryParams: { tabIndex: RiskInsightsTabType.AllApps },
|
||||||
|
queryParamsHandling: "merge",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected i18nService: I18nService,
|
||||||
|
protected activatedRoute: ActivatedRoute,
|
||||||
|
protected router: Router,
|
||||||
|
) {
|
||||||
|
this.dataSource.data = []; //applicationTableMockData;
|
||||||
|
this.searchControl.valueChanges
|
||||||
|
.pipe(debounceTime(200), takeUntilDestroyed())
|
||||||
|
.subscribe((v) => (this.dataSource.filter = v));
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ import { mock, MockProxy } from "jest-mock-extended";
|
|||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
|
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
@ -6,7 +6,7 @@ import { map } from "rxjs";
|
|||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
|
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
@ -0,0 +1,64 @@
|
|||||||
|
<p>{{ "passwordsReportDesc" | i18n }}</p>
|
||||||
|
<div *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
|
||||||
|
<bit-table [dataSource]="dataSource">
|
||||||
|
<ng-container header>
|
||||||
|
<tr bitRow>
|
||||||
|
<th bitCell></th>
|
||||||
|
<th bitCell bitSortable="name">{{ "name" | i18n }}</th>
|
||||||
|
<th bitCell class="tw-text-right">{{ "weakness" | i18n }}</th>
|
||||||
|
<th bitCell class="tw-text-right">{{ "timesReused" | i18n }}</th>
|
||||||
|
<th bitCell class="tw-text-right">{{ "timesExposed" | i18n }}</th>
|
||||||
|
<th bitCell class="tw-text-right">{{ "totalMembers" | i18n }}</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr bitRow *ngFor="let r of rows$ | async; trackBy: trackByFunction">
|
||||||
|
<td bitCell>
|
||||||
|
<input
|
||||||
|
bitCheckbox
|
||||||
|
type="checkbox"
|
||||||
|
[checked]="selectedIds.has(r.id)"
|
||||||
|
(change)="onCheckboxChange(r.id, $event)"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td bitCell>
|
||||||
|
<ng-container>
|
||||||
|
<span>{{ r.name }}</span>
|
||||||
|
</ng-container>
|
||||||
|
<br />
|
||||||
|
<small>{{ r.subTitle }}</small>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-right">
|
||||||
|
<span
|
||||||
|
bitBadge
|
||||||
|
*ngIf="passwordStrengthMap.has(r.id)"
|
||||||
|
[variant]="passwordStrengthMap.get(r.id)[1]"
|
||||||
|
>
|
||||||
|
{{ passwordStrengthMap.get(r.id)[0] | i18n }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-right">
|
||||||
|
<span bitBadge *ngIf="passwordUseMap.has(r.login.password)" variant="warning">
|
||||||
|
{{ "reusedXTimes" | i18n: passwordUseMap.get(r.login.password) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-right">
|
||||||
|
<span bitBadge *ngIf="exposedPasswordMap.has(r.id)" variant="warning">
|
||||||
|
{{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-right" data-testid="total-membership">
|
||||||
|
{{ totalMembersMap.get(r.id) || 0 }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
</div>
|
@ -5,7 +5,7 @@ import { ActivatedRoute } from "@angular/router";
|
|||||||
import { debounceTime, map } from "rxjs";
|
import { debounceTime, map } from "rxjs";
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/access-intelligence";
|
import { PasswordHealthService } from "@bitwarden/bit-common/tools/reports/risk-insights";
|
||||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||||
@ -27,8 +27,6 @@ import { OrganizationBadgeModule } from "../../vault/individual-vault/organizati
|
|||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
import { PipesModule } from "../../vault/individual-vault/pipes/pipes.module";
|
||||||
|
|
||||||
import { NoPriorityAppsComponent } from "./no-priority-apps.component";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
selector: "tools-password-health-members",
|
selector: "tools-password-health-members",
|
||||||
@ -40,7 +38,6 @@ import { NoPriorityAppsComponent } from "./no-priority-apps.component";
|
|||||||
HeaderModule,
|
HeaderModule,
|
||||||
SearchModule,
|
SearchModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NoPriorityAppsComponent,
|
|
||||||
SharedModule,
|
SharedModule,
|
||||||
TableModule,
|
TableModule,
|
||||||
],
|
],
|
||||||
@ -100,7 +97,7 @@ export class PasswordHealthMembersComponent implements OnInit {
|
|||||||
|
|
||||||
await passwordHealthService.generateReport();
|
await passwordHealthService.generateReport();
|
||||||
|
|
||||||
this.dataSource.data = []; //passwordHealthService.reportCiphers;
|
this.dataSource.data = passwordHealthService.reportCiphers;
|
||||||
|
|
||||||
this.exposedPasswordMap = passwordHealthService.exposedPasswordMap;
|
this.exposedPasswordMap = passwordHealthService.exposedPasswordMap;
|
||||||
this.passwordStrengthMap = passwordHealthService.passwordStrengthMap;
|
this.passwordStrengthMap = passwordHealthService.passwordStrengthMap;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user