mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 02:03:39 +00:00
Merge branch 'main' into PM-26250-Explore-options-to-enable-direct-importer-for-mac-app-store-build
This commit is contained in:
16
.github/renovate.json5
vendored
16
.github/renovate.json5
vendored
@@ -154,7 +154,6 @@
|
|||||||
"@types/glob",
|
"@types/glob",
|
||||||
"@types/lowdb",
|
"@types/lowdb",
|
||||||
"@types/node",
|
"@types/node",
|
||||||
"@types/node-forge",
|
|
||||||
"@types/node-ipc",
|
"@types/node-ipc",
|
||||||
"@yao-pkg/pkg",
|
"@yao-pkg/pkg",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -192,12 +191,10 @@
|
|||||||
"napi",
|
"napi",
|
||||||
"napi-build",
|
"napi-build",
|
||||||
"napi-derive",
|
"napi-derive",
|
||||||
"node-forge",
|
|
||||||
"node-ipc",
|
"node-ipc",
|
||||||
"nx",
|
"nx",
|
||||||
"oo7",
|
"oo7",
|
||||||
"oslog",
|
"oslog",
|
||||||
"parse5",
|
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"pkg",
|
"pkg",
|
||||||
"postcss",
|
"postcss",
|
||||||
@@ -215,6 +212,8 @@
|
|||||||
"simplelog",
|
"simplelog",
|
||||||
"style-loader",
|
"style-loader",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"ts-node",
|
"ts-node",
|
||||||
@@ -261,6 +260,11 @@
|
|||||||
groupName: "windows",
|
groupName: "windows",
|
||||||
matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"],
|
matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// We need to group all tokio-related packages together to avoid build errors caused by version incompatibilities.
|
||||||
|
groupName: "tokio",
|
||||||
|
matchPackageNames: ["bytes", "tokio", "tokio-util"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// We group all webpack build-related minor and patch updates together to reduce PR noise.
|
// We group all webpack build-related minor and patch updates together to reduce PR noise.
|
||||||
// We include patch updates here because we want PRs for webpack patch updates and it's in this group.
|
// We include patch updates here because we want PRs for webpack patch updates and it's in this group.
|
||||||
@@ -409,14 +413,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
|
"@types/node-forge",
|
||||||
"aes",
|
"aes",
|
||||||
"big-integer",
|
"big-integer",
|
||||||
"cbc",
|
"cbc",
|
||||||
|
"linux-keyutils",
|
||||||
|
"memsec",
|
||||||
|
"node-forge",
|
||||||
"rsa",
|
"rsa",
|
||||||
"russh-cryptovec",
|
"russh-cryptovec",
|
||||||
"sha2",
|
"sha2",
|
||||||
"memsec",
|
|
||||||
"linux-keyutils",
|
|
||||||
],
|
],
|
||||||
description: "Key Management owned dependencies",
|
description: "Key Management owned dependencies",
|
||||||
commitMessagePrefix: "[deps] KM:",
|
commitMessagePrefix: "[deps] KM:",
|
||||||
|
|||||||
32
.github/workflows/build-desktop.yml
vendored
32
.github/workflows/build-desktop.yml
vendored
@@ -209,7 +209,7 @@ jobs:
|
|||||||
- name: Set up environment
|
- name: Set up environment
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder
|
sudo apt-get -y install pkg-config libxss-dev rpm flatpak flatpak-builder
|
||||||
|
|
||||||
- name: Set up Snap
|
- name: Set up Snap
|
||||||
run: sudo snap install snapcraft --classic
|
run: sudo snap install snapcraft --classic
|
||||||
@@ -262,12 +262,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
PKG_CONFIG_ALLOW_CROSS: true
|
PKG_CONFIG_ALLOW_CROSS: true
|
||||||
PKG_CONFIG_ALL_STATIC: true
|
PKG_CONFIG_ALL_STATIC: true
|
||||||
TARGET: musl
|
|
||||||
# Note: It is important that we use the release build because some compute heavy
|
# Note: It is important that we use the release build because some compute heavy
|
||||||
# operations such as key derivation for oo7 on linux are too slow in debug mode
|
# operations such as key derivation for oo7 on linux are too slow in debug mode
|
||||||
run: |
|
run: |
|
||||||
rustup target add x86_64-unknown-linux-musl
|
node build.js --release
|
||||||
node build.js --target=x86_64-unknown-linux-musl --release
|
|
||||||
|
|
||||||
- name: Build application
|
- name: Build application
|
||||||
run: npm run dist:lin
|
run: npm run dist:lin
|
||||||
@@ -367,7 +365,7 @@ jobs:
|
|||||||
- name: Set up environment
|
- name: Set up environment
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder squashfs-tools ruby ruby-dev rubygems build-essential
|
sudo apt-get -y install pkg-config libxss-dev rpm flatpak flatpak-builder squashfs-tools ruby ruby-dev rubygems build-essential
|
||||||
sudo gem install --no-document fpm
|
sudo gem install --no-document fpm
|
||||||
|
|
||||||
- name: Set up Snap
|
- name: Set up Snap
|
||||||
@@ -427,12 +425,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
PKG_CONFIG_ALLOW_CROSS: true
|
PKG_CONFIG_ALLOW_CROSS: true
|
||||||
PKG_CONFIG_ALL_STATIC: true
|
PKG_CONFIG_ALL_STATIC: true
|
||||||
TARGET: musl
|
|
||||||
# Note: It is important that we use the release build because some compute heavy
|
# Note: It is important that we use the release build because some compute heavy
|
||||||
# operations such as key derivation for oo7 on linux are too slow in debug mode
|
# operations such as key derivation for oo7 on linux are too slow in debug mode
|
||||||
run: |
|
run: |
|
||||||
rustup target add aarch64-unknown-linux-musl
|
node build.js --release
|
||||||
node build.js --target=aarch64-unknown-linux-musl --release
|
|
||||||
|
|
||||||
- name: Check index.d.ts generated
|
- name: Check index.d.ts generated
|
||||||
if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true'
|
if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true'
|
||||||
@@ -587,7 +583,9 @@ jobs:
|
|||||||
- name: Build Native Module
|
- name: Build Native Module
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
working-directory: apps/desktop/desktop_native
|
working-directory: apps/desktop/desktop_native
|
||||||
run: node build.js cross-platform
|
env:
|
||||||
|
MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }}
|
||||||
|
run: node build.js cross-platform "$env:MODE"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm run build
|
run: npm run build
|
||||||
@@ -850,7 +848,9 @@ jobs:
|
|||||||
- name: Build Native Module
|
- name: Build Native Module
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
working-directory: apps/desktop/desktop_native
|
working-directory: apps/desktop/desktop_native
|
||||||
run: node build.js cross-platform
|
env:
|
||||||
|
MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }}
|
||||||
|
run: node build.js cross-platform "$env:MODE"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm run build
|
run: npm run build
|
||||||
@@ -1206,7 +1206,9 @@ jobs:
|
|||||||
- name: Build Native Module
|
- name: Build Native Module
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
working-directory: apps/desktop/desktop_native
|
working-directory: apps/desktop/desktop_native
|
||||||
run: node build.js cross-platform
|
env:
|
||||||
|
MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }}
|
||||||
|
run: node build.js cross-platform "$MODE"
|
||||||
|
|
||||||
- name: Build application (dev)
|
- name: Build application (dev)
|
||||||
run: npm run build
|
run: npm run build
|
||||||
@@ -1428,7 +1430,9 @@ jobs:
|
|||||||
- name: Build Native Module
|
- name: Build Native Module
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
working-directory: apps/desktop/desktop_native
|
working-directory: apps/desktop/desktop_native
|
||||||
run: node build.js cross-platform
|
env:
|
||||||
|
MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }}
|
||||||
|
run: node build.js cross-platform "$MODE"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
@@ -1709,7 +1713,9 @@ jobs:
|
|||||||
- name: Build Native Module
|
- name: Build Native Module
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
working-directory: apps/desktop/desktop_native
|
working-directory: apps/desktop/desktop_native
|
||||||
run: node build.js cross-platform
|
env:
|
||||||
|
MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }}
|
||||||
|
run: node build.js cross-platform "$MODE"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
if: steps.build-cache.outputs.cache-hit != 'true'
|
if: steps.build-cache.outputs.cache-hit != 'true'
|
||||||
|
|||||||
2
.github/workflows/publish-web.yml
vendored
2
.github/workflows/publish-web.yml
vendored
@@ -187,6 +187,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
|
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
|
||||||
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
|
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
|
||||||
|
owner: ${{ github.repository_owner }}
|
||||||
|
repositories: self-host
|
||||||
|
|
||||||
- name: Trigger Bitwarden lite build
|
- name: Trigger Bitwarden lite build
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
|
|||||||
8
.github/workflows/release-desktop.yml
vendored
8
.github/workflows/release-desktop.yml
vendored
@@ -98,6 +98,14 @@ jobs:
|
|||||||
working-directory: apps/desktop/artifacts
|
working-directory: apps/desktop/artifacts
|
||||||
run: mv "Bitwarden-${PKG_VERSION}-universal.pkg" "Bitwarden-${PKG_VERSION}-universal.pkg.archive"
|
run: mv "Bitwarden-${PKG_VERSION}-universal.pkg" "Bitwarden-${PKG_VERSION}-universal.pkg.archive"
|
||||||
|
|
||||||
|
- name: Rename .tar.gz to include version
|
||||||
|
env:
|
||||||
|
PKG_VERSION: ${{ steps.version.outputs.version }}
|
||||||
|
working-directory: apps/desktop/artifacts
|
||||||
|
run: |
|
||||||
|
mv "bitwarden_desktop_x64.tar.gz" "bitwarden_${PKG_VERSION}_x64.tar.gz"
|
||||||
|
mv "bitwarden_desktop_arm64.tar.gz" "bitwarden_${PKG_VERSION}_arm64.tar.gz"
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
|
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0
|
||||||
if: ${{ steps.release_channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }}
|
if: ${{ steps.release_channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Trigger test-all workflow in browser-interactions-testing
|
- name: Trigger test-all workflow in browser-interactions-testing
|
||||||
if: steps.changed-files.outputs.monitored == 'true'
|
if: steps.changed-files.outputs.monitored == 'true'
|
||||||
uses: peter-evans/repository-dispatch@5fc4efd1a4797ddb68ffd0714a238564e4cc0e6f # v4.0.0
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
repository: "bitwarden/browser-interactions-testing"
|
repository: "bitwarden/browser-interactions-testing"
|
||||||
|
|||||||
@@ -436,8 +436,8 @@
|
|||||||
"sync": {
|
"sync": {
|
||||||
"message": "Sync"
|
"message": "Sync"
|
||||||
},
|
},
|
||||||
"syncVaultNow": {
|
"syncNow": {
|
||||||
"message": "Sync vault now"
|
"message": "Sync now"
|
||||||
},
|
},
|
||||||
"lastSync": {
|
"lastSync": {
|
||||||
"message": "Last sync:"
|
"message": "Last sync:"
|
||||||
@@ -455,9 +455,6 @@
|
|||||||
"bitWebVaultApp": {
|
"bitWebVaultApp": {
|
||||||
"message": "Bitwarden web app"
|
"message": "Bitwarden web app"
|
||||||
},
|
},
|
||||||
"importItems": {
|
|
||||||
"message": "Import items"
|
|
||||||
},
|
|
||||||
"select": {
|
"select": {
|
||||||
"message": "Select"
|
"message": "Select"
|
||||||
},
|
},
|
||||||
@@ -1325,8 +1322,11 @@
|
|||||||
"exportFrom": {
|
"exportFrom": {
|
||||||
"message": "Export from"
|
"message": "Export from"
|
||||||
},
|
},
|
||||||
"exportVault": {
|
"export": {
|
||||||
"message": "Export vault"
|
"message": "Export"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"message": "Import"
|
||||||
},
|
},
|
||||||
"fileFormat": {
|
"fileFormat": {
|
||||||
"message": "File format"
|
"message": "File format"
|
||||||
@@ -1475,6 +1475,9 @@
|
|||||||
"selectFile": {
|
"selectFile": {
|
||||||
"message": "Select a file"
|
"message": "Select a file"
|
||||||
},
|
},
|
||||||
|
"itemsTransferred": {
|
||||||
|
"message": "Items transferred"
|
||||||
|
},
|
||||||
"maxFileSize": {
|
"maxFileSize": {
|
||||||
"message": "Maximum file size is 500 MB."
|
"message": "Maximum file size is 500 MB."
|
||||||
},
|
},
|
||||||
@@ -3249,9 +3252,6 @@
|
|||||||
"copyCustomFieldNameNotUnique": {
|
"copyCustomFieldNameNotUnique": {
|
||||||
"message": "No unique identifier found."
|
"message": "No unique identifier found."
|
||||||
},
|
},
|
||||||
"removeMasterPasswordForOrganizationUserKeyConnector": {
|
|
||||||
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
|
|
||||||
},
|
|
||||||
"organizationName": {
|
"organizationName": {
|
||||||
"message": "Organization name"
|
"message": "Organization name"
|
||||||
},
|
},
|
||||||
@@ -4215,10 +4215,6 @@
|
|||||||
"ignore": {
|
"ignore": {
|
||||||
"message": "Ignore"
|
"message": "Ignore"
|
||||||
},
|
},
|
||||||
"importData": {
|
|
||||||
"message": "Import data",
|
|
||||||
"description": "Used for the header of the import dialog, the import button and within the file-password-prompt"
|
|
||||||
},
|
|
||||||
"importError": {
|
"importError": {
|
||||||
"message": "Import error"
|
"message": "Import error"
|
||||||
},
|
},
|
||||||
@@ -5888,6 +5884,45 @@
|
|||||||
"cardNumberLabel": {
|
"cardNumberLabel": {
|
||||||
"message": "Card number"
|
"message": "Card number"
|
||||||
},
|
},
|
||||||
|
"removeMasterPasswordForOrgUserKeyConnector":{
|
||||||
|
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
|
||||||
|
},
|
||||||
|
"continueWithLogIn": {
|
||||||
|
"message": "Continue with log in"
|
||||||
|
},
|
||||||
|
"doNotContinue": {
|
||||||
|
"message": "Do not continue"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"message": "Domain"
|
||||||
|
},
|
||||||
|
"keyConnectorDomainTooltip": {
|
||||||
|
"message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin."
|
||||||
|
},
|
||||||
|
"verifyYourOrganization": {
|
||||||
|
"message": "Verify your organization to log in"
|
||||||
|
},
|
||||||
|
"organizationVerified":{
|
||||||
|
"message": "Organization verified"
|
||||||
|
},
|
||||||
|
"domainVerified":{
|
||||||
|
"message": "Domain verified"
|
||||||
|
},
|
||||||
|
"leaveOrganizationContent": {
|
||||||
|
"message": "If you don't verify your organization, your access to the organization will be revoked."
|
||||||
|
},
|
||||||
|
"leaveNow": {
|
||||||
|
"message": "Leave now"
|
||||||
|
},
|
||||||
|
"verifyYourDomainToLogin": {
|
||||||
|
"message": "Verify your domain to log in"
|
||||||
|
},
|
||||||
|
"verifyYourDomainDescription": {
|
||||||
|
"message": "To continue with log in, verify this domain."
|
||||||
|
},
|
||||||
|
"confirmKeyConnectorOrganizationUserDescription": {
|
||||||
|
"message": "To continue with log in, verify the organization and domain."
|
||||||
|
},
|
||||||
"sessionTimeoutSettingsAction": {
|
"sessionTimeoutSettingsAction": {
|
||||||
"message": "Timeout action"
|
"message": "Timeout action"
|
||||||
},
|
},
|
||||||
@@ -5937,5 +5972,53 @@
|
|||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
"message": "Upgrade"
|
"message": "Upgrade"
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogTitle": {
|
||||||
|
"message": "Are you sure you want to leave?"
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogContentOne": {
|
||||||
|
"message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features."
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogContentTwo": {
|
||||||
|
"message": "Contact your admin to regain access."
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogConfirmButton": {
|
||||||
|
"message": "Leave $ORGANIZATION$",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"howToManageMyVault": {
|
||||||
|
"message": "How do I manage my vault?"
|
||||||
|
},
|
||||||
|
"transferItemsToOrganizationTitle": {
|
||||||
|
"message": "Transfer items to $ORGANIZATION$",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transferItemsToOrganizationContent": {
|
||||||
|
"message": "$ORGANIZATION$ is requiring all items to be owned by the organization for security and compliance. Click accept to transfer ownership of your items.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acceptTransfer": {
|
||||||
|
"message": "Accept transfer"
|
||||||
|
},
|
||||||
|
"declineAndLeave": {
|
||||||
|
"message": "Decline and leave"
|
||||||
|
},
|
||||||
|
"whyAmISeeingThis": {
|
||||||
|
"message": "Why am I seeing this?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<button
|
<button
|
||||||
*ngIf="currentAccount$ | async as currentAccount; else defaultButton"
|
*ngIf="currentAccount$ | async as currentAccount; else defaultButton"
|
||||||
type="button"
|
type="button"
|
||||||
class="tw-rounded-full hover:tw-outline hover:tw-outline-1 hover:tw-outline-offset-1 hover:tw-outline-primary-600"
|
class="tw-rounded-full hover:tw-outline hover:tw-outline-1 hover:tw-outline-primary-600"
|
||||||
(click)="currentAccountClicked()"
|
(click)="currentAccountClicked()"
|
||||||
>
|
>
|
||||||
<span class="tw-sr-only"> {{ "bitwardenAccount" | i18n }} {{ currentAccount.email }}</span>
|
<span class="tw-sr-only"> {{ "bitwardenAccount" | i18n }} {{ currentAccount.email }}</span>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
} from "@bitwarden/common/platform/abstractions/environment.service";
|
} from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { AccountSwitcherService } from "./account-switcher.service";
|
import { AccountSwitcherService } from "./account-switcher.service";
|
||||||
@@ -71,11 +72,10 @@ describe("AccountSwitcherService", () => {
|
|||||||
|
|
||||||
describe("availableAccounts$", () => {
|
describe("availableAccounts$", () => {
|
||||||
it("should return all logged in accounts and an add account option when accounts are less than 5", async () => {
|
it("should return all logged in accounts and an add account option when accounts are less than 5", async () => {
|
||||||
const accountInfo: AccountInfo = {
|
const accountInfo = mockAccountInfoWith({
|
||||||
name: "Test User 1",
|
name: "Test User 1",
|
||||||
email: "test1@email.com",
|
email: "test1@email.com",
|
||||||
emailVerified: true,
|
});
|
||||||
};
|
|
||||||
|
|
||||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||||
accountsSubject.next({ ["1" as UserId]: accountInfo, ["2" as UserId]: accountInfo });
|
accountsSubject.next({ ["1" as UserId]: accountInfo, ["2" as UserId]: accountInfo });
|
||||||
@@ -109,11 +109,10 @@ describe("AccountSwitcherService", () => {
|
|||||||
const seedAccounts: Record<UserId, AccountInfo> = {};
|
const seedAccounts: Record<UserId, AccountInfo> = {};
|
||||||
const seedStatuses: Record<UserId, AuthenticationStatus> = {};
|
const seedStatuses: Record<UserId, AuthenticationStatus> = {};
|
||||||
for (let i = 0; i < numberOfAccounts; i++) {
|
for (let i = 0; i < numberOfAccounts; i++) {
|
||||||
seedAccounts[`${i}` as UserId] = {
|
seedAccounts[`${i}` as UserId] = mockAccountInfoWith({
|
||||||
email: `test${i}@email.com`,
|
email: `test${i}@email.com`,
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User ${i}",
|
name: "Test User ${i}",
|
||||||
};
|
});
|
||||||
seedStatuses[`${i}` as UserId] = AuthenticationStatus.Unlocked;
|
seedStatuses[`${i}` as UserId] = AuthenticationStatus.Unlocked;
|
||||||
}
|
}
|
||||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||||
@@ -133,11 +132,10 @@ describe("AccountSwitcherService", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it("excludes logged out accounts", async () => {
|
it("excludes logged out accounts", async () => {
|
||||||
const user1AccountInfo: AccountInfo = {
|
const user1AccountInfo = mockAccountInfoWith({
|
||||||
name: "Test User 1",
|
name: "Test User 1",
|
||||||
email: "",
|
email: "",
|
||||||
emailVerified: true,
|
});
|
||||||
};
|
|
||||||
accountsSubject.next({ ["1" as UserId]: user1AccountInfo });
|
accountsSubject.next({ ["1" as UserId]: user1AccountInfo });
|
||||||
authStatusSubject.next({ ["1" as UserId]: AuthenticationStatus.LoggedOut });
|
authStatusSubject.next({ ["1" as UserId]: AuthenticationStatus.LoggedOut });
|
||||||
accountsSubject.next({
|
accountsSubject.next({
|
||||||
|
|||||||
@@ -102,6 +102,36 @@ describe("ExtensionLoginComponentService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("redirectToSsoLoginWithOrganizationSsoIdentifier", () => {
|
||||||
|
it("launches SSO browser window with correct Url", async () => {
|
||||||
|
const email = "test@bitwarden.com";
|
||||||
|
const state = "testState";
|
||||||
|
const expectedState = "testState:clientId=browser";
|
||||||
|
const codeVerifier = "testCodeVerifier";
|
||||||
|
const codeChallenge = "testCodeChallenge";
|
||||||
|
const orgSsoIdentifier = "org-sso-identifier";
|
||||||
|
|
||||||
|
passwordGenerationService.generatePassword.mockResolvedValueOnce(state);
|
||||||
|
passwordGenerationService.generatePassword.mockResolvedValueOnce(codeVerifier);
|
||||||
|
jest.spyOn(Utils, "fromBufferToUrlB64").mockReturnValue(codeChallenge);
|
||||||
|
|
||||||
|
await service.redirectToSsoLoginWithOrganizationSsoIdentifier(email, orgSsoIdentifier);
|
||||||
|
|
||||||
|
expect(ssoUrlService.buildSsoUrl).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
|
);
|
||||||
|
expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(expectedState);
|
||||||
|
expect(ssoLoginService.setCodeVerifier).toHaveBeenCalledWith(codeVerifier);
|
||||||
|
expect(platformUtilsService.launchUri).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("showBackButton", () => {
|
describe("showBackButton", () => {
|
||||||
it("sets showBackButton in extensionAnonLayoutWrapperDataService", () => {
|
it("sets showBackButton in extensionAnonLayoutWrapperDataService", () => {
|
||||||
service.showBackButton(true);
|
service.showBackButton(true);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export class ExtensionLoginComponentService
|
|||||||
email: string,
|
email: string,
|
||||||
state: string,
|
state: string,
|
||||||
codeChallenge: string,
|
codeChallenge: string,
|
||||||
|
orgSsoIdentifier?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const env = await firstValueFrom(this.environmentService.environment$);
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
const webVaultUrl = env.getWebVaultUrl();
|
const webVaultUrl = env.getWebVaultUrl();
|
||||||
@@ -60,6 +61,7 @@ export class ExtensionLoginComponentService
|
|||||||
state,
|
state,
|
||||||
codeChallenge,
|
codeChallenge,
|
||||||
email,
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.platformUtilsService.launchUri(webAppSsoUrl);
|
this.platformUtilsService.launchUri(webAppSsoUrl);
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { DeviceManagementComponentServiceAbstraction } from "@bitwarden/angular/
|
|||||||
/**
|
/**
|
||||||
* Browser extension implementation of the device management component service
|
* Browser extension implementation of the device management component service
|
||||||
*/
|
*/
|
||||||
export class ExtensionDeviceManagementComponentService
|
export class ExtensionDeviceManagementComponentService implements DeviceManagementComponentServiceAbstraction {
|
||||||
implements DeviceManagementComponentServiceAbstraction
|
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Don't show header information in browser extension client
|
* Don't show header information in browser extension client
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { BehaviorSubject, firstValueFrom, of } from "rxjs";
|
|||||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||||
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";
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||||
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
||||||
@@ -17,6 +17,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
|||||||
import { ThemeTypes } from "@bitwarden/common/platform/enums";
|
import { ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||||
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
||||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@@ -80,11 +81,12 @@ describe("NotificationBackground", () => {
|
|||||||
const organizationService = mock<OrganizationService>();
|
const organizationService = mock<OrganizationService>();
|
||||||
|
|
||||||
const userId = "testId" as UserId;
|
const userId = "testId" as UserId;
|
||||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
const activeAccountSubject = new BehaviorSubject({
|
||||||
id: userId,
|
id: userId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
|
|||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||||
@@ -123,9 +124,10 @@ describe("context-menu", () => {
|
|||||||
autofillSettingsService.enableContextMenu$ = of(true);
|
autofillSettingsService.enableContextMenu$ = of(true);
|
||||||
accountService.activeAccount$ = of({
|
accountService.activeAccount$ = of({
|
||||||
id: "userId" as UserId,
|
id: "userId" as UserId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "",
|
email: "",
|
||||||
emailVerified: false,
|
|
||||||
name: undefined,
|
name: undefined,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -120,9 +120,7 @@ export type BrowserFido2ParentWindowReference = chrome.tabs.Tab;
|
|||||||
* Browser implementation of the {@link Fido2UserInterfaceService}.
|
* Browser implementation of the {@link Fido2UserInterfaceService}.
|
||||||
* The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it.
|
* The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it.
|
||||||
*/
|
*/
|
||||||
export class BrowserFido2UserInterfaceService
|
export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction<BrowserFido2ParentWindowReference> {
|
||||||
implements Fido2UserInterfaceServiceAbstraction<BrowserFido2ParentWindowReference>
|
|
||||||
{
|
|
||||||
constructor(private authService: AuthService) {}
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
async newSession(
|
async newSession(
|
||||||
|
|||||||
@@ -129,7 +129,12 @@ export class AutofillInlineMenuContainer {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
const isExtensionProtocol = /^[a-z]+(-[a-z]+)?-extension:$/i.test(urlObj.protocol);
|
const extensionProtocols = new Set([
|
||||||
|
"chrome-extension:",
|
||||||
|
"moz-extension:",
|
||||||
|
"safari-web-extension:",
|
||||||
|
]);
|
||||||
|
const isExtensionProtocol = extensionProtocols.has(urlObj.protocol);
|
||||||
|
|
||||||
if (!isExtensionProtocol) {
|
if (!isExtensionProtocol) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ import {
|
|||||||
OverlayNotificationsExtensionMessageHandlers,
|
OverlayNotificationsExtensionMessageHandlers,
|
||||||
} from "../abstractions/overlay-notifications-content.service";
|
} from "../abstractions/overlay-notifications-content.service";
|
||||||
|
|
||||||
export class OverlayNotificationsContentService
|
export class OverlayNotificationsContentService implements OverlayNotificationsContentServiceInterface {
|
||||||
implements OverlayNotificationsContentServiceInterface
|
|
||||||
{
|
|
||||||
private notificationBarRootElement: HTMLElement | null = null;
|
private notificationBarRootElement: HTMLElement | null = null;
|
||||||
private notificationBarElement: HTMLElement | null = null;
|
private notificationBarElement: HTMLElement | null = null;
|
||||||
private notificationBarIframeElement: HTMLIFrameElement | null = null;
|
private notificationBarIframeElement: HTMLIFrameElement | null = null;
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ import {
|
|||||||
} from "./autofill-constants";
|
} from "./autofill-constants";
|
||||||
import AutofillService from "./autofill.service";
|
import AutofillService from "./autofill.service";
|
||||||
|
|
||||||
export class InlineMenuFieldQualificationService
|
export class InlineMenuFieldQualificationService implements InlineMenuFieldQualificationServiceInterface {
|
||||||
implements InlineMenuFieldQualificationServiceInterface
|
|
||||||
{
|
|
||||||
private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
||||||
private excludedAutofillFieldTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes);
|
private excludedAutofillFieldTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes);
|
||||||
private usernameFieldTypes = new Set(["text", "email", "number", "tel"]);
|
private usernameFieldTypes = new Set(["text", "email", "number", "tel"]);
|
||||||
|
|||||||
@@ -841,10 +841,7 @@ export default class MainBackground {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.pinService = new PinService(
|
this.pinService = new PinService(
|
||||||
this.accountService,
|
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.kdfConfigService,
|
|
||||||
this.keyGenerationService,
|
|
||||||
this.logService,
|
this.logService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.sdkService,
|
this.sdkService,
|
||||||
@@ -1112,7 +1109,7 @@ export default class MainBackground {
|
|||||||
this.collectionService,
|
this.collectionService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.restrictedItemTypesService,
|
this.restrictedItemTypesService,
|
||||||
);
|
);
|
||||||
@@ -1120,7 +1117,7 @@ export default class MainBackground {
|
|||||||
this.individualVaultExportService = new IndividualVaultExportService(
|
this.individualVaultExportService = new IndividualVaultExportService(
|
||||||
this.folderService,
|
this.folderService,
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
@@ -1134,7 +1131,7 @@ export default class MainBackground {
|
|||||||
this.organizationVaultExportService = new OrganizationVaultExportService(
|
this.organizationVaultExportService = new OrganizationVaultExportService(
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.exportApiService,
|
this.exportApiService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ export class ForegroundBrowserBiometricsService extends BiometricsService {
|
|||||||
result: BiometricsStatus;
|
result: BiometricsStatus;
|
||||||
error: string;
|
error: string;
|
||||||
}>(BiometricsCommands.GetBiometricsStatusForUser, { userId: id });
|
}>(BiometricsCommands.GetBiometricsStatusForUser, { userId: id });
|
||||||
|
if (response != null) {
|
||||||
return response.result;
|
return response.result;
|
||||||
|
} else {
|
||||||
|
return BiometricsStatus.DesktopDisconnected;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getShouldAutopromptNow(): Promise<boolean> {
|
async getShouldAutopromptNow(): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
<popup-page>
|
|
||||||
<popup-header slot="header" pageTitle="{{ 'removeMasterPassword' | i18n }}">
|
|
||||||
<ng-container slot="end">
|
|
||||||
<app-pop-out></app-pop-out>
|
|
||||||
</ng-container>
|
|
||||||
</popup-header>
|
|
||||||
|
|
||||||
@if (loading) {
|
|
||||||
<div class="tw-text-center">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin bwi-2x tw-text-muted"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<p>{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "organizationName" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.name }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "keyConnectorDomain" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.keyConnectorUrl }}</p>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
bitButton
|
|
||||||
buttonType="primary"
|
|
||||||
block
|
|
||||||
(click)="convert()"
|
|
||||||
[disabled]="action"
|
|
||||||
class="tw-mb-2"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
*ngIf="continuing"
|
|
||||||
></i>
|
|
||||||
{{ "removeMasterPassword" | i18n }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button" bitButton block (click)="leave()" [disabled]="action">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-spin"
|
|
||||||
title="{{ 'loading' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
*ngIf="leaving"
|
|
||||||
></i>
|
|
||||||
{{ "leaveOrganization" | i18n }}
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</popup-page>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// FIXME (PM-22628): angular imports are forbidden in background
|
|
||||||
// eslint-disable-next-line no-restricted-imports
|
|
||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui";
|
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
|
||||||
selector: "app-remove-password",
|
|
||||||
templateUrl: "remove-password.component.html",
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
|
||||||
@@ -22,7 +22,7 @@ export type NavButton = {
|
|||||||
templateUrl: "popup-tab-navigation.component.html",
|
templateUrl: "popup-tab-navigation.component.html",
|
||||||
imports: [CommonModule, LinkModule, RouterModule, JslibModule, IconModule],
|
imports: [CommonModule, LinkModule, RouterModule, JslibModule, IconModule],
|
||||||
host: {
|
host: {
|
||||||
class: "tw-block tw-h-full tw-w-full tw-flex tw-flex-col",
|
class: "tw-block tw-size-full tw-flex tw-flex-col",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class PopupTabNavigationComponent {
|
export class PopupTabNavigationComponent {
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ export class PopupRouterCacheService {
|
|||||||
/**
|
/**
|
||||||
* Navigate back in history
|
* Navigate back in history
|
||||||
*/
|
*/
|
||||||
async back() {
|
async back(updateCache = false) {
|
||||||
if (!BrowserPopupUtils.inPopup(window)) {
|
if (!updateCache && !BrowserPopupUtils.inPopup(window)) {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ import {
|
|||||||
TwoFactorAuthGuard,
|
TwoFactorAuthGuard,
|
||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||||
import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui";
|
import {
|
||||||
|
LockComponent,
|
||||||
|
ConfirmKeyConnectorDomainComponent,
|
||||||
|
RemovePasswordComponent,
|
||||||
|
} from "@bitwarden/key-management-ui";
|
||||||
|
|
||||||
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component";
|
||||||
import { AuthExtensionRoute } from "../auth/popup/constants/auth-extension-route.constant";
|
import { AuthExtensionRoute } from "../auth/popup/constants/auth-extension-route.constant";
|
||||||
@@ -59,7 +63,6 @@ import { NotificationsSettingsComponent } from "../autofill/popup/settings/notif
|
|||||||
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
||||||
import { PhishingWarning } from "../dirt/phishing-detection/popup/phishing-warning.component";
|
import { PhishingWarning } from "../dirt/phishing-detection/popup/phishing-warning.component";
|
||||||
import { ProtectedByComponent } from "../dirt/phishing-detection/popup/protected-by-component";
|
import { ProtectedByComponent } from "../dirt/phishing-detection/popup/protected-by-component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
|
import BrowserPopupUtils from "../platform/browser/browser-popup-utils";
|
||||||
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
||||||
import { RouteCacheOptions } from "../platform/services/popup-view-cache-background.service";
|
import { RouteCacheOptions } from "../platform/services/popup-view-cache-background.service";
|
||||||
@@ -188,9 +191,22 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "remove-password",
|
path: "remove-password",
|
||||||
component: RemovePasswordComponent,
|
component: ExtensionAnonLayoutWrapperComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: RemovePasswordComponent,
|
||||||
|
data: {
|
||||||
|
pageTitle: {
|
||||||
|
key: "verifyYourOrganization",
|
||||||
|
},
|
||||||
|
showBackButton: false,
|
||||||
|
pageIcon: LockIcon,
|
||||||
|
} satisfies ExtensionAnonLayoutWrapperData,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "view-cipher",
|
path: "view-cipher",
|
||||||
@@ -646,7 +662,7 @@ const routes: Routes = [
|
|||||||
component: ConfirmKeyConnectorDomainComponent,
|
component: ConfirmKeyConnectorDomainComponent,
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "confirmKeyConnectorDomain",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
pageIcon: DomainIcon,
|
pageIcon: DomainIcon,
|
||||||
|
|||||||
@@ -13,8 +13,11 @@
|
|||||||
</bit-callout>
|
</bit-callout>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div [@routerTransition]="getRouteElevation(outlet)">
|
<!-- eslint-disable-next-line -->
|
||||||
|
<div class="tw-h-screen tw-w-screen">
|
||||||
|
<div [@routerTransition]="getRouteElevation(outlet)" class="tw-size-full">
|
||||||
<router-outlet #outlet="outlet"></router-outlet>
|
<router-outlet #outlet="outlet"></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
<bit-toast-container></bit-toast-container>
|
<bit-toast-container></bit-toast-container>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import { CurrentAccountComponent } from "../auth/popup/account-switching/current
|
|||||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||||
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
||||||
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";
|
||||||
@@ -85,13 +84,7 @@ import "../platform/popup/locales";
|
|||||||
CalloutModule,
|
CalloutModule,
|
||||||
LinkModule,
|
LinkModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [AppComponent, ColorPasswordPipe, ColorPasswordCountPipe, TabsV2Component],
|
||||||
AppComponent,
|
|
||||||
ColorPasswordPipe,
|
|
||||||
ColorPasswordCountPipe,
|
|
||||||
TabsV2Component,
|
|
||||||
RemovePasswordComponent,
|
|
||||||
],
|
|
||||||
exports: [CalloutModule],
|
exports: [CalloutModule],
|
||||||
providers: [CurrencyPipe, DatePipe],
|
providers: [CurrencyPipe, DatePipe],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
|||||||
@@ -1,31 +1,21 @@
|
|||||||
<popup-page [disablePadding]="true">
|
<popup-page [disablePadding]="true">
|
||||||
<popup-header
|
|
||||||
slot="header"
|
|
||||||
[background]="'alt'"
|
|
||||||
[showBackButton]="showBackButton"
|
|
||||||
[pageTitle]="''"
|
|
||||||
>
|
|
||||||
<div class="tw-w-32">
|
|
||||||
<bit-icon *ngIf="showLogo" [icon]="logo" [ariaLabel]="'appLogoLabel' | i18n"></bit-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-container slot="end">
|
|
||||||
<app-pop-out></app-pop-out>
|
|
||||||
<app-current-account *ngIf="showAcctSwitcher && hasLoggedInAccount"></app-current-account>
|
|
||||||
</ng-container>
|
|
||||||
</popup-header>
|
|
||||||
|
|
||||||
<auth-anon-layout
|
<auth-anon-layout
|
||||||
[title]="pageTitle"
|
[title]="pageTitle"
|
||||||
[subtitle]="pageSubtitle"
|
[subtitle]="pageSubtitle"
|
||||||
[icon]="pageIcon"
|
[icon]="pageIcon"
|
||||||
[showReadonlyHostname]="showReadonlyHostname"
|
[showReadonlyHostname]="showReadonlyHostname"
|
||||||
[hideLogo]="true"
|
[hideLogo]="!showLogo"
|
||||||
[maxWidth]="maxWidth"
|
[maxWidth]="maxWidth"
|
||||||
[hideFooter]="hideFooter"
|
[hideFooter]="hideFooter"
|
||||||
[hideCardWrapper]="hideCardWrapper"
|
[hideCardWrapper]="hideCardWrapper"
|
||||||
>
|
>
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
<div class="tw-flex tw-gap-2" slot="header-actions">
|
||||||
|
<app-pop-out></app-pop-out>
|
||||||
|
@if (showAcctSwitcher && hasLoggedInAccount) {
|
||||||
|
<app-current-account></app-current-account>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<router-outlet slot="secondary" name="secondary"></router-outlet>
|
<router-outlet slot="secondary" name="secondary"></router-outlet>
|
||||||
<router-outlet slot="environment-selector" name="environment-selector"></router-outlet>
|
<router-outlet slot="environment-selector" name="environment-selector"></router-outlet>
|
||||||
</auth-anon-layout>
|
</auth-anon-layout>
|
||||||
|
|||||||
@@ -76,11 +76,14 @@ const decorators = (options: {
|
|||||||
{
|
{
|
||||||
provide: AccountService,
|
provide: AccountService,
|
||||||
useValue: {
|
useValue: {
|
||||||
|
// We can't use mockAccountInfoWith() here because we can't take a dependency on @bitwarden/common/spec.
|
||||||
|
// This is because that package relies on jest dependencies that aren't available here.
|
||||||
activeAccount$: of({
|
activeAccount$: of({
|
||||||
id: "test-user-id" as UserId,
|
id: "test-user-id" as UserId,
|
||||||
name: "Test User 1",
|
name: "Test User 1",
|
||||||
email: "test@email.com",
|
email: "test@email.com",
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
|
creationDate: "2024-01-01T00:00:00.000Z",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -238,6 +241,11 @@ export const DefaultContentExample: Story = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
parameters: {
|
||||||
|
chromatic: {
|
||||||
|
viewports: [380, 1280],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dynamic Content Example
|
// Dynamic Content Example
|
||||||
|
|||||||
@@ -1,453 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
overflow: hidden;
|
|
||||||
min-height: 600px;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&.body-sm {
|
|
||||||
min-height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.body-xs {
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.body-xxs {
|
|
||||||
min-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.body-3xs {
|
|
||||||
min-height: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.body-full {
|
|
||||||
min-height: unset;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
& body {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
font-family: $font-family-sans-serif;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
line-height: $line-height-base;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
width: 380px;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
min-height: inherit;
|
|
||||||
overflow: hidden;
|
|
||||||
color: $text-color;
|
|
||||||
background-color: $background-color;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
font-family: $font-family-sans-serif;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:not(popup-page a, popup-tab-navigation a) {
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("primaryColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: darken(themed("primaryColor"), 6%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not(bit-form-field input, bit-search input, input[bitcheckbox]),
|
|
||||||
select:not(bit-form-field select),
|
|
||||||
textarea:not(bit-form-field textarea) {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
background-color: themed("inputBackgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not(input[bitcheckbox]),
|
|
||||||
select,
|
|
||||||
textarea,
|
|
||||||
button:not(bit-chip-select button) {
|
|
||||||
font-size: $font-size-base;
|
|
||||||
font-family: $font-family-sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type*="date"] {
|
|
||||||
@include themify($themes) {
|
|
||||||
color-scheme: themed("dateInputColorScheme");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-calendar-picker-indicator {
|
|
||||||
@include themify($themes) {
|
|
||||||
filter: themed("webkitCalendarPickerFilter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-calendar-picker-indicator:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
filter: themed("webkitCalendarPickerHoverFilter");
|
|
||||||
}
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-root > div {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main::-webkit-scrollbar,
|
|
||||||
cdk-virtual-scroll-viewport::-webkit-scrollbar,
|
|
||||||
.vault-select::-webkit-scrollbar {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main::-webkit-scrollbar-track,
|
|
||||||
.vault-select::-webkit-scrollbar-track {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdk-virtual-scroll-viewport::-webkit-scrollbar-track {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main::-webkit-scrollbar-thumb,
|
|
||||||
cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb,
|
|
||||||
.vault-select::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 10px;
|
|
||||||
margin-right: 1px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("scrollbarColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("scrollbarHoverColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header:not(bit-callout header, bit-dialog header, popup-page header) {
|
|
||||||
height: 44px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&:not(.no-theme) {
|
|
||||||
border-bottom: 1px solid #000000;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("headerColor");
|
|
||||||
background-color: themed("headerBackgroundColor");
|
|
||||||
border-bottom-color: themed("headerBorderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content > .right,
|
|
||||||
.header-content > .right > .right {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left,
|
|
||||||
.right {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
min-width: -webkit-min-content; /* Workaround to Chrome bug */
|
|
||||||
.header-icon {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
app-avatar {
|
|
||||||
max-height: 30px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-center {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-pop-out > button,
|
|
||||||
div > button:not(app-current-account button):not(.home-acc-switcher-btn),
|
|
||||||
div > a {
|
|
||||||
border: none;
|
|
||||||
padding: 0 10px;
|
|
||||||
text-decoration: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
white-space: pre;
|
|
||||||
|
|
||||||
&:not(.home-acc-switcher-btn):hover,
|
|
||||||
&:not(.home-acc-switcher-btn):focus {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("headerBackgroundHoverColor");
|
|
||||||
color: themed("headerColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[disabled] {
|
|
||||||
opacity: 0.65;
|
|
||||||
cursor: default !important;
|
|
||||||
background-color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
i + span {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app-pop-out {
|
|
||||||
display: flex;
|
|
||||||
padding-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-weight: bold;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
padding: 7px 10px;
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.bwi {
|
|
||||||
position: absolute;
|
|
||||||
top: 15px;
|
|
||||||
left: 20px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("headerInputPlaceholderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not(bit-form-field input) {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
padding: 5px 10px 5px 30px;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("headerInputBackgroundColor");
|
|
||||||
color: themed("headerInputColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&::selection {
|
|
||||||
@include themify($themes) {
|
|
||||||
// explicitly set text selection to invert foreground/background
|
|
||||||
background-color: themed("headerInputColor");
|
|
||||||
color: themed("headerInputBackgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("headerInputBackgroundFocusColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-input-placeholder {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("headerInputPlaceholderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/** make the cancel button visible in both dark/light themes **/
|
|
||||||
&[type="search"]::-webkit-search-cancel-button {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
height: 15px;
|
|
||||||
width: 15px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
mask-image: url("../images/close-button-white.svg");
|
|
||||||
-webkit-mask-image: url("../images/close-button-white.svg");
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("headerInputColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.left + .search,
|
|
||||||
.left + .sr-only + .search {
|
|
||||||
padding-left: 0;
|
|
||||||
|
|
||||||
.bwi {
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search + .right {
|
|
||||||
margin-left: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 15px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
app-root {
|
|
||||||
width: 100%;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main:not(popup-page main):not(auth-anon-layout main) {
|
|
||||||
position: absolute;
|
|
||||||
top: 44px;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-header {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.flex {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
height: calc(100% - 44px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-content,
|
|
||||||
.no-items,
|
|
||||||
.full-loading-spinner {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-items,
|
|
||||||
.full-loading-spinner {
|
|
||||||
text-align: center;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
|
|
||||||
.no-items-image {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/search-desktop" + themed("svgSuffix"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bwi {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("disabledIconColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cdk-virtual-scroll
|
|
||||||
.cdk-virtual-scroll-viewport {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdk-virtual-scroll-content-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
@@ -1,620 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.first {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-header {
|
|
||||||
margin: 0 10px 5px 10px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("headingColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-content {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
border-color: themed("borderColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-padded {
|
|
||||||
padding: 10px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.condensed .box-content-row,
|
|
||||||
.box-content-row.condensed {
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-hover .box-content-row,
|
|
||||||
.box-content-row.no-hover {
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.single-line .box-content-row,
|
|
||||||
.box-content-row.single-line {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.row-top-padding {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-footer {
|
|
||||||
margin: 0 5px 5px 5px;
|
|
||||||
padding: 0 10px 5px 10px;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
|
|
||||||
button.btn {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.btn.primary {
|
|
||||||
font-size: $font-size-base;
|
|
||||||
padding: 7px 15px;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
border-color: themed("borderHoverColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.list {
|
|
||||||
margin: 10px 0 20px 0;
|
|
||||||
.box-content {
|
|
||||||
.virtual-scroll-item {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-content-row {
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
// background-color: $background-color;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
background-color: themed("boxBackgroundColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.padded {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-hover {
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&.active {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("listItemBackgroundHoverColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-left: 5px solid #000000;
|
|
||||||
padding-left: 5px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
border-left-color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
.row-btn {
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text:not(.no-ellipsis),
|
|
||||||
.detail {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-main {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
align-items: normal;
|
|
||||||
|
|
||||||
.row-main-content {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.single-line {
|
|
||||||
.box-content-row {
|
|
||||||
display: flex;
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-content-row {
|
|
||||||
display: block;
|
|
||||||
padding: 5px 10px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
margin: 3px 5px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
&:before {
|
|
||||||
border: none;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.override-last:last-child:before {
|
|
||||||
border-bottom: 1px solid #000000;
|
|
||||||
@include themify($themes) {
|
|
||||||
border-bottom-color: themed("boxBorderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.last:last-child:before {
|
|
||||||
border-bottom: 1px solid #000000;
|
|
||||||
@include themify($themes) {
|
|
||||||
border-bottom-color: themed("boxBorderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&.active {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundHoverColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.pre {
|
|
||||||
white-space: pre;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.pre-wrap {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-label,
|
|
||||||
label {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-label {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-label {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
> a {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text,
|
|
||||||
.detail {
|
|
||||||
display: block;
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-right,
|
|
||||||
.txt-right {
|
|
||||||
float: right;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-main {
|
|
||||||
flex-grow: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-flex,
|
|
||||||
.box-content-row-flex,
|
|
||||||
&.box-content-row-checkbox,
|
|
||||||
&.box-content-row-link,
|
|
||||||
&.box-content-row-input,
|
|
||||||
&.box-content-row-slider,
|
|
||||||
&.box-content-row-multi {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
word-break: break-all;
|
|
||||||
|
|
||||||
&.box-content-row-word-break {
|
|
||||||
word-break: normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-multi {
|
|
||||||
input:not([type="checkbox"]) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input + label.sr-only + select {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> a,
|
|
||||||
> button {
|
|
||||||
padding: 8px 8px 8px 4px;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("dangerColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-multi,
|
|
||||||
&.box-content-row-newmulti {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-newmulti {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("primaryColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-checkbox,
|
|
||||||
&.box-content-row-link,
|
|
||||||
&.box-content-row-input,
|
|
||||||
&.box-content-row-slider {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin: 5px;
|
|
||||||
|
|
||||||
label,
|
|
||||||
.row-label {
|
|
||||||
font-size: $font-size-base;
|
|
||||||
display: block;
|
|
||||||
width: initial;
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> span {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> input {
|
|
||||||
margin: 0 0 0 auto;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
margin-right: 15px;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-checkbox-left {
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
> input {
|
|
||||||
margin: 0 15px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-input {
|
|
||||||
label {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
&[type="number"] {
|
|
||||||
max-width: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-content-row-slider {
|
|
||||||
input[type="range"] {
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="number"] {
|
|
||||||
width: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not([type="checkbox"]):not([type="radio"]),
|
|
||||||
textarea {
|
|
||||||
border: none;
|
|
||||||
width: 100%;
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
&::-webkit-input-placeholder {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("inputPlaceholderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not([type="file"]):focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #000000;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
padding: 7px 4px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
border-color: themed("inputBorderColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 5px;
|
|
||||||
|
|
||||||
&.action-buttons-fixed {
|
|
||||||
align-self: start;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-btn {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 10px 8px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("boxRowButtonColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("boxRowButtonHoverColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled,
|
|
||||||
&[disabled] {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("disabledIconColor");
|
|
||||||
opacity: themed("disabledBoxOpacity");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("disabledIconColor");
|
|
||||||
opacity: themed("disabledBoxOpacity");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor: default !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-pad .row-btn {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.box-draggable-row) {
|
|
||||||
.action-buttons .row-btn:last-child {
|
|
||||||
margin-right: -3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.box-draggable-row {
|
|
||||||
&.box-content-row-checkbox {
|
|
||||||
input[type="checkbox"] + .drag-handle {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.drag-handle {
|
|
||||||
cursor: move;
|
|
||||||
padding: 10px 2px 10px 8px;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.cdk-drag-preview {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
opacity: 0.8;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
select.field-type {
|
|
||||||
margin: 5px 0 0 25px;
|
|
||||||
width: calc(100% - 25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-width: 34px;
|
|
||||||
margin-left: -5px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.icon-small {
|
|
||||||
min-width: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
max-height: 20px;
|
|
||||||
max-width: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress {
|
|
||||||
display: flex;
|
|
||||||
height: 5px;
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 5px -15px -10px;
|
|
||||||
|
|
||||||
.progress-bar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
background-color: $brand-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-group {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
input {
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin: 0 0 0 5px;
|
|
||||||
flex-grow: 1;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.align-start {
|
|
||||||
align-items: start;
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin-top: -4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.truncate {
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
.box {
|
|
||||||
.box-content {
|
|
||||||
.box-content-row {
|
|
||||||
&.no-hover {
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("transparentColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
padding: 7px 15px;
|
|
||||||
border: 1px solid #000000;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("buttonBackgroundColor");
|
|
||||||
border-color: themed("buttonBorderColor");
|
|
||||||
color: themed("buttonColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.primary {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("buttonPrimaryColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("buttonDangerColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.callout-half {
|
|
||||||
font-weight: bold;
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:not([disabled]) {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: darken(themed("buttonBackgroundColor"), 1.5%);
|
|
||||||
border-color: darken(themed("buttonBorderColor"), 17%);
|
|
||||||
color: darken(themed("buttonColor"), 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.primary {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: darken(themed("buttonPrimaryColor"), 6%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.danger {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: darken(themed("buttonDangerColor"), 6%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus:not([disabled]) {
|
|
||||||
cursor: pointer;
|
|
||||||
outline: 0;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: darken(themed("buttonBackgroundColor"), 6%);
|
|
||||||
border-color: darken(themed("buttonBorderColor"), 25%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[disabled] {
|
|
||||||
opacity: 0.65;
|
|
||||||
cursor: default !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.block {
|
|
||||||
display: block;
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.link,
|
|
||||||
&.neutral {
|
|
||||||
border: none !important;
|
|
||||||
background: none !important;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
.btn {
|
|
||||||
&:focus {
|
|
||||||
outline: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.box-content-row {
|
|
||||||
display: block;
|
|
||||||
width: calc(100% - 10px);
|
|
||||||
text-align: left;
|
|
||||||
border-color: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-buttons {
|
|
||||||
.btn.block {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
html.browser_safari {
|
|
||||||
&.safari_height_fix {
|
|
||||||
body {
|
|
||||||
height: 360px !important;
|
|
||||||
|
|
||||||
&.body-xs {
|
|
||||||
height: 300px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.body-full {
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
.search .bwi {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left + .search .bwi {
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
&.login-page {
|
|
||||||
padding-top: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app-root {
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.theme_light app-root {
|
|
||||||
border-color: #777777;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
margin: 0 -15px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col {
|
|
||||||
flex-basis: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
small,
|
|
||||||
.small {
|
|
||||||
font-size: $font-size-small;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-primary {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("primaryColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-success {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("successColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-danger {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("dangerColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-info {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("infoColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-warning {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("warningColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-primary {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("primaryColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-success {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("successColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-muted {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-default {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-danger {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("dangerColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-info {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("infoColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-warning {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("warningColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-weight-semibold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.lead {
|
|
||||||
font-size: $font-size-large;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-right {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-bottom {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-margin {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-block {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.monospaced {
|
|
||||||
font-family: $font-family-monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.show-whitespace {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-responsive {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-rounded {
|
|
||||||
border-radius: $border-radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-index-top {
|
|
||||||
position: relative;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
|
||||||
position: absolute !important;
|
|
||||||
width: 1px !important;
|
|
||||||
height: 1px !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
margin: -1px !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
clip: rect(0, 0, 0, 0) !important;
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(:focus) > .exists-only-on-parent-focus {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-wrapper {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-number {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("passwordNumberColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-special {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("passwordSpecialColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-character {
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 30px;
|
|
||||||
height: 36px;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
&:nth-child(odd) {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("backgroundColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-count {
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 8px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("passwordCountText") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#duo-frame {
|
|
||||||
background: url("../images/loading.svg") 0 0 no-repeat;
|
|
||||||
width: 100%;
|
|
||||||
height: 470px;
|
|
||||||
margin-bottom: -10px;
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#web-authn-frame {
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
|
|
||||||
iframe {
|
|
||||||
border: none;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body.linux-webauthn {
|
|
||||||
width: 485px !important;
|
|
||||||
#web-authn-frame {
|
|
||||||
iframe {
|
|
||||||
width: 375px;
|
|
||||||
margin: 0 55px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app-root > #loading {
|
|
||||||
display: flex;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
color: $text-muted;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
app-vault-icon,
|
|
||||||
.app-vault-icon {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-image {
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 142px;
|
|
||||||
height: 21px;
|
|
||||||
background-size: 142px 21px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
@include themify($themes) {
|
|
||||||
background-image: url("../images/logo-" + themed("logoSuffix") + "@2x.png");
|
|
||||||
}
|
|
||||||
@media (min-width: 219px) {
|
|
||||||
width: 189px;
|
|
||||||
height: 28px;
|
|
||||||
background-size: 189px 28px;
|
|
||||||
}
|
|
||||||
@media (min-width: 314px) {
|
|
||||||
width: 284px;
|
|
||||||
height: 43px;
|
|
||||||
background-size: 284px 43px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[hidden] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.draggable {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="password"]::-ms-reveal {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&.flex-grow {
|
|
||||||
> * {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text selection styles
|
|
||||||
// Set explicit selection styles (assumes primary accent color has sufficient
|
|
||||||
// contrast against the background, so its inversion is also still readable)
|
|
||||||
// and suppress user selection for most elements (to make it more app-like)
|
|
||||||
|
|
||||||
:not(bit-form-field input)::selection {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("backgroundColor");
|
|
||||||
background-color: themed("primaryAccentColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
label,
|
|
||||||
a,
|
|
||||||
button,
|
|
||||||
p,
|
|
||||||
img,
|
|
||||||
.box-header,
|
|
||||||
.box-footer,
|
|
||||||
.callout,
|
|
||||||
.row-label,
|
|
||||||
.modal-title,
|
|
||||||
.overlay-container {
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
&.user-select {
|
|
||||||
user-select: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tweak for inconsistent line heights in cipher view */
|
|
||||||
.box-footer button,
|
|
||||||
.box-footer a {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Workaround for slow performance on external monitors on Chrome + MacOS
|
|
||||||
// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
|
|
||||||
@keyframes redraw {
|
|
||||||
0% {
|
|
||||||
opacity: 0.99;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html.force_redraw {
|
|
||||||
animation: redraw 1s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* override for vault icon in browser (pre extension refresh) */
|
|
||||||
app-vault-icon:not(app-vault-list-items-container app-vault-icon) > div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
float: left;
|
|
||||||
height: 36px;
|
|
||||||
width: 34px;
|
|
||||||
margin-left: -5px;
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
app-home {
|
|
||||||
position: fixed;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.center-content {
|
|
||||||
margin-top: -50px;
|
|
||||||
height: calc(100% + 50px);
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 284px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.lead {
|
|
||||||
margin: 30px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn + .btn {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.settings-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("mutedColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:hover):not(:focus) {
|
|
||||||
span {
|
|
||||||
clip: rect(0 0 0 0);
|
|
||||||
clip-path: inset(50%);
|
|
||||||
height: 1px;
|
|
||||||
overflow: hidden;
|
|
||||||
position: absolute;
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("primaryColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body.body-sm,
|
|
||||||
body.body-xs {
|
|
||||||
app-home {
|
|
||||||
.center-content {
|
|
||||||
margin-top: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.lead {
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body.body-full {
|
|
||||||
app-home {
|
|
||||||
.center-content {
|
|
||||||
margin-top: -80px;
|
|
||||||
height: calc(100% + 80px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.createAccountLink {
|
|
||||||
padding: 30px 10px 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remember-email-check {
|
|
||||||
padding-top: 18px;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-bottom: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-buttons > button {
|
|
||||||
margin: 15px 0 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.useBrowserlink {
|
|
||||||
margin-left: 5px;
|
|
||||||
margin-top: 20px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: $font-size-small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-browser-selector-dropdown {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("boxBackgroundColor");
|
|
||||||
}
|
|
||||||
padding: 8px;
|
|
||||||
width: 100%;
|
|
||||||
box-shadow:
|
|
||||||
0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
|
||||||
0 3px 1px -2px rgba(0, 0, 0, 0.12),
|
|
||||||
0 1px 5px 0 rgba(0, 0, 0, 0.2);
|
|
||||||
border-radius: $border-radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fido2-browser-selector-dropdown-item {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("textColor") !important;
|
|
||||||
}
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
padding: 0px 15px 0px 5px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
background-color: themed("listItemBackgroundHoverColor") !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Temporary fix for avatar, will not be required once we migrate to tailwind preflight **/
|
|
||||||
bit-avatar svg {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
@import "variables.scss";
|
|
||||||
|
|
||||||
@each $mfaType in $mfaTypes {
|
|
||||||
.mfaType#{$mfaType} {
|
|
||||||
content: url("../images/two-factor/" + $mfaType + ".png");
|
|
||||||
max-width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mfaType1 {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/two-factor/1" + themed("mfaLogoSuffix"));
|
|
||||||
max-width: 100px;
|
|
||||||
max-height: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mfaType7 {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/two-factor/7" + themed("mfaLogoSuffix"));
|
|
||||||
max-width: 100px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,50 @@
|
|||||||
@import "../../../../../libs/angular/src/scss/bwicons/styles/style.scss";
|
@import "../../../../../libs/angular/src/scss/bwicons/styles/style.scss";
|
||||||
@import "variables.scss";
|
@import "variables.scss";
|
||||||
@import "../../../../../libs/angular/src/scss/icons.scss";
|
@import "../../../../../libs/angular/src/scss/icons.scss";
|
||||||
@import "base.scss";
|
|
||||||
@import "grid.scss";
|
|
||||||
@import "box.scss";
|
|
||||||
@import "buttons.scss";
|
|
||||||
@import "misc.scss";
|
|
||||||
@import "environment.scss";
|
|
||||||
@import "pages.scss";
|
|
||||||
@import "plugins.scss";
|
|
||||||
@import "@angular/cdk/overlay-prebuilt.css";
|
@import "@angular/cdk/overlay-prebuilt.css";
|
||||||
@import "../../../../../libs/components/src/multi-select/scss/bw.theme";
|
@import "../../../../../libs/components/src/multi-select/scss/bw.theme";
|
||||||
|
|
||||||
|
.cdk-virtual-scroll-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MFA Types for logo styling with no dark theme alternative
|
||||||
|
$mfaTypes: 0, 2, 3, 4, 6;
|
||||||
|
|
||||||
|
@each $mfaType in $mfaTypes {
|
||||||
|
.mfaType#{$mfaType} {
|
||||||
|
content: url("../images/two-factor/" + $mfaType + ".png");
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfaType0 {
|
||||||
|
content: url("../images/two-factor/0.png");
|
||||||
|
max-width: 100px;
|
||||||
|
max-height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfaType1 {
|
||||||
|
max-width: 100px;
|
||||||
|
max-height: 45px;
|
||||||
|
|
||||||
|
&:is(.theme_light *) {
|
||||||
|
content: url("../images/two-factor/1.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(.theme_dark *) {
|
||||||
|
content: url("../images/two-factor/1-w.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mfaType7 {
|
||||||
|
max-width: 100px;
|
||||||
|
|
||||||
|
&:is(.theme_light *) {
|
||||||
|
content: url("../images/two-factor/7.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
&:is(.theme_dark *) {
|
||||||
|
content: url("../images/two-factor/7-w.png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,104 @@
|
|||||||
@import "../../../../../libs/components/src/tw-theme.css";
|
@import "../../../../../libs/components/src/tw-theme-preflight.css";
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
html {
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 600px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.body-sm {
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.body-xs {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.body-xxs {
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.body-3xs {
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.body-full {
|
||||||
|
min-height: unset;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& body {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html.browser_safari {
|
||||||
|
&.safari_height_fix {
|
||||||
|
body {
|
||||||
|
height: 360px !important;
|
||||||
|
|
||||||
|
&.body-xs {
|
||||||
|
height: 300px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.body-full {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app-root {
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.theme_light app-root {
|
||||||
|
border-color: #777777;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 380px;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
min-height: inherit;
|
||||||
|
overflow: hidden;
|
||||||
|
@apply tw-bg-background-alt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround for slow performance on external monitors on Chrome + MacOS
|
||||||
|
* See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64
|
||||||
|
*/
|
||||||
|
@keyframes redraw {
|
||||||
|
0% {
|
||||||
|
opacity: 0.99;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html.force_redraw {
|
||||||
|
animation: redraw 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text selection style:
|
||||||
|
* suppress user selection for most elements (to make it more app-like)
|
||||||
|
*/
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
label,
|
||||||
|
a,
|
||||||
|
button,
|
||||||
|
p,
|
||||||
|
img {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
/** Safari Support */
|
/** Safari Support */
|
||||||
@@ -19,4 +119,59 @@
|
|||||||
html:not(.browser_safari) .tw-styled-scrollbar {
|
html:not(.browser_safari) .tw-styled-scrollbar {
|
||||||
scrollbar-color: rgb(var(--color-secondary-500)) rgb(var(--color-background-alt));
|
scrollbar-color: rgb(var(--color-secondary-500)) rgb(var(--color-background-alt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#duo-frame {
|
||||||
|
background: url("../images/loading.svg") 0 0 no-repeat;
|
||||||
|
width: 100%;
|
||||||
|
height: 470px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#web-authn-frame {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.linux-webauthn {
|
||||||
|
width: 485px !important;
|
||||||
|
#web-authn-frame {
|
||||||
|
iframe {
|
||||||
|
width: 375px;
|
||||||
|
margin: 0 55px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app-root > #loading {
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@apply tw-text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text selection style:
|
||||||
|
* Set explicit selection styles (assumes primary accent color has sufficient
|
||||||
|
* contrast against the background, so its inversion is also still readable)
|
||||||
|
*/
|
||||||
|
:not(bit-form-field input)::selection {
|
||||||
|
@apply tw-text-contrast;
|
||||||
|
@apply tw-bg-primary-700;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,178 +1,42 @@
|
|||||||
$dark-icon-themes: "theme_dark";
|
/**
|
||||||
|
* DEPRECATED: DO NOT MODIFY OR USE!
|
||||||
|
*/
|
||||||
|
|
||||||
|
$dark-icon-themes: "theme_dark";
|
||||||
|
|
||||||
$font-family-sans-serif: Inter, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
$font-family-sans-serif: Inter, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
$font-size-base: 16px;
|
|
||||||
$font-size-large: 18px;
|
|
||||||
$font-size-xlarge: 22px;
|
|
||||||
$font-size-xxlarge: 28px;
|
|
||||||
$font-size-small: 12px;
|
|
||||||
$text-color: #000000;
|
$text-color: #000000;
|
||||||
$border-color: #f0f0f0;
|
|
||||||
$border-color-dark: #ddd;
|
$border-color-dark: #ddd;
|
||||||
$list-item-hover: #fbfbfb;
|
|
||||||
$list-icon-color: #767679;
|
|
||||||
$disabled-box-opacity: 1;
|
|
||||||
$border-radius: 6px;
|
|
||||||
$line-height-base: 1.42857143;
|
|
||||||
$icon-hover-color: lighten($text-color, 50%);
|
|
||||||
|
|
||||||
$mfaTypes: 0, 2, 3, 4, 6;
|
|
||||||
|
|
||||||
$gray: #555;
|
|
||||||
$gray-light: #777;
|
|
||||||
$text-muted: $gray-light;
|
|
||||||
|
|
||||||
$brand-primary: #175ddc;
|
$brand-primary: #175ddc;
|
||||||
$brand-danger: #c83522;
|
|
||||||
$brand-success: #017e45;
|
$brand-success: #017e45;
|
||||||
$brand-info: #555555;
|
|
||||||
$brand-warning: #8b6609;
|
|
||||||
$brand-primary-accent: #1252a3;
|
|
||||||
|
|
||||||
$background-color: #f0f0f0;
|
$background-color: #f0f0f0;
|
||||||
|
|
||||||
$box-background-color: white;
|
|
||||||
$box-background-hover-color: $list-item-hover;
|
|
||||||
$box-border-color: $border-color;
|
|
||||||
$border-color-alt: #c3c5c7;
|
|
||||||
|
|
||||||
$button-border-color: darken($border-color-dark, 12%);
|
|
||||||
$button-background-color: white;
|
|
||||||
$button-color: lighten($text-color, 40%);
|
|
||||||
$button-color-primary: darken($brand-primary, 8%);
|
$button-color-primary: darken($brand-primary, 8%);
|
||||||
$button-color-danger: darken($brand-danger, 10%);
|
|
||||||
|
|
||||||
$code-color: #c01176;
|
|
||||||
$code-color-dark: #f08dc7;
|
|
||||||
|
|
||||||
$themes: (
|
$themes: (
|
||||||
light: (
|
light: (
|
||||||
textColor: $text-color,
|
textColor: $text-color,
|
||||||
hoverColorTransparent: rgba($text-color, 0.15),
|
|
||||||
borderColor: $border-color-dark,
|
borderColor: $border-color-dark,
|
||||||
backgroundColor: $background-color,
|
backgroundColor: $background-color,
|
||||||
borderColorAlt: $border-color-alt,
|
|
||||||
backgroundColorAlt: #ffffff,
|
|
||||||
scrollbarColor: rgba(100, 100, 100, 0.2),
|
|
||||||
scrollbarHoverColor: rgba(100, 100, 100, 0.4),
|
|
||||||
boxBackgroundColor: $box-background-color,
|
|
||||||
boxBackgroundHoverColor: $box-background-hover-color,
|
|
||||||
boxBorderColor: $box-border-color,
|
|
||||||
tabBackgroundColor: #ffffff,
|
|
||||||
tabBackgroundHoverColor: $list-item-hover,
|
|
||||||
headerColor: #ffffff,
|
|
||||||
headerBackgroundColor: $brand-primary,
|
|
||||||
headerBackgroundHoverColor: rgba(255, 255, 255, 0.1),
|
|
||||||
headerBorderColor: $brand-primary,
|
|
||||||
headerInputBackgroundColor: darken($brand-primary, 8%),
|
|
||||||
headerInputBackgroundFocusColor: darken($brand-primary, 10%),
|
|
||||||
headerInputColor: #ffffff,
|
|
||||||
headerInputPlaceholderColor: lighten($brand-primary, 35%),
|
|
||||||
listItemBackgroundHoverColor: $list-item-hover,
|
|
||||||
disabledIconColor: $list-icon-color,
|
|
||||||
disabledBoxOpacity: $disabled-box-opacity,
|
|
||||||
headingColor: $gray-light,
|
|
||||||
labelColor: $gray-light,
|
|
||||||
mutedColor: $text-muted,
|
|
||||||
totpStrokeColor: $brand-primary,
|
|
||||||
boxRowButtonColor: $brand-primary,
|
|
||||||
boxRowButtonHoverColor: darken($brand-primary, 10%),
|
|
||||||
inputBorderColor: darken($border-color-dark, 7%),
|
inputBorderColor: darken($border-color-dark, 7%),
|
||||||
inputBackgroundColor: #ffffff,
|
inputBackgroundColor: #ffffff,
|
||||||
inputPlaceholderColor: lighten($gray-light, 35%),
|
|
||||||
buttonBackgroundColor: $button-background-color,
|
|
||||||
buttonBorderColor: $button-border-color,
|
|
||||||
buttonColor: $button-color,
|
|
||||||
buttonPrimaryColor: $button-color-primary,
|
buttonPrimaryColor: $button-color-primary,
|
||||||
buttonDangerColor: $button-color-danger,
|
|
||||||
primaryColor: $brand-primary,
|
primaryColor: $brand-primary,
|
||||||
primaryAccentColor: $brand-primary-accent,
|
|
||||||
dangerColor: $brand-danger,
|
|
||||||
successColor: $brand-success,
|
successColor: $brand-success,
|
||||||
infoColor: $brand-info,
|
|
||||||
warningColor: $brand-warning,
|
|
||||||
logoSuffix: "dark",
|
|
||||||
mfaLogoSuffix: ".png",
|
|
||||||
passwordNumberColor: #007fde,
|
passwordNumberColor: #007fde,
|
||||||
passwordSpecialColor: #c40800,
|
passwordSpecialColor: #c40800,
|
||||||
passwordCountText: #212529,
|
|
||||||
calloutBorderColor: $border-color-dark,
|
|
||||||
calloutBackgroundColor: $box-background-color,
|
|
||||||
toastTextColor: #ffffff,
|
|
||||||
svgSuffix: "-light.svg",
|
|
||||||
transparentColor: rgba(0, 0, 0, 0),
|
|
||||||
dateInputColorScheme: light,
|
|
||||||
// https://stackoverflow.com/a/53336754
|
|
||||||
webkitCalendarPickerFilter: invert(46%) sepia(69%) saturate(6397%) hue-rotate(211deg)
|
|
||||||
brightness(85%) contrast(103%),
|
|
||||||
// light has no hover so use same color
|
|
||||||
webkitCalendarPickerHoverFilter: invert(46%) sepia(69%) saturate(6397%) hue-rotate(211deg)
|
|
||||||
brightness(85%) contrast(103%),
|
|
||||||
codeColor: $code-color,
|
|
||||||
),
|
),
|
||||||
dark: (
|
dark: (
|
||||||
textColor: #ffffff,
|
textColor: #ffffff,
|
||||||
hoverColorTransparent: rgba($text-color, 0.15),
|
|
||||||
borderColor: #161c26,
|
borderColor: #161c26,
|
||||||
backgroundColor: #161c26,
|
backgroundColor: #161c26,
|
||||||
borderColorAlt: #6e788a,
|
|
||||||
backgroundColorAlt: #2f343d,
|
|
||||||
scrollbarColor: #6e788a,
|
|
||||||
scrollbarHoverColor: #8d94a5,
|
|
||||||
boxBackgroundColor: #2f343d,
|
|
||||||
boxBackgroundHoverColor: #3c424e,
|
|
||||||
boxBorderColor: #4c525f,
|
|
||||||
tabBackgroundColor: #2f343d,
|
|
||||||
tabBackgroundHoverColor: #3c424e,
|
|
||||||
headerColor: #ffffff,
|
|
||||||
headerBackgroundColor: #2f343d,
|
|
||||||
headerBackgroundHoverColor: #3c424e,
|
|
||||||
headerBorderColor: #161c26,
|
|
||||||
headerInputBackgroundColor: #3c424e,
|
|
||||||
headerInputBackgroundFocusColor: #4c525f,
|
|
||||||
headerInputColor: #ffffff,
|
|
||||||
headerInputPlaceholderColor: #bac0ce,
|
|
||||||
listItemBackgroundHoverColor: #3c424e,
|
|
||||||
disabledIconColor: #bac0ce,
|
|
||||||
disabledBoxOpacity: 0.5,
|
|
||||||
headingColor: #bac0ce,
|
|
||||||
labelColor: #bac0ce,
|
|
||||||
mutedColor: #bac0ce,
|
|
||||||
totpStrokeColor: #4c525f,
|
|
||||||
boxRowButtonColor: #bac0ce,
|
|
||||||
boxRowButtonHoverColor: #ffffff,
|
|
||||||
inputBorderColor: #4c525f,
|
inputBorderColor: #4c525f,
|
||||||
inputBackgroundColor: #2f343d,
|
inputBackgroundColor: #2f343d,
|
||||||
inputPlaceholderColor: #bac0ce,
|
|
||||||
buttonBackgroundColor: #3c424e,
|
|
||||||
buttonBorderColor: #4c525f,
|
|
||||||
buttonColor: #bac0ce,
|
|
||||||
buttonPrimaryColor: #6f9df1,
|
buttonPrimaryColor: #6f9df1,
|
||||||
buttonDangerColor: #ff8d85,
|
|
||||||
primaryColor: #6f9df1,
|
primaryColor: #6f9df1,
|
||||||
primaryAccentColor: #6f9df1,
|
|
||||||
dangerColor: #ff8d85,
|
|
||||||
successColor: #52e07c,
|
successColor: #52e07c,
|
||||||
infoColor: #a4b0c6,
|
|
||||||
warningColor: #ffeb66,
|
|
||||||
logoSuffix: "white",
|
|
||||||
mfaLogoSuffix: "-w.png",
|
|
||||||
passwordNumberColor: #6f9df1,
|
passwordNumberColor: #6f9df1,
|
||||||
passwordSpecialColor: #ff8d85,
|
passwordSpecialColor: #ff8d85,
|
||||||
passwordCountText: #ffffff,
|
|
||||||
calloutBorderColor: #4c525f,
|
|
||||||
calloutBackgroundColor: #3c424e,
|
|
||||||
toastTextColor: #1f242e,
|
|
||||||
svgSuffix: "-dark.svg",
|
|
||||||
transparentColor: rgba(0, 0, 0, 0),
|
|
||||||
dateInputColorScheme: dark,
|
|
||||||
// https://stackoverflow.com/a/53336754 - must prepend brightness(0) saturate(100%) to dark themed date inputs
|
|
||||||
webkitCalendarPickerFilter: brightness(0) saturate(100%) invert(86%) sepia(19%) saturate(152%)
|
|
||||||
hue-rotate(184deg) brightness(87%) contrast(93%),
|
|
||||||
webkitCalendarPickerHoverFilter: brightness(0) saturate(100%) invert(100%) sepia(0%)
|
|
||||||
saturate(0%) hue-rotate(93deg) brightness(103%) contrast(103%),
|
|
||||||
codeColor: $code-color-dark,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ import {
|
|||||||
DialogService,
|
DialogService,
|
||||||
ToastService,
|
ToastService,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { GeneratorServicesModule } from "@bitwarden/generator-components";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
import {
|
import {
|
||||||
BiometricsService,
|
BiometricsService,
|
||||||
@@ -743,7 +744,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [JslibServicesModule],
|
imports: [JslibServicesModule, GeneratorServicesModule],
|
||||||
declarations: [],
|
declarations: [],
|
||||||
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
||||||
providers: safeProviders,
|
providers: safeProviders,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
@@ -96,10 +97,11 @@ describe("SendV2Component", () => {
|
|||||||
useValue: {
|
useValue: {
|
||||||
activeAccount$: of({
|
activeAccount$: of({
|
||||||
id: "123",
|
id: "123",
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "test@email.com",
|
email: "test@email.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
}),
|
}),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ provide: AuthService, useValue: mock<AuthService>() },
|
{ provide: AuthService, useValue: mock<AuthService>() },
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<popup-page>
|
<popup-page>
|
||||||
<popup-header slot="header" [pageTitle]="'exportVault' | i18n" showBackButton>
|
<popup-header slot="header" [pageTitle]="'export' | i18n" showBackButton>
|
||||||
<ng-container slot="end">
|
<ng-container slot="end">
|
||||||
<app-pop-out></app-pop-out>
|
<app-pop-out></app-pop-out>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
bitFormButton
|
bitFormButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
>
|
>
|
||||||
{{ "exportVault" | i18n }}
|
{{ "export" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button bitButton type="button" buttonType="secondary" [popupBackAction]>
|
<button bitButton type="button" buttonType="secondary" [popupBackAction]>
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<popup-page>
|
<popup-page>
|
||||||
<popup-header slot="header" [pageTitle]="'importData' | i18n" showBackButton>
|
<popup-header slot="header" [pageTitle]="'import' | i18n" showBackButton>
|
||||||
<ng-container slot="end">
|
<ng-container slot="end">
|
||||||
<app-pop-out></app-pop-out>
|
<app-pop-out></app-pop-out>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
bitFormButton
|
bitFormButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
>
|
>
|
||||||
{{ "importData" | i18n }}
|
{{ "import" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</popup-footer>
|
</popup-footer>
|
||||||
</popup-page>
|
</popup-page>
|
||||||
|
|||||||
@@ -51,6 +51,6 @@ export class AttachmentsV2Component {
|
|||||||
|
|
||||||
/** Navigate the user back to the edit screen after uploading an attachment */
|
/** Navigate the user back to the edit screen after uploading an attachment */
|
||||||
async navigateBack() {
|
async navigateBack() {
|
||||||
await this.popupRouterCacheService.back();
|
await this.popupRouterCacheService.back(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
|
|||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
import { CipherId, OrganizationId, 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 { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
|
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
|
||||||
@@ -60,10 +61,11 @@ describe("OpenAttachmentsComponent", () => {
|
|||||||
const accountService = {
|
const accountService = {
|
||||||
activeAccount$: of({
|
activeAccount$: of({
|
||||||
id: mockUserId,
|
id: mockUserId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "test@email.com",
|
email: "test@email.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
}),
|
}),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
const formStatusChange$ = new BehaviorSubject<"enabled" | "disabled">("enabled");
|
const formStatusChange$ = new BehaviorSubject<"enabled" | "disabled">("enabled");
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<bit-item>
|
<bit-item>
|
||||||
<button type="button" bit-item-content (click)="import()">
|
<button type="button" bit-item-content (click)="import()">
|
||||||
<div class="tw-flex tw-items-center tw-justify-center tw-gap-2">
|
<div class="tw-flex tw-items-center tw-justify-center tw-gap-2">
|
||||||
<p>{{ "importItems" | i18n }}</p>
|
<p>{{ "import" | i18n }}</p>
|
||||||
<span
|
<span
|
||||||
*ngIf="emptyVaultImportBadge$ | async"
|
*ngIf="emptyVaultImportBadge$ | async"
|
||||||
bitBadge
|
bitBadge
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</bit-item>
|
</bit-item>
|
||||||
<bit-item>
|
<bit-item>
|
||||||
<a bit-item-content routerLink="/export">
|
<a bit-item-content routerLink="/export">
|
||||||
{{ "exportVault" | i18n }}
|
{{ "export" | i18n }}
|
||||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</bit-item>
|
</bit-item>
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
</bit-item>
|
</bit-item>
|
||||||
<bit-item>
|
<bit-item>
|
||||||
<button type="button" bit-item-content (click)="sync()">
|
<button type="button" bit-item-content (click)="sync()">
|
||||||
{{ "syncVaultNow" | i18n }}
|
{{ "syncNow" | i18n }}
|
||||||
<span slot="secondary">{{ lastSync }}</span>
|
<span slot="secondary">{{ lastSync }}</span>
|
||||||
<i slot="end" class="bwi bwi-refresh" aria-hidden="true"></i>
|
<i slot="end" class="bwi bwi-refresh" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ config.content = [
|
|||||||
"../../libs/vault/src/**/*.{html,ts}",
|
"../../libs/vault/src/**/*.{html,ts}",
|
||||||
"../../libs/pricing/src/**/*.{html,ts}",
|
"../../libs/pricing/src/**/*.{html,ts}",
|
||||||
];
|
];
|
||||||
|
config.corePlugins.preflight = true;
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
@@ -113,20 +113,14 @@ export class LoginCommand {
|
|||||||
} else if (options.sso != null && this.canInteract) {
|
} else if (options.sso != null && this.canInteract) {
|
||||||
// If the optional Org SSO Identifier isn't provided, the option value is `true`.
|
// If the optional Org SSO Identifier isn't provided, the option value is `true`.
|
||||||
const orgSsoIdentifier = options.sso === true ? null : options.sso;
|
const orgSsoIdentifier = options.sso === true ? null : options.sso;
|
||||||
const passwordOptions: any = {
|
const ssoPromptData = await this.makeSsoPromptData();
|
||||||
type: "password",
|
ssoCodeVerifier = ssoPromptData.ssoCodeVerifier;
|
||||||
length: 64,
|
|
||||||
uppercase: true,
|
|
||||||
lowercase: true,
|
|
||||||
numbers: true,
|
|
||||||
special: false,
|
|
||||||
};
|
|
||||||
const state = await this.passwordGenerationService.generatePassword(passwordOptions);
|
|
||||||
ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
|
||||||
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256");
|
|
||||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
|
||||||
try {
|
try {
|
||||||
const ssoParams = await this.openSsoPrompt(codeChallenge, state, orgSsoIdentifier);
|
const ssoParams = await this.openSsoPrompt(
|
||||||
|
ssoPromptData.codeChallenge,
|
||||||
|
ssoPromptData.state,
|
||||||
|
orgSsoIdentifier,
|
||||||
|
);
|
||||||
ssoCode = ssoParams.ssoCode;
|
ssoCode = ssoParams.ssoCode;
|
||||||
orgIdentifier = ssoParams.orgIdentifier;
|
orgIdentifier = ssoParams.orgIdentifier;
|
||||||
} catch {
|
} catch {
|
||||||
@@ -231,9 +225,43 @@ export class LoginCommand {
|
|||||||
new PasswordLoginCredentials(email, password, twoFactor),
|
new PasswordLoginCredentials(email, password, twoFactor),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin Acting on initial AuthResult
|
||||||
|
|
||||||
if (response.requiresEncryptionKeyMigration) {
|
if (response.requiresEncryptionKeyMigration) {
|
||||||
return Response.error(this.i18nService.t("legacyEncryptionUnsupported"));
|
return Response.error(this.i18nService.t("legacyEncryptionUnsupported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opting for not checking feature flag since the server will not respond with
|
||||||
|
// SsoOrganizationIdentifier if the feature flag is not enabled.
|
||||||
|
if (response.requiresSso && this.canInteract) {
|
||||||
|
const ssoPromptData = await this.makeSsoPromptData();
|
||||||
|
ssoCodeVerifier = ssoPromptData.ssoCodeVerifier;
|
||||||
|
try {
|
||||||
|
const ssoParams = await this.openSsoPrompt(
|
||||||
|
ssoPromptData.codeChallenge,
|
||||||
|
ssoPromptData.state,
|
||||||
|
response.ssoOrganizationIdentifier,
|
||||||
|
);
|
||||||
|
ssoCode = ssoParams.ssoCode;
|
||||||
|
orgIdentifier = ssoParams.orgIdentifier;
|
||||||
|
if (ssoCode != null && ssoCodeVerifier != null) {
|
||||||
|
response = await this.loginStrategyService.logIn(
|
||||||
|
new SsoLoginCredentials(
|
||||||
|
ssoCode,
|
||||||
|
ssoCodeVerifier,
|
||||||
|
this.ssoRedirectUri,
|
||||||
|
orgIdentifier,
|
||||||
|
undefined, // email to look up 2FA token not required as CLI can't remember 2FA token
|
||||||
|
twoFactor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return Response.badRequest("Something went wrong. Try again.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (response.requiresTwoFactor) {
|
if (response.requiresTwoFactor) {
|
||||||
const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null);
|
const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null);
|
||||||
if (twoFactorProviders.length === 0) {
|
if (twoFactorProviders.length === 0) {
|
||||||
@@ -279,6 +307,10 @@ export class LoginCommand {
|
|||||||
if (twoFactorToken == null && selectedProvider.type === TwoFactorProviderType.Email) {
|
if (twoFactorToken == null && selectedProvider.type === TwoFactorProviderType.Email) {
|
||||||
const emailReq = new TwoFactorEmailRequest();
|
const emailReq = new TwoFactorEmailRequest();
|
||||||
emailReq.email = await this.loginStrategyService.getEmail();
|
emailReq.email = await this.loginStrategyService.getEmail();
|
||||||
|
// if the user was logging in with SSO, we need to include the SSO session token
|
||||||
|
if (response.ssoEmail2FaSessionToken != null) {
|
||||||
|
emailReq.ssoEmail2FaSessionToken = response.ssoEmail2FaSessionToken;
|
||||||
|
}
|
||||||
emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash();
|
emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash();
|
||||||
await this.twoFactorApiService.postTwoFactorEmail(emailReq);
|
await this.twoFactorApiService.postTwoFactorEmail(emailReq);
|
||||||
}
|
}
|
||||||
@@ -324,6 +356,7 @@ export class LoginCommand {
|
|||||||
response = await this.loginStrategyService.logInNewDeviceVerification(newDeviceToken);
|
response = await this.loginStrategyService.logInNewDeviceVerification(newDeviceToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We check response two factor again here since MFA could fail based on the logic on ln 226
|
||||||
if (response.requiresTwoFactor) {
|
if (response.requiresTwoFactor) {
|
||||||
return Response.error("Login failed.");
|
return Response.error("Login failed.");
|
||||||
}
|
}
|
||||||
@@ -692,6 +725,27 @@ export class LoginCommand {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate SSO prompt data: code verifier, code challenge, and state
|
||||||
|
private async makeSsoPromptData(): Promise<{
|
||||||
|
ssoCodeVerifier: string;
|
||||||
|
codeChallenge: string;
|
||||||
|
state: string;
|
||||||
|
}> {
|
||||||
|
const passwordOptions: any = {
|
||||||
|
type: "password",
|
||||||
|
length: 64,
|
||||||
|
uppercase: true,
|
||||||
|
lowercase: true,
|
||||||
|
numbers: true,
|
||||||
|
special: false,
|
||||||
|
};
|
||||||
|
const state = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||||
|
const ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
|
||||||
|
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256");
|
||||||
|
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||||
|
return { ssoCodeVerifier, codeChallenge, state };
|
||||||
|
}
|
||||||
|
|
||||||
private async openSsoPrompt(
|
private async openSsoPrompt(
|
||||||
codeChallenge: string,
|
codeChallenge: string,
|
||||||
state: string,
|
state: string,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
|||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
@@ -48,9 +49,10 @@ describe("UnlockCommand", () => {
|
|||||||
const mockMasterPassword = "testExample";
|
const mockMasterPassword = "testExample";
|
||||||
const activeAccount: Account = {
|
const activeAccount: Account = {
|
||||||
id: "user-id" as UserId,
|
id: "user-id" as UserId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "user@example.com",
|
email: "user@example.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "User",
|
name: "User",
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
|
const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
|
||||||
const mockSessionKey = new Uint8Array(64) as CsprngArray;
|
const mockSessionKey = new Uint8Array(64) as CsprngArray;
|
||||||
|
|||||||
@@ -492,10 +492,7 @@ export class ServiceContainer {
|
|||||||
|
|
||||||
const pinStateService = new PinStateService(this.stateProvider);
|
const pinStateService = new PinStateService(this.stateProvider);
|
||||||
this.pinService = new PinService(
|
this.pinService = new PinService(
|
||||||
this.accountService,
|
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.kdfConfigService,
|
|
||||||
this.keyGenerationService,
|
|
||||||
this.logService,
|
this.logService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.sdkService,
|
this.sdkService,
|
||||||
@@ -908,7 +905,7 @@ export class ServiceContainer {
|
|||||||
this.collectionService,
|
this.collectionService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.accountService,
|
this.accountService,
|
||||||
this.restrictedItemTypesService,
|
this.restrictedItemTypesService,
|
||||||
);
|
);
|
||||||
@@ -916,7 +913,7 @@ export class ServiceContainer {
|
|||||||
this.individualExportService = new IndividualVaultExportService(
|
this.individualExportService = new IndividualVaultExportService(
|
||||||
this.folderService,
|
this.folderService,
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
@@ -930,7 +927,7 @@ export class ServiceContainer {
|
|||||||
this.organizationExportService = new OrganizationVaultExportService(
|
this.organizationExportService = new OrganizationVaultExportService(
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.vaultExportApiService,
|
this.vaultExportApiService,
|
||||||
this.pinService,
|
this.keyGenerationService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
|
|||||||
394
apps/desktop/desktop_native/Cargo.lock
generated
394
apps/desktop/desktop_native/Cargo.lock
generated
@@ -2,21 +2,6 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.24.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler2"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aead"
|
name = "aead"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -114,9 +99,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.94"
|
version = "1.0.100"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
@@ -138,14 +123,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ashpd"
|
name = "ashpd"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
|
checksum = "da0986d5b4f0802160191ad75f8d33ada000558757db3defb70299ca95d9fcbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -347,23 +332,8 @@ dependencies = [
|
|||||||
"mockall",
|
"mockall",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.75"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -457,7 +427,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -501,6 +471,12 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -509,9 +485,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
@@ -556,9 +532,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.46"
|
version = "1.2.49"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
|
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"shlex",
|
"shlex",
|
||||||
@@ -615,7 +591,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"oo7",
|
"oo7",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"security-framework",
|
"security-framework",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -624,7 +600,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"verifysign",
|
"verifysign",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -710,9 +686,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.6.0"
|
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 = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
@@ -771,16 +747,6 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ctor"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -868,7 +834,7 @@ dependencies = [
|
|||||||
"memsec",
|
"memsec",
|
||||||
"oo7",
|
"oo7",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"secmem-proc",
|
"secmem-proc",
|
||||||
"security-framework",
|
"security-framework",
|
||||||
@@ -878,13 +844,13 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"ssh-key",
|
"ssh-key",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"typenum",
|
"typenum",
|
||||||
"widestring",
|
"widestring",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
"windows-future",
|
"windows-future",
|
||||||
"zbus",
|
"zbus",
|
||||||
"zbus_polkit",
|
"zbus_polkit",
|
||||||
@@ -1410,17 +1376,11 @@ dependencies = [
|
|||||||
"polyval",
|
"polyval",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.31.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
@@ -1500,14 +1460,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "homedir"
|
name = "homedir"
|
||||||
version = "0.3.4"
|
version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5bdbbd5bc8c5749697ccaa352fa45aff8730cf21c68029c0eef1ffed7c3d6ba2"
|
checksum = "68df315d2857b2d8d2898be54a85e1d001bbbe0dbb5f8ef847b48dd3a23c4527"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"nix 0.29.0",
|
"nix",
|
||||||
"widestring",
|
"widestring",
|
||||||
"windows 0.57.0",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1664,6 +1624,16 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1686,7 +1656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
|
checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.53.3",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1842,15 +1812,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.8.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
|
||||||
dependencies = [
|
|
||||||
"adler2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@@ -1890,32 +1851,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi"
|
name = "napi"
|
||||||
version = "2.16.17"
|
version = "3.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
|
checksum = "f1b74e3dce5230795bb4d2821b941706dee733c7308752507254b0497f39cad7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"ctor 0.2.9",
|
"ctor",
|
||||||
"napi-derive",
|
"napi-build",
|
||||||
"napi-sys",
|
"napi-sys",
|
||||||
"once_cell",
|
"nohash-hasher",
|
||||||
|
"rustc-hash",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-build"
|
name = "napi-build"
|
||||||
version = "2.2.0"
|
version = "2.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03acbfa4f156a32188bfa09b86dc11a431b5725253fc1fc6f6df5bed273382c4"
|
checksum = "dcae8ad5609d14afb3a3b91dee88c757016261b151e9dcecabf1b2a31a6cab14"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-derive"
|
name = "napi-derive"
|
||||||
version = "2.16.13"
|
version = "3.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
|
checksum = "7552d5a579b834614bbd496db5109f1b9f1c758f08224b0dee1e408333adf0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"convert_case",
|
"convert_case",
|
||||||
|
"ctor",
|
||||||
"napi-derive-backend",
|
"napi-derive-backend",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1924,40 +1886,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-derive-backend"
|
name = "napi-derive-backend"
|
||||||
version = "1.0.75"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
|
checksum = "5f6a81ac7486b70f2532a289603340862c06eea5a1e650c1ffeda2ce1238516a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
|
||||||
"semver",
|
"semver",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "napi-sys"
|
name = "napi-sys"
|
||||||
version = "2.4.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
|
checksum = "3e4e7135a8f97aa0f1509cce21a8a1f9dcec1b50d8dee006b48a5adb69a9d64d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nix"
|
|
||||||
version = "0.29.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cfg-if",
|
|
||||||
"cfg_aliases",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.30.1"
|
version = "0.30.1"
|
||||||
@@ -1971,6 +1919,12 @@ dependencies = [
|
|||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
@@ -2174,15 +2128,6 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.36.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.3"
|
||||||
@@ -2191,9 +2136,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oo7"
|
name = "oo7"
|
||||||
version = "0.4.3"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cb23d3ec3527d65a83be1c1795cb883c52cfa57147d42acc797127df56fc489"
|
checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"ashpd",
|
"ashpd",
|
||||||
@@ -2209,7 +2154,7 @@ dependencies = [
|
|||||||
"num",
|
"num",
|
||||||
"num-bigint-dig",
|
"num-bigint-dig",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
@@ -2549,7 +2494,7 @@ dependencies = [
|
|||||||
name = "process_isolation"
|
name = "process_isolation"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctor 0.5.0",
|
"ctor",
|
||||||
"desktop_core",
|
"desktop_core",
|
||||||
"libc",
|
"libc",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -2592,9 +2537,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rand_core 0.9.3",
|
"rand_core 0.9.3",
|
||||||
@@ -2661,19 +2606,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.16",
|
"getrandom 0.2.16",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.17",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2749,10 +2682,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-hash"
|
||||||
version = "0.1.24"
|
version = "2.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
@@ -2799,6 +2732,12 @@ dependencies = [
|
|||||||
"rustix 1.0.7",
|
"rustix 1.0.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
@@ -2871,8 +2810,8 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"rustix 1.0.7",
|
"rustix 1.0.7",
|
||||||
"rustix-linux-procfs",
|
"rustix-linux-procfs",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.17",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3069,12 +3008,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.9"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
|
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3198,7 +3137,7 @@ dependencies = [
|
|||||||
"ntapi",
|
"ntapi",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-io-kit",
|
"objc2-io-kit",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3240,11 +3179,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.12",
|
"thiserror-impl 2.0.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3260,9 +3199,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.12"
|
version = "2.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3290,11 +3229,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.45.0"
|
version = "1.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
@@ -3304,14 +3242,14 @@ dependencies = [
|
|||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3320,9 +3258,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.13"
|
version = "0.7.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
|
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -3681,6 +3619,17 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -3746,6 +3695,51 @@ dependencies = [
|
|||||||
"wit-bindgen-rt",
|
"wit-bindgen-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-backend"
|
name = "wayland-backend"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
@@ -3853,16 +3847,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.57.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.61.1"
|
version = "0.61.1"
|
||||||
@@ -3870,7 +3854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections",
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
"windows-future",
|
"windows-future",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
"windows-numerics",
|
"windows-numerics",
|
||||||
@@ -3882,19 +3866,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement 0.57.0",
|
|
||||||
"windows-interface 0.57.0",
|
|
||||||
"windows-result 0.1.2",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3903,8 +3875,8 @@ version = "0.61.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement 0.60.0",
|
"windows-implement",
|
||||||
"windows-interface 0.59.1",
|
"windows-interface",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
"windows-result 0.3.4",
|
"windows-result 0.3.4",
|
||||||
"windows-strings 0.4.2",
|
"windows-strings 0.4.2",
|
||||||
@@ -3916,21 +3888,10 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
|
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.60.0"
|
version = "0.60.0"
|
||||||
@@ -3942,17 +3903,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.59.1"
|
version = "0.59.1"
|
||||||
@@ -3982,7 +3932,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3997,15 +3947,6 @@ dependencies = [
|
|||||||
"windows-strings 0.5.1",
|
"windows-strings 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -4263,8 +4204,8 @@ name = "windows_plugin_authenticator"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"windows 0.61.1",
|
"windows",
|
||||||
"windows-core 0.61.0",
|
"windows-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4435,9 +4376,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "5.11.0"
|
version = "5.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7"
|
checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
@@ -4453,14 +4394,15 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"hex",
|
"hex",
|
||||||
"nix 0.30.1",
|
"nix",
|
||||||
"ordered-stream",
|
"ordered-stream",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uds_windows",
|
"uds_windows",
|
||||||
"windows-sys 0.60.2",
|
"uuid",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
"winnow",
|
"winnow",
|
||||||
"zbus_macros",
|
"zbus_macros",
|
||||||
"zbus_names",
|
"zbus_names",
|
||||||
@@ -4469,9 +4411,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus_macros"
|
name = "zbus_macros"
|
||||||
version = "5.11.0"
|
version = "5.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca"
|
checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ publish = false
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
aes = "=0.8.4"
|
aes = "=0.8.4"
|
||||||
aes-gcm = "=0.10.3"
|
aes-gcm = "=0.10.3"
|
||||||
anyhow = "=1.0.94"
|
anyhow = "=1.0.100"
|
||||||
arboard = { version = "=3.6.1", default-features = false }
|
arboard = { version = "=3.6.1", default-features = false }
|
||||||
ashpd = "=0.11.0"
|
ashpd = "=0.12.0"
|
||||||
base64 = "=0.22.1"
|
base64 = "=0.22.1"
|
||||||
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "a641316227227f8777fdf56ac9fa2d6b5f7fe662" }
|
bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "a641316227227f8777fdf56ac9fa2d6b5f7fe662" }
|
||||||
byteorder = "=1.5.0"
|
byteorder = "=1.5.0"
|
||||||
bytes = "=1.10.1"
|
bytes = "=1.11.0"
|
||||||
cbc = "=0.1.2"
|
cbc = "=0.1.2"
|
||||||
chacha20poly1305 = "=0.10.1"
|
chacha20poly1305 = "=0.10.1"
|
||||||
core-foundation = "=0.10.1"
|
core-foundation = "=0.10.1"
|
||||||
@@ -37,18 +37,18 @@ ed25519 = "=2.2.3"
|
|||||||
embed_plist = "=1.2.2"
|
embed_plist = "=1.2.2"
|
||||||
futures = "=0.3.31"
|
futures = "=0.3.31"
|
||||||
hex = "=0.4.3"
|
hex = "=0.4.3"
|
||||||
homedir = "=0.3.4"
|
homedir = "=0.3.6"
|
||||||
interprocess = "=2.2.1"
|
interprocess = "=2.2.1"
|
||||||
libc = "=0.2.178"
|
libc = "=0.2.178"
|
||||||
linux-keyutils = "=0.2.4"
|
linux-keyutils = "=0.2.4"
|
||||||
memsec = "=0.7.0"
|
memsec = "=0.7.0"
|
||||||
napi = "=2.16.17"
|
napi = "=3.3.0"
|
||||||
napi-build = "=2.2.0"
|
napi-build = "=2.2.3"
|
||||||
napi-derive = "=2.16.13"
|
napi-derive = "=3.2.5"
|
||||||
oo7 = "=0.4.3"
|
oo7 = "=0.5.0"
|
||||||
pin-project = "=1.1.10"
|
pin-project = "=1.1.10"
|
||||||
pkcs8 = "=0.10.2"
|
pkcs8 = "=0.10.2"
|
||||||
rand = "=0.9.1"
|
rand = "=0.9.2"
|
||||||
rsa = "=0.9.6"
|
rsa = "=0.9.6"
|
||||||
russh-cryptovec = "=0.7.3"
|
russh-cryptovec = "=0.7.3"
|
||||||
scopeguard = "=1.2.0"
|
scopeguard = "=1.2.0"
|
||||||
@@ -61,9 +61,9 @@ sha2 = "=0.10.8"
|
|||||||
ssh-encoding = "=0.2.0"
|
ssh-encoding = "=0.2.0"
|
||||||
ssh-key = { version = "=0.6.7", default-features = false }
|
ssh-key = { version = "=0.6.7", default-features = false }
|
||||||
sysinfo = "=0.37.2"
|
sysinfo = "=0.37.2"
|
||||||
thiserror = "=2.0.12"
|
thiserror = "=2.0.17"
|
||||||
tokio = "=1.45.0"
|
tokio = "=1.48.0"
|
||||||
tokio-util = "=0.7.13"
|
tokio-util = "=0.7.17"
|
||||||
tracing = "=0.1.41"
|
tracing = "=0.1.41"
|
||||||
tracing-subscriber = { version = "=0.3.20", features = [
|
tracing-subscriber = { version = "=0.3.20", features = [
|
||||||
"fmt",
|
"fmt",
|
||||||
@@ -77,7 +77,7 @@ windows = "=0.61.1"
|
|||||||
windows-core = "=0.61.0"
|
windows-core = "=0.61.0"
|
||||||
windows-future = "=0.2.0"
|
windows-future = "=0.2.0"
|
||||||
windows-registry = "=0.6.1"
|
windows-registry = "=0.6.1"
|
||||||
zbus = "=5.11.0"
|
zbus = "=5.12.0"
|
||||||
zbus_polkit = "=5.0.0"
|
zbus_polkit = "=5.0.0"
|
||||||
zeroizing-alloc = "=0.1.0"
|
zeroizing-alloc = "=0.1.0"
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ const rustTargetsMap = {
|
|||||||
"aarch64-pc-windows-msvc": { nodeArch: 'arm64', platform: 'win32' },
|
"aarch64-pc-windows-msvc": { nodeArch: 'arm64', platform: 'win32' },
|
||||||
"x86_64-apple-darwin": { nodeArch: 'x64', platform: 'darwin' },
|
"x86_64-apple-darwin": { nodeArch: 'x64', platform: 'darwin' },
|
||||||
"aarch64-apple-darwin": { nodeArch: 'arm64', platform: 'darwin' },
|
"aarch64-apple-darwin": { nodeArch: 'arm64', platform: 'darwin' },
|
||||||
'x86_64-unknown-linux-musl': { nodeArch: 'x64', platform: 'linux' },
|
'x86_64-unknown-linux-gnu': { nodeArch: 'x64', platform: 'linux' },
|
||||||
'aarch64-unknown-linux-musl': { nodeArch: 'arm64', platform: 'linux' },
|
'aarch64-unknown-linux-gnu': { nodeArch: 'arm64', platform: 'linux' },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the dist directory exists
|
// Ensure the dist directory exists
|
||||||
@@ -113,8 +113,8 @@ if (process.platform === "linux") {
|
|||||||
|
|
||||||
platformTargets.forEach(([target, _]) => {
|
platformTargets.forEach(([target, _]) => {
|
||||||
installTarget(target);
|
installTarget(target);
|
||||||
buildNapiModule(target);
|
buildNapiModule(target, mode === "release");
|
||||||
buildProxyBin(target);
|
buildProxyBin(target, mode === "release");
|
||||||
buildImporterBinaries(target);
|
buildImporterBinaries(target, mode === "release");
|
||||||
buildProcessIsolation();
|
buildProcessIsolation();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::{
|
|||||||
pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[
|
pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
name: "Chrome",
|
name: "Chrome",
|
||||||
data_dir: &[".config/google-chrome"],
|
data_dir: &[".config/google-chrome", "snap/chromium/common/chromium"],
|
||||||
},
|
},
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
name: "Chromium",
|
name: "Chromium",
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ pub struct NativeImporterMetadata {
|
|||||||
/// Identifies the importer
|
/// Identifies the importer
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// Describes the strategies used to obtain imported data
|
/// Describes the strategies used to obtain imported data
|
||||||
pub loaders: Vec<&'static str>,
|
pub loaders: Vec<String>,
|
||||||
/// Identifies the instructions for the importer
|
/// Identifies the instructions for the importer
|
||||||
pub instructions: &'static str,
|
pub instructions: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a map of supported importers based on the current platform.
|
/// Returns a map of supported importers based on the current platform.
|
||||||
@@ -37,9 +37,9 @@ pub fn get_supported_importers<T: InstalledBrowserRetriever>(
|
|||||||
PLATFORM_SUPPORTED_BROWSERS.iter().map(|b| b.name).collect();
|
PLATFORM_SUPPORTED_BROWSERS.iter().map(|b| b.name).collect();
|
||||||
|
|
||||||
for (id, browser_name) in IMPORTERS {
|
for (id, browser_name) in IMPORTERS {
|
||||||
let mut loaders: Vec<&'static str> = vec!["file"];
|
let mut loaders: Vec<String> = vec!["file".to_string()];
|
||||||
if supported.contains(browser_name) {
|
if supported.contains(browser_name) {
|
||||||
loaders.push("chromium");
|
loaders.push("chromium".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if installed_browsers.contains(&browser_name.to_string()) {
|
if installed_browsers.contains(&browser_name.to_string()) {
|
||||||
@@ -48,7 +48,7 @@ pub fn get_supported_importers<T: InstalledBrowserRetriever>(
|
|||||||
NativeImporterMetadata {
|
NativeImporterMetadata {
|
||||||
id: id.to_string(),
|
id: id.to_string(),
|
||||||
loaders,
|
loaders,
|
||||||
instructions: "chromium",
|
instructions: "chromium".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -80,12 +80,9 @@ mod tests {
|
|||||||
map.keys().cloned().collect()
|
map.keys().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_loaders(
|
fn get_loaders(map: &HashMap<String, NativeImporterMetadata>, id: &str) -> HashSet<String> {
|
||||||
map: &HashMap<String, NativeImporterMetadata>,
|
|
||||||
id: &str,
|
|
||||||
) -> HashSet<&'static str> {
|
|
||||||
map.get(id)
|
map.get(id)
|
||||||
.map(|m| m.loaders.iter().copied().collect::<HashSet<_>>())
|
.map(|m| m.loaders.iter().cloned().collect::<HashSet<_>>())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +105,7 @@ mod tests {
|
|||||||
for (key, meta) in map.iter() {
|
for (key, meta) in map.iter() {
|
||||||
assert_eq!(&meta.id, key);
|
assert_eq!(&meta.id, key);
|
||||||
assert_eq!(meta.instructions, "chromium");
|
assert_eq!(meta.instructions, "chromium");
|
||||||
assert!(meta.loaders.contains(&"file"));
|
assert!(meta.loaders.contains(&"file".to_owned()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +145,7 @@ mod tests {
|
|||||||
for (key, meta) in map.iter() {
|
for (key, meta) in map.iter() {
|
||||||
assert_eq!(&meta.id, key);
|
assert_eq!(&meta.id, key);
|
||||||
assert_eq!(meta.instructions, "chromium");
|
assert_eq!(meta.instructions, "chromium");
|
||||||
assert!(meta.loaders.contains(&"file"));
|
assert!(meta.loaders.contains(&"file".to_owned()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +181,7 @@ mod tests {
|
|||||||
for (key, meta) in map.iter() {
|
for (key, meta) in map.iter() {
|
||||||
assert_eq!(&meta.id, key);
|
assert_eq!(&meta.id, key);
|
||||||
assert_eq!(meta.instructions, "chromium");
|
assert_eq!(meta.instructions, "chromium");
|
||||||
assert!(meta.loaders.contains(&"file"));
|
assert!(meta.loaders.contains(&"file".to_owned()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
428
apps/desktop/desktop_native/napi/index.d.ts
vendored
428
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -1,125 +1,7 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/* auto-generated by NAPI-RS */
|
/* auto-generated by NAPI-RS */
|
||||||
|
/* eslint-disable */
|
||||||
export declare namespace passwords {
|
export declare namespace autofill {
|
||||||
/** The error message returned when a password is not found during retrieval or deletion. */
|
export class AutofillIpcServer {
|
||||||
export const PASSWORD_NOT_FOUND: string
|
|
||||||
/**
|
|
||||||
* Fetch the stored password from the keychain.
|
|
||||||
* Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist.
|
|
||||||
*/
|
|
||||||
export function getPassword(service: string, account: string): Promise<string>
|
|
||||||
/**
|
|
||||||
* Save the password to the keychain. Adds an entry if none exists otherwise updates the
|
|
||||||
* existing entry.
|
|
||||||
*/
|
|
||||||
export function setPassword(service: string, account: string, password: string): Promise<void>
|
|
||||||
/**
|
|
||||||
* Delete the stored password from the keychain.
|
|
||||||
* Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist.
|
|
||||||
*/
|
|
||||||
export function deletePassword(service: string, account: string): Promise<void>
|
|
||||||
/** Checks if the os secure storage is available */
|
|
||||||
export function isAvailable(): Promise<boolean>
|
|
||||||
}
|
|
||||||
export declare namespace biometrics {
|
|
||||||
export function prompt(hwnd: Buffer, message: string): Promise<boolean>
|
|
||||||
export function available(): Promise<boolean>
|
|
||||||
export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise<string>
|
|
||||||
/**
|
|
||||||
* Retrieves the biometric secret for the given service and account.
|
|
||||||
* Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist.
|
|
||||||
*/
|
|
||||||
export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise<string>
|
|
||||||
/**
|
|
||||||
* Derives key material from biometric data. Returns a string encoded with a
|
|
||||||
* base64 encoded key and the base64 encoded challenge used to create it
|
|
||||||
* separated by a `|` character.
|
|
||||||
*
|
|
||||||
* If the iv is provided, it will be used as the challenge. Otherwise a random challenge will
|
|
||||||
* be generated.
|
|
||||||
*
|
|
||||||
* `format!("<key_base64>|<iv_base64>")`
|
|
||||||
*/
|
|
||||||
export function deriveKeyMaterial(iv?: string | undefined | null): Promise<OsDerivedKey>
|
|
||||||
export interface KeyMaterial {
|
|
||||||
osKeyPartB64: string
|
|
||||||
clientKeyPartB64?: string
|
|
||||||
}
|
|
||||||
export interface OsDerivedKey {
|
|
||||||
keyB64: string
|
|
||||||
ivB64: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export declare namespace biometrics_v2 {
|
|
||||||
export function initBiometricSystem(): BiometricLockSystem
|
|
||||||
export function authenticate(biometricLockSystem: BiometricLockSystem, hwnd: Buffer, message: string): Promise<boolean>
|
|
||||||
export function authenticateAvailable(biometricLockSystem: BiometricLockSystem): Promise<boolean>
|
|
||||||
export function enrollPersistent(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise<void>
|
|
||||||
export function provideKey(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise<void>
|
|
||||||
export function unlock(biometricLockSystem: BiometricLockSystem, userId: string, hwnd: Buffer): Promise<Buffer>
|
|
||||||
export function unlockAvailable(biometricLockSystem: BiometricLockSystem, userId: string): Promise<boolean>
|
|
||||||
export function hasPersistent(biometricLockSystem: BiometricLockSystem, userId: string): Promise<boolean>
|
|
||||||
export function unenroll(biometricLockSystem: BiometricLockSystem, userId: string): Promise<void>
|
|
||||||
export class BiometricLockSystem { }
|
|
||||||
}
|
|
||||||
export declare namespace clipboards {
|
|
||||||
export function read(): Promise<string>
|
|
||||||
export function write(text: string, password: boolean): Promise<void>
|
|
||||||
}
|
|
||||||
export declare namespace sshagent {
|
|
||||||
export interface PrivateKey {
|
|
||||||
privateKey: string
|
|
||||||
name: string
|
|
||||||
cipherId: string
|
|
||||||
}
|
|
||||||
export interface SshKey {
|
|
||||||
privateKey: string
|
|
||||||
publicKey: string
|
|
||||||
keyFingerprint: string
|
|
||||||
}
|
|
||||||
export interface SshUiRequest {
|
|
||||||
cipherId?: string
|
|
||||||
isList: boolean
|
|
||||||
processName: string
|
|
||||||
isForwarding: boolean
|
|
||||||
namespace?: string
|
|
||||||
}
|
|
||||||
export function serve(callback: (err: Error | null, arg: SshUiRequest) => any): Promise<SshAgentState>
|
|
||||||
export function stop(agentState: SshAgentState): void
|
|
||||||
export function isRunning(agentState: SshAgentState): boolean
|
|
||||||
export function setKeys(agentState: SshAgentState, newKeys: Array<PrivateKey>): void
|
|
||||||
export function lock(agentState: SshAgentState): void
|
|
||||||
export function clearKeys(agentState: SshAgentState): void
|
|
||||||
export class SshAgentState { }
|
|
||||||
}
|
|
||||||
export declare namespace processisolations {
|
|
||||||
export function disableCoredumps(): Promise<void>
|
|
||||||
export function isCoreDumpingDisabled(): Promise<boolean>
|
|
||||||
export function isolateProcess(): Promise<void>
|
|
||||||
}
|
|
||||||
export declare namespace powermonitors {
|
|
||||||
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
|
||||||
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 interface IpcMessage {
|
|
||||||
clientId: number
|
|
||||||
kind: IpcMessageType
|
|
||||||
message?: string
|
|
||||||
}
|
|
||||||
export const enum IpcMessageType {
|
|
||||||
Connected = 0,
|
|
||||||
Disconnected = 1,
|
|
||||||
Message = 2
|
|
||||||
}
|
|
||||||
export class IpcServer {
|
|
||||||
/**
|
/**
|
||||||
* Create and start the IPC server without blocking.
|
* Create and start the IPC server without blocking.
|
||||||
*
|
*
|
||||||
@@ -127,34 +9,43 @@ export declare namespace ipc {
|
|||||||
* connection and must be the same for both the server and client. @param callback
|
* connection and must be the same for both the server and client. @param callback
|
||||||
* This function will be called whenever a message is received from a client.
|
* This function will be called whenever a message is received from a client.
|
||||||
*/
|
*/
|
||||||
static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise<IpcServer>
|
static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void, nativeStatusCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void): Promise<AutofillIpcServer>
|
||||||
/** Return the path to the IPC server. */
|
/** Return the path to the IPC server. */
|
||||||
getPath(): string
|
getPath(): string
|
||||||
/** Stop the IPC server. */
|
/** Stop the IPC server. */
|
||||||
stop(): void
|
stop(): void
|
||||||
/**
|
completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number
|
||||||
* Send a message over the IPC server to all the connected clients
|
completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number
|
||||||
*
|
completeError(clientId: number, sequenceNumber: number, error: string): number
|
||||||
* @return The number of clients that the message was sent to. Note that the number of
|
|
||||||
* messages actually received may be less, as some clients could disconnect before
|
|
||||||
* receiving the message.
|
|
||||||
*/
|
|
||||||
send(message: string): number
|
|
||||||
}
|
}
|
||||||
}
|
export interface NativeStatus {
|
||||||
export declare namespace autostart {
|
key: string
|
||||||
export function setAutostart(autostart: boolean, params: Array<string>): Promise<void>
|
value: string
|
||||||
}
|
|
||||||
export declare namespace autofill {
|
|
||||||
export function runCommand(value: string): Promise<string>
|
|
||||||
export const enum UserVerification {
|
|
||||||
Preferred = 'preferred',
|
|
||||||
Required = 'required',
|
|
||||||
Discouraged = 'discouraged'
|
|
||||||
}
|
}
|
||||||
export interface Position {
|
export interface PasskeyAssertionRequest {
|
||||||
x: number
|
rpId: string
|
||||||
y: number
|
clientDataHash: Array<number>
|
||||||
|
userVerification: UserVerification
|
||||||
|
allowedCredentials: Array<Array<number>>
|
||||||
|
windowXy: Position
|
||||||
|
}
|
||||||
|
export interface PasskeyAssertionResponse {
|
||||||
|
rpId: string
|
||||||
|
userHandle: Array<number>
|
||||||
|
signature: Array<number>
|
||||||
|
clientDataHash: Array<number>
|
||||||
|
authenticatorData: Array<number>
|
||||||
|
credentialId: Array<number>
|
||||||
|
}
|
||||||
|
export interface PasskeyAssertionWithoutUserInterfaceRequest {
|
||||||
|
rpId: string
|
||||||
|
credentialId: Array<number>
|
||||||
|
userName: string
|
||||||
|
userHandle: Array<number>
|
||||||
|
recordIdentifier?: string
|
||||||
|
clientDataHash: Array<number>
|
||||||
|
userVerification: UserVerification
|
||||||
|
windowXy: Position
|
||||||
}
|
}
|
||||||
export interface PasskeyRegistrationRequest {
|
export interface PasskeyRegistrationRequest {
|
||||||
rpId: string
|
rpId: string
|
||||||
@@ -172,71 +63,77 @@ export declare namespace autofill {
|
|||||||
credentialId: Array<number>
|
credentialId: Array<number>
|
||||||
attestationObject: Array<number>
|
attestationObject: Array<number>
|
||||||
}
|
}
|
||||||
export interface PasskeyAssertionRequest {
|
export interface Position {
|
||||||
rpId: string
|
x: number
|
||||||
clientDataHash: Array<number>
|
y: number
|
||||||
userVerification: UserVerification
|
|
||||||
allowedCredentials: Array<Array<number>>
|
|
||||||
windowXy: Position
|
|
||||||
}
|
}
|
||||||
export interface PasskeyAssertionWithoutUserInterfaceRequest {
|
export function runCommand(value: string): Promise<string>
|
||||||
rpId: string
|
export const enum UserVerification {
|
||||||
credentialId: Array<number>
|
Preferred = 'preferred',
|
||||||
userName: string
|
Required = 'required',
|
||||||
userHandle: Array<number>
|
Discouraged = 'discouraged'
|
||||||
recordIdentifier?: string
|
|
||||||
clientDataHash: Array<number>
|
|
||||||
userVerification: UserVerification
|
|
||||||
windowXy: Position
|
|
||||||
}
|
}
|
||||||
export interface NativeStatus {
|
}
|
||||||
key: string
|
|
||||||
value: string
|
export declare namespace autostart {
|
||||||
}
|
export function setAutostart(autostart: boolean, params: Array<string>): Promise<void>
|
||||||
export interface PasskeyAssertionResponse {
|
}
|
||||||
rpId: string
|
|
||||||
userHandle: Array<number>
|
export declare namespace autotype {
|
||||||
signature: Array<number>
|
export function getForegroundWindowTitle(): string
|
||||||
clientDataHash: Array<number>
|
export function typeInput(input: Array<number>, keyboardShortcut: Array<string>): void
|
||||||
authenticatorData: Array<number>
|
}
|
||||||
credentialId: Array<number>
|
|
||||||
}
|
export declare namespace biometrics {
|
||||||
export class IpcServer {
|
export function available(): Promise<boolean>
|
||||||
/**
|
/**
|
||||||
* Create and start the IPC server without blocking.
|
* Derives key material from biometric data. Returns a string encoded with a
|
||||||
|
* base64 encoded key and the base64 encoded challenge used to create it
|
||||||
|
* separated by a `|` character.
|
||||||
*
|
*
|
||||||
* @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
* If the iv is provided, it will be used as the challenge. Otherwise a random challenge will
|
||||||
* connection and must be the same for both the server and client. @param callback
|
* be generated.
|
||||||
* This function will be called whenever a message is received from a client.
|
*
|
||||||
|
* `format!("<key_base64>|<iv_base64>")`
|
||||||
*/
|
*/
|
||||||
static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void, nativeStatusCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void): Promise<IpcServer>
|
export function deriveKeyMaterial(iv?: string | undefined | null): Promise<OsDerivedKey>
|
||||||
/** Return the path to the IPC server. */
|
/**
|
||||||
getPath(): string
|
* Retrieves the biometric secret for the given service and account.
|
||||||
/** Stop the IPC server. */
|
* Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist.
|
||||||
stop(): void
|
*/
|
||||||
completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number
|
export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise<string>
|
||||||
completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number
|
export interface KeyMaterial {
|
||||||
completeError(clientId: number, sequenceNumber: number, error: string): number
|
osKeyPartB64: string
|
||||||
|
clientKeyPartB64?: string
|
||||||
}
|
}
|
||||||
}
|
export interface OsDerivedKey {
|
||||||
export declare namespace passkey_authenticator {
|
keyB64: string
|
||||||
export function register(): void
|
ivB64: string
|
||||||
}
|
|
||||||
export declare namespace logging {
|
|
||||||
export const enum LogLevel {
|
|
||||||
Trace = 0,
|
|
||||||
Debug = 1,
|
|
||||||
Info = 2,
|
|
||||||
Warn = 3,
|
|
||||||
Error = 4
|
|
||||||
}
|
}
|
||||||
export function initNapiLog(jsLogFn: (err: Error | null, arg0: LogLevel, arg1: string) => any): void
|
export function prompt(hwnd: Buffer, message: string): Promise<boolean>
|
||||||
|
export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export declare namespace biometrics_v2 {
|
||||||
|
export class BiometricLockSystem {
|
||||||
|
|
||||||
|
}
|
||||||
|
export function authenticate(biometricLockSystem: BiometricLockSystem, hwnd: Buffer, message: string): Promise<boolean>
|
||||||
|
export function authenticateAvailable(biometricLockSystem: BiometricLockSystem): Promise<boolean>
|
||||||
|
export function enrollPersistent(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise<void>
|
||||||
|
export function hasPersistent(biometricLockSystem: BiometricLockSystem, userId: string): Promise<boolean>
|
||||||
|
export function initBiometricSystem(): BiometricLockSystem
|
||||||
|
export function provideKey(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise<void>
|
||||||
|
export function unenroll(biometricLockSystem: BiometricLockSystem, userId: string): Promise<void>
|
||||||
|
export function unlock(biometricLockSystem: BiometricLockSystem, userId: string, hwnd: Buffer): Promise<Buffer>
|
||||||
|
export function unlockAvailable(biometricLockSystem: BiometricLockSystem, userId: string): Promise<boolean>
|
||||||
|
}
|
||||||
|
|
||||||
export declare namespace chromium_importer {
|
export declare namespace chromium_importer {
|
||||||
export interface ProfileInfo {
|
export function getAvailableProfiles(browser: string): Array<ProfileInfo>
|
||||||
id: string
|
/** Returns OS aware metadata describing supported Chromium based importers as a JSON string. */
|
||||||
name: string
|
export function getMetadata(): Record<string, NativeImporterMetadata>
|
||||||
}
|
export function importLogins(browser: string, profileId: string): Promise<Array<LoginImportResult>>
|
||||||
export interface Login {
|
export interface Login {
|
||||||
url: string
|
url: string
|
||||||
username: string
|
username: string
|
||||||
@@ -257,13 +154,130 @@ export declare namespace chromium_importer {
|
|||||||
loaders: Array<string>
|
loaders: Array<string>
|
||||||
instructions: string
|
instructions: string
|
||||||
}
|
}
|
||||||
/** Returns OS aware metadata describing supported Chromium based importers as a JSON string. */
|
export interface ProfileInfo {
|
||||||
export function getMetadata(masBuild: boolean): Record<string, NativeImporterMetadata>
|
id: string
|
||||||
export function getAvailableProfiles(browser: string, masBuild: boolean): Promise<Array<ProfileInfo>>
|
name: string
|
||||||
export function importLogins(browser: string, profileId: string, masBuild: boolean): Promise<Array<LoginImportResult>>
|
}
|
||||||
export function requestBrowserAccess(browser: string, masBuild: boolean): Promise<void>
|
|
||||||
}
|
}
|
||||||
export declare namespace autotype {
|
|
||||||
export function getForegroundWindowTitle(): string
|
export declare namespace clipboards {
|
||||||
export function typeInput(input: Array<number>, keyboardShortcut: Array<string>): void
|
export function read(): Promise<string>
|
||||||
|
export function write(text: string, password: boolean): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace ipc {
|
||||||
|
export class NativeIpcServer {
|
||||||
|
/**
|
||||||
|
* Create and start the IPC server without blocking.
|
||||||
|
*
|
||||||
|
* @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
||||||
|
* connection and must be the same for both the server and client. @param callback
|
||||||
|
* This function will be called whenever a message is received from a client.
|
||||||
|
*/
|
||||||
|
static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise<NativeIpcServer>
|
||||||
|
/** Return the path to the IPC server. */
|
||||||
|
getPath(): string
|
||||||
|
/** Stop the IPC server. */
|
||||||
|
stop(): void
|
||||||
|
/**
|
||||||
|
* Send a message over the IPC server to all the connected clients
|
||||||
|
*
|
||||||
|
* @return The number of clients that the message was sent to. Note that the number of
|
||||||
|
* messages actually received may be less, as some clients could disconnect before
|
||||||
|
* receiving the message.
|
||||||
|
*/
|
||||||
|
send(message: string): number
|
||||||
|
}
|
||||||
|
export interface IpcMessage {
|
||||||
|
clientId: number
|
||||||
|
kind: IpcMessageType
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
export const enum IpcMessageType {
|
||||||
|
Connected = 0,
|
||||||
|
Disconnected = 1,
|
||||||
|
Message = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace logging {
|
||||||
|
export function initNapiLog(jsLogFn: ((err: Error | null, arg0: LogLevel, arg1: string) => any)): void
|
||||||
|
export const enum LogLevel {
|
||||||
|
Trace = 0,
|
||||||
|
Debug = 1,
|
||||||
|
Info = 2,
|
||||||
|
Warn = 3,
|
||||||
|
Error = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace passkey_authenticator {
|
||||||
|
export function register(): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace passwords {
|
||||||
|
/**
|
||||||
|
* Delete the stored password from the keychain.
|
||||||
|
* Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist.
|
||||||
|
*/
|
||||||
|
export function deletePassword(service: string, account: string): Promise<void>
|
||||||
|
/**
|
||||||
|
* Fetch the stored password from the keychain.
|
||||||
|
* Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist.
|
||||||
|
*/
|
||||||
|
export function getPassword(service: string, account: string): Promise<string>
|
||||||
|
/** Checks if the os secure storage is available */
|
||||||
|
export function isAvailable(): Promise<boolean>
|
||||||
|
/** The error message returned when a password is not found during retrieval or deletion. */
|
||||||
|
export const PASSWORD_NOT_FOUND: string
|
||||||
|
/**
|
||||||
|
* Save the password to the keychain. Adds an entry if none exists otherwise updates the
|
||||||
|
* existing entry.
|
||||||
|
*/
|
||||||
|
export function setPassword(service: string, account: string, password: string): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace powermonitors {
|
||||||
|
export function isLockMonitorAvailable(): Promise<boolean>
|
||||||
|
export function onLock(callback: ((err: Error | null, ) => any)): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace processisolations {
|
||||||
|
export function disableCoredumps(): Promise<void>
|
||||||
|
export function isCoreDumpingDisabled(): Promise<boolean>
|
||||||
|
export function isolateProcess(): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare namespace sshagent {
|
||||||
|
export class SshAgentState {
|
||||||
|
|
||||||
|
}
|
||||||
|
export function clearKeys(agentState: SshAgentState): void
|
||||||
|
export function isRunning(agentState: SshAgentState): boolean
|
||||||
|
export function lock(agentState: SshAgentState): void
|
||||||
|
export interface PrivateKey {
|
||||||
|
privateKey: string
|
||||||
|
name: string
|
||||||
|
cipherId: string
|
||||||
|
}
|
||||||
|
export function serve(callback: ((err: Error | null, arg: SshUiRequest) => Promise<boolean>)): Promise<SshAgentState>
|
||||||
|
export function setKeys(agentState: SshAgentState, newKeys: Array<PrivateKey>): void
|
||||||
|
export interface SshKey {
|
||||||
|
privateKey: string
|
||||||
|
publicKey: string
|
||||||
|
keyFingerprint: string
|
||||||
|
}
|
||||||
|
export interface SshUiRequest {
|
||||||
|
cipherId?: string
|
||||||
|
isList: boolean
|
||||||
|
processName: string
|
||||||
|
isForwarding: boolean
|
||||||
|
namespace?: string
|
||||||
|
}
|
||||||
|
export function stop(agentState: SshAgentState): void
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,20 +82,20 @@ switch (platform) {
|
|||||||
switch (arch) {
|
switch (arch) {
|
||||||
case "x64":
|
case "x64":
|
||||||
nativeBinding = loadFirstAvailable(
|
nativeBinding = loadFirstAvailable(
|
||||||
["desktop_napi.linux-x64-musl.node", "desktop_napi.linux-x64-gnu.node"],
|
["desktop_napi.linux-x64-gnu.node"],
|
||||||
"@bitwarden/desktop-napi-linux-x64-musl",
|
"@bitwarden/desktop-napi-linux-x64-gnu",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "arm64":
|
case "arm64":
|
||||||
nativeBinding = loadFirstAvailable(
|
nativeBinding = loadFirstAvailable(
|
||||||
["desktop_napi.linux-arm64-musl.node", "desktop_napi.linux-arm64-gnu.node"],
|
["desktop_napi.linux-arm64-gnu.node"],
|
||||||
"@bitwarden/desktop-napi-linux-arm64-musl",
|
"@bitwarden/desktop-napi-linux-arm64-gnu",
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "arm":
|
case "arm":
|
||||||
nativeBinding = loadFirstAvailable(
|
nativeBinding = loadFirstAvailable(
|
||||||
["desktop_napi.linux-arm-musl.node", "desktop_napi.linux-arm-gnu.node"],
|
["desktop_napi.linux-arm-gnu.node"],
|
||||||
"@bitwarden/desktop-napi-linux-arm-musl",
|
"@bitwarden/desktop-napi-linux-arm-gnu",
|
||||||
);
|
);
|
||||||
localFileExisted = existsSync(join(__dirname, "desktop_napi.linux-arm-gnueabihf.node"));
|
localFileExisted = existsSync(join(__dirname, "desktop_napi.linux-arm-gnueabihf.node"));
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3,27 +3,23 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "napi build --platform --js false",
|
"build": "node scripts/build.js",
|
||||||
"test": "cargo test"
|
"test": "cargo test"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@napi-rs/cli": "2.18.4"
|
"@napi-rs/cli": "3.2.0"
|
||||||
},
|
},
|
||||||
"napi": {
|
"napi": {
|
||||||
"name": "desktop_napi",
|
"binaryName": "desktop_napi",
|
||||||
"triples": {
|
"targets": [
|
||||||
"defaults": true,
|
|
||||||
"additional": [
|
|
||||||
"x86_64-unknown-linux-musl",
|
|
||||||
"aarch64-unknown-linux-gnu",
|
|
||||||
"i686-pc-windows-msvc",
|
|
||||||
"armv7-unknown-linux-gnueabihf",
|
|
||||||
"aarch64-apple-darwin",
|
"aarch64-apple-darwin",
|
||||||
"aarch64-unknown-linux-musl",
|
"aarch64-pc-windows-msvc",
|
||||||
"aarch64-pc-windows-msvc"
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"armv7-unknown-linux-gnueabihf",
|
||||||
|
"i686-pc-windows-msvc",
|
||||||
|
"x86_64-unknown-linux-gnu"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
22
apps/desktop/desktop_native/napi/scripts/build.js
Normal file
22
apps/desktop/desktop_native/napi/scripts/build.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
const isRelease = args.includes('--release');
|
||||||
|
|
||||||
|
const argsString = args.join(' ');
|
||||||
|
|
||||||
|
if (isRelease) {
|
||||||
|
console.log('Building release mode.');
|
||||||
|
|
||||||
|
execSync(`napi build --platform --no-js ${argsString}`, { stdio: 'inherit'});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('Building debug mode.');
|
||||||
|
|
||||||
|
execSync(`napi build --platform --no-js ${argsString}`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: { ...process.env, RUST_LOG: 'debug' }
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -290,7 +290,7 @@ pub mod sshagent {
|
|||||||
|
|
||||||
use napi::{
|
use napi::{
|
||||||
bindgen_prelude::Promise,
|
bindgen_prelude::Promise,
|
||||||
threadsafe_function::{ErrorStrategy::CalleeHandled, ThreadsafeFunction},
|
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||||
};
|
};
|
||||||
use tokio::{self, sync::Mutex};
|
use tokio::{self, sync::Mutex};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@@ -326,13 +326,15 @@ pub mod sshagent {
|
|||||||
#[allow(clippy::unused_async)] // FIXME: Remove unused async!
|
#[allow(clippy::unused_async)] // FIXME: Remove unused async!
|
||||||
#[napi]
|
#[napi]
|
||||||
pub async fn serve(
|
pub async fn serve(
|
||||||
callback: ThreadsafeFunction<SshUIRequest, CalleeHandled>,
|
callback: ThreadsafeFunction<SshUIRequest, Promise<bool>>,
|
||||||
) -> napi::Result<SshAgentState> {
|
) -> napi::Result<SshAgentState> {
|
||||||
let (auth_request_tx, mut auth_request_rx) =
|
let (auth_request_tx, mut auth_request_rx) =
|
||||||
tokio::sync::mpsc::channel::<desktop_core::ssh_agent::SshAgentUIRequest>(32);
|
tokio::sync::mpsc::channel::<desktop_core::ssh_agent::SshAgentUIRequest>(32);
|
||||||
let (auth_response_tx, auth_response_rx) =
|
let (auth_response_tx, auth_response_rx) =
|
||||||
tokio::sync::broadcast::channel::<(u32, bool)>(32);
|
tokio::sync::broadcast::channel::<(u32, bool)>(32);
|
||||||
let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx));
|
let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx));
|
||||||
|
// Wrap callback in Arc so it can be shared across spawned tasks
|
||||||
|
let callback = Arc::new(callback);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let _ = auth_response_rx;
|
let _ = auth_response_rx;
|
||||||
|
|
||||||
@@ -342,42 +344,50 @@ pub mod sshagent {
|
|||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let auth_response_tx_arc = cloned_response_tx_arc;
|
let auth_response_tx_arc = cloned_response_tx_arc;
|
||||||
let callback = cloned_callback;
|
let callback = cloned_callback;
|
||||||
let promise_result: Result<Promise<bool>, napi::Error> = callback
|
// In NAPI v3, obtain the JS callback return as a Promise<boolean> and await it
|
||||||
.call_async(Ok(SshUIRequest {
|
// in Rust
|
||||||
|
let (tx, rx) = std::sync::mpsc::channel::<Promise<bool>>();
|
||||||
|
let status = callback.call_with_return_value(
|
||||||
|
Ok(SshUIRequest {
|
||||||
cipher_id: request.cipher_id,
|
cipher_id: request.cipher_id,
|
||||||
is_list: request.is_list,
|
is_list: request.is_list,
|
||||||
process_name: request.process_name,
|
process_name: request.process_name,
|
||||||
is_forwarding: request.is_forwarding,
|
is_forwarding: request.is_forwarding,
|
||||||
namespace: request.namespace,
|
namespace: request.namespace,
|
||||||
}))
|
}),
|
||||||
.await;
|
ThreadsafeFunctionCallMode::Blocking,
|
||||||
match promise_result {
|
move |ret: Result<Promise<bool>, napi::Error>, _env| {
|
||||||
Ok(promise_result) => match promise_result.await {
|
if let Ok(p) = ret {
|
||||||
Ok(result) => {
|
let _ = tx.send(p);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = if status == napi::Status::Ok {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(promise) => match promise.await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!(error = %e, "UI callback promise rejected");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!(error = %e, "Failed to receive UI callback promise");
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(error = ?status, "Calling UI callback failed");
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let _ = auth_response_tx_arc
|
let _ = auth_response_tx_arc
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send((request.request_id, result))
|
.send((request.request_id, result))
|
||||||
.expect("should be able to send auth response to agent");
|
.expect("should be able to send auth response to agent");
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!(error = %e, "Calling UI callback promise was rejected");
|
|
||||||
let _ = auth_response_tx_arc
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.send((request.request_id, false))
|
|
||||||
.expect("should be able to send auth response to agent");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!(error = %e, "Calling UI callback could not create promise");
|
|
||||||
let _ = auth_response_tx_arc
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.send((request.request_id, false))
|
|
||||||
.expect("should be able to send auth response to agent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -465,14 +475,12 @@ pub mod processisolations {
|
|||||||
#[napi]
|
#[napi]
|
||||||
pub mod powermonitors {
|
pub mod powermonitors {
|
||||||
use napi::{
|
use napi::{
|
||||||
threadsafe_function::{
|
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||||
ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
||||||
},
|
|
||||||
tokio,
|
tokio,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub async fn on_lock(callback: ThreadsafeFunction<(), CalleeHandled>) -> napi::Result<()> {
|
pub async fn on_lock(callback: ThreadsafeFunction<()>) -> napi::Result<()> {
|
||||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(32);
|
let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(32);
|
||||||
desktop_core::powermonitor::on_lock(tx)
|
desktop_core::powermonitor::on_lock(tx)
|
||||||
.await
|
.await
|
||||||
@@ -511,9 +519,7 @@ pub mod windows_registry {
|
|||||||
#[napi]
|
#[napi]
|
||||||
pub mod ipc {
|
pub mod ipc {
|
||||||
use desktop_core::ipc::server::{Message, MessageType};
|
use desktop_core::ipc::server::{Message, MessageType};
|
||||||
use napi::threadsafe_function::{
|
use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode};
|
||||||
ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[napi(object)]
|
#[napi(object)]
|
||||||
pub struct IpcMessage {
|
pub struct IpcMessage {
|
||||||
@@ -550,12 +556,12 @@ pub mod ipc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub struct IpcServer {
|
pub struct NativeIpcServer {
|
||||||
server: desktop_core::ipc::server::Server,
|
server: desktop_core::ipc::server::Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
impl IpcServer {
|
impl NativeIpcServer {
|
||||||
/// Create and start the IPC server without blocking.
|
/// Create and start the IPC server without blocking.
|
||||||
///
|
///
|
||||||
/// @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
/// @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
||||||
@@ -566,7 +572,7 @@ pub mod ipc {
|
|||||||
pub async fn listen(
|
pub async fn listen(
|
||||||
name: String,
|
name: String,
|
||||||
#[napi(ts_arg_type = "(error: null | Error, message: IpcMessage) => void")]
|
#[napi(ts_arg_type = "(error: null | Error, message: IpcMessage) => void")]
|
||||||
callback: ThreadsafeFunction<IpcMessage, ErrorStrategy::CalleeHandled>,
|
callback: ThreadsafeFunction<IpcMessage>,
|
||||||
) -> napi::Result<Self> {
|
) -> napi::Result<Self> {
|
||||||
let (send, mut recv) = tokio::sync::mpsc::channel::<Message>(32);
|
let (send, mut recv) = tokio::sync::mpsc::channel::<Message>(32);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -583,7 +589,7 @@ pub mod ipc {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(IpcServer { server })
|
Ok(NativeIpcServer { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the path to the IPC server.
|
/// Return the path to the IPC server.
|
||||||
@@ -630,8 +636,9 @@ pub mod autostart {
|
|||||||
#[napi]
|
#[napi]
|
||||||
pub mod autofill {
|
pub mod autofill {
|
||||||
use desktop_core::ipc::server::{Message, MessageType};
|
use desktop_core::ipc::server::{Message, MessageType};
|
||||||
use napi::threadsafe_function::{
|
use napi::{
|
||||||
ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
bindgen_prelude::FnArgs,
|
||||||
|
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
@@ -746,14 +753,14 @@ pub mod autofill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub struct IpcServer {
|
pub struct AutofillIpcServer {
|
||||||
server: desktop_core::ipc::server::Server,
|
server: desktop_core::ipc::server::Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Remove unwraps! They panic and terminate the whole application.
|
// FIXME: Remove unwraps! They panic and terminate the whole application.
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
#[napi]
|
#[napi]
|
||||||
impl IpcServer {
|
impl AutofillIpcServer {
|
||||||
/// Create and start the IPC server without blocking.
|
/// Create and start the IPC server without blocking.
|
||||||
///
|
///
|
||||||
/// @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
/// @param name The endpoint name to listen on. This name uniquely identifies the IPC
|
||||||
@@ -769,30 +776,24 @@ pub mod autofill {
|
|||||||
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void"
|
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void"
|
||||||
)]
|
)]
|
||||||
registration_callback: ThreadsafeFunction<
|
registration_callback: ThreadsafeFunction<
|
||||||
(u32, u32, PasskeyRegistrationRequest),
|
FnArgs<(u32, u32, PasskeyRegistrationRequest)>,
|
||||||
ErrorStrategy::CalleeHandled,
|
|
||||||
>,
|
>,
|
||||||
#[napi(
|
#[napi(
|
||||||
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void"
|
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void"
|
||||||
)]
|
)]
|
||||||
assertion_callback: ThreadsafeFunction<
|
assertion_callback: ThreadsafeFunction<
|
||||||
(u32, u32, PasskeyAssertionRequest),
|
FnArgs<(u32, u32, PasskeyAssertionRequest)>,
|
||||||
ErrorStrategy::CalleeHandled,
|
|
||||||
>,
|
>,
|
||||||
#[napi(
|
#[napi(
|
||||||
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void"
|
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void"
|
||||||
)]
|
)]
|
||||||
assertion_without_user_interface_callback: ThreadsafeFunction<
|
assertion_without_user_interface_callback: ThreadsafeFunction<
|
||||||
(u32, u32, PasskeyAssertionWithoutUserInterfaceRequest),
|
FnArgs<(u32, u32, PasskeyAssertionWithoutUserInterfaceRequest)>,
|
||||||
ErrorStrategy::CalleeHandled,
|
|
||||||
>,
|
>,
|
||||||
#[napi(
|
#[napi(
|
||||||
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void"
|
ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void"
|
||||||
)]
|
)]
|
||||||
native_status_callback: ThreadsafeFunction<
|
native_status_callback: ThreadsafeFunction<(u32, u32, NativeStatus)>,
|
||||||
(u32, u32, NativeStatus),
|
|
||||||
ErrorStrategy::CalleeHandled,
|
|
||||||
>,
|
|
||||||
) -> napi::Result<Self> {
|
) -> napi::Result<Self> {
|
||||||
let (send, mut recv) = tokio::sync::mpsc::channel::<Message>(32);
|
let (send, mut recv) = tokio::sync::mpsc::channel::<Message>(32);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -817,7 +818,7 @@ pub mod autofill {
|
|||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
let value = msg
|
let value = msg
|
||||||
.value
|
.value
|
||||||
.map(|value| (client_id, msg.sequence_number, value))
|
.map(|value| (client_id, msg.sequence_number, value).into())
|
||||||
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
||||||
|
|
||||||
assertion_callback
|
assertion_callback
|
||||||
@@ -836,7 +837,7 @@ pub mod autofill {
|
|||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
let value = msg
|
let value = msg
|
||||||
.value
|
.value
|
||||||
.map(|value| (client_id, msg.sequence_number, value))
|
.map(|value| (client_id, msg.sequence_number, value).into())
|
||||||
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
||||||
|
|
||||||
assertion_without_user_interface_callback
|
assertion_without_user_interface_callback
|
||||||
@@ -854,7 +855,7 @@ pub mod autofill {
|
|||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
let value = msg
|
let value = msg
|
||||||
.value
|
.value
|
||||||
.map(|value| (client_id, msg.sequence_number, value))
|
.map(|value| (client_id, msg.sequence_number, value).into())
|
||||||
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
.map_err(|e| napi::Error::from_reason(format!("{e:?}")));
|
||||||
registration_callback
|
registration_callback
|
||||||
.call(value, ThreadsafeFunctionCallMode::NonBlocking);
|
.call(value, ThreadsafeFunctionCallMode::NonBlocking);
|
||||||
@@ -894,7 +895,7 @@ pub mod autofill {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(IpcServer { server })
|
Ok(AutofillIpcServer { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the path to the IPC server.
|
/// Return the path to the IPC server.
|
||||||
@@ -987,19 +988,20 @@ pub mod logging {
|
|||||||
|
|
||||||
use std::{fmt::Write, sync::OnceLock};
|
use std::{fmt::Write, sync::OnceLock};
|
||||||
|
|
||||||
use napi::threadsafe_function::{
|
use napi::{
|
||||||
ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode,
|
bindgen_prelude::FnArgs,
|
||||||
|
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||||
};
|
};
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
filter::{EnvFilter, LevelFilter},
|
filter::EnvFilter,
|
||||||
fmt::format::{DefaultVisitor, Writer},
|
fmt::format::{DefaultVisitor, Writer},
|
||||||
layer::SubscriberExt,
|
layer::SubscriberExt,
|
||||||
util::SubscriberInitExt,
|
util::SubscriberInitExt,
|
||||||
Layer,
|
Layer,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct JsLogger(OnceLock<ThreadsafeFunction<(LogLevel, String), CalleeHandled>>);
|
struct JsLogger(OnceLock<ThreadsafeFunction<FnArgs<(LogLevel, String)>>>);
|
||||||
static JS_LOGGER: JsLogger = JsLogger(OnceLock::new());
|
static JS_LOGGER: JsLogger = JsLogger(OnceLock::new());
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
@@ -1071,18 +1073,26 @@ pub mod logging {
|
|||||||
let msg = (event.metadata().level().into(), buffer);
|
let msg = (event.metadata().level().into(), buffer);
|
||||||
|
|
||||||
if let Some(logger) = JS_LOGGER.0.get() {
|
if let Some(logger) = JS_LOGGER.0.get() {
|
||||||
let _ = logger.call(Ok(msg), ThreadsafeFunctionCallMode::NonBlocking);
|
let _ = logger.call(Ok(msg.into()), ThreadsafeFunctionCallMode::NonBlocking);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn init_napi_log(js_log_fn: ThreadsafeFunction<(LogLevel, String), CalleeHandled>) {
|
pub fn init_napi_log(js_log_fn: ThreadsafeFunction<FnArgs<(LogLevel, String)>>) {
|
||||||
let _ = JS_LOGGER.0.set(js_log_fn);
|
let _ = JS_LOGGER.0.set(js_log_fn);
|
||||||
|
|
||||||
|
// the log level hierarchy is determined by:
|
||||||
|
// - if RUST_LOG is detected at runtime
|
||||||
|
// - if RUST_LOG is provided at compile time
|
||||||
|
// - default to INFO
|
||||||
let filter = EnvFilter::builder()
|
let filter = EnvFilter::builder()
|
||||||
// set the default log level to INFO.
|
.with_default_directive(
|
||||||
.with_default_directive(LevelFilter::INFO.into())
|
option_env!("RUST_LOG")
|
||||||
|
.unwrap_or("info")
|
||||||
|
.parse()
|
||||||
|
.expect("should provide valid log level at compile time."),
|
||||||
|
)
|
||||||
// parse directives from the RUST_LOG environment variable,
|
// parse directives from the RUST_LOG environment variable,
|
||||||
// overriding the default directive for matching targets.
|
// overriding the default directive for matching targets.
|
||||||
.from_env_lossy();
|
.from_env_lossy();
|
||||||
@@ -1140,8 +1150,8 @@ pub mod chromium_importer {
|
|||||||
#[napi(object)]
|
#[napi(object)]
|
||||||
pub struct NativeImporterMetadata {
|
pub struct NativeImporterMetadata {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub loaders: Vec<&'static str>,
|
pub loaders: Vec<String>,
|
||||||
pub instructions: &'static str,
|
pub instructions: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<_LoginImportResult> for LoginImportResult {
|
impl From<_LoginImportResult> for LoginImportResult {
|
||||||
@@ -1237,7 +1247,7 @@ pub mod chromium_importer {
|
|||||||
#[napi]
|
#[napi]
|
||||||
pub mod autotype {
|
pub mod autotype {
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn get_foreground_window_title() -> napi::Result<String, napi::Status> {
|
pub fn get_foreground_window_title() -> napi::Result<String> {
|
||||||
autotype::get_foreground_window_title().map_err(|_| {
|
autotype::get_foreground_window_title().map_err(|_| {
|
||||||
napi::Error::from_reason(
|
napi::Error::from_reason(
|
||||||
"Autotype Error: failed to get foreground window title".to_string(),
|
"Autotype Error: failed to get foreground window title".to_string(),
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ tokio = { workspace = true, features = ["sync"] }
|
|||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.build-dependencies]
|
[target.'cfg(target_os = "macos")'.build-dependencies]
|
||||||
cc = "=1.2.46"
|
cc = "=1.2.49"
|
||||||
glob = "=0.3.2"
|
glob = "=0.3.3"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"yargs": "18.0.0"
|
"yargs": "18.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "22.19.1",
|
"@types/node": "22.19.2",
|
||||||
"typescript": "5.4.2"
|
"typescript": "5.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -117,9 +117,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.19.1",
|
"version": "22.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz",
|
||||||
"integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
|
"integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"yargs": "18.0.0"
|
"yargs": "18.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "22.19.1",
|
"@types/node": "22.19.2",
|
||||||
"typescript": "5.4.2"
|
"typescript": "5.4.2"
|
||||||
},
|
},
|
||||||
"_moduleAliases": {
|
"_moduleAliases": {
|
||||||
|
|||||||
@@ -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": "2025.12.0",
|
"version": "2025.12.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bitwarden",
|
"bitwarden",
|
||||||
"password",
|
"password",
|
||||||
|
|||||||
@@ -42,14 +42,17 @@ import {
|
|||||||
} from "@bitwarden/auth/angular";
|
} from "@bitwarden/auth/angular";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components";
|
||||||
import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui";
|
import {
|
||||||
|
LockComponent,
|
||||||
|
ConfirmKeyConnectorDomainComponent,
|
||||||
|
RemovePasswordComponent,
|
||||||
|
} from "@bitwarden/key-management-ui";
|
||||||
|
|
||||||
import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard";
|
import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard";
|
||||||
import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard";
|
import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard";
|
||||||
import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component";
|
import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component";
|
||||||
import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component";
|
import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component";
|
||||||
import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component";
|
import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
||||||
import { VaultComponent } from "../vault/app/vault-v3/vault.component";
|
import { VaultComponent } from "../vault/app/vault-v3/vault.component";
|
||||||
|
|
||||||
@@ -117,11 +120,6 @@ const routes: Routes = [
|
|||||||
component: SendComponent,
|
component: SendComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "remove-password",
|
|
||||||
component: RemovePasswordComponent,
|
|
||||||
canActivate: [authGuard],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "fido2-assertion",
|
path: "fido2-assertion",
|
||||||
component: Fido2VaultComponent,
|
component: Fido2VaultComponent,
|
||||||
@@ -327,13 +325,24 @@ const routes: Routes = [
|
|||||||
pageIcon: LockIcon,
|
pageIcon: LockIcon,
|
||||||
} satisfies AnonLayoutWrapperData,
|
} satisfies AnonLayoutWrapperData,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "remove-password",
|
||||||
|
component: RemovePasswordComponent,
|
||||||
|
canActivate: [authGuard],
|
||||||
|
data: {
|
||||||
|
pageTitle: {
|
||||||
|
key: "verifyYourOrganization",
|
||||||
|
},
|
||||||
|
pageIcon: LockIcon,
|
||||||
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "confirm-key-connector-domain",
|
path: "confirm-key-connector-domain",
|
||||||
component: ConfirmKeyConnectorDomainComponent,
|
component: ConfirmKeyConnectorDomainComponent,
|
||||||
canActivate: [],
|
canActivate: [],
|
||||||
data: {
|
data: {
|
||||||
pageTitle: {
|
pageTitle: {
|
||||||
key: "confirmKeyConnectorDomain",
|
key: "verifyYourOrganization",
|
||||||
},
|
},
|
||||||
pageIcon: DomainIcon,
|
pageIcon: DomainIcon,
|
||||||
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
} satisfies RouteDataProperties & AnonLayoutWrapperData,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { DeleteAccountComponent } from "../auth/delete-account.component";
|
|||||||
import { LoginModule } from "../auth/login/login.module";
|
import { LoginModule } from "../auth/login/login.module";
|
||||||
import { SshAgentService } from "../autofill/services/ssh-agent.service";
|
import { SshAgentService } from "../autofill/services/ssh-agent.service";
|
||||||
import { PremiumComponent } from "../billing/app/accounts/premium.component";
|
import { PremiumComponent } from "../billing/app/accounts/premium.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
|
||||||
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
||||||
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
||||||
|
|
||||||
@@ -50,7 +49,6 @@ import { SharedModule } from "./shared/shared.module";
|
|||||||
ColorPasswordCountPipe,
|
ColorPasswordCountPipe,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
PremiumComponent,
|
PremiumComponent,
|
||||||
RemovePasswordComponent,
|
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
],
|
],
|
||||||
providers: [SshAgentService],
|
providers: [SshAgentService],
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<bit-nav-logo [openIcon]="logo" route="." [label]="'passwordManager' | i18n"></bit-nav-logo>
|
<bit-nav-logo [openIcon]="logo" route="." [label]="'passwordManager' | i18n"></bit-nav-logo>
|
||||||
|
|
||||||
<bit-nav-item icon="bwi-vault" [text]="'vault' | i18n" route="new-vault"></bit-nav-item>
|
<bit-nav-item icon="bwi-vault" [text]="'vault' | i18n" route="new-vault"></bit-nav-item>
|
||||||
<bit-nav-item icon="bwi-send" [text]="'send' | i18n" route="new-sends"></bit-nav-item>
|
<app-send-filters-nav></app-send-filters-nav>
|
||||||
</app-side-nav>
|
</app-side-nav>
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { RouterModule } from "@angular/router";
|
import { RouterModule } from "@angular/router";
|
||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
@@ -5,8 +6,18 @@ import { mock } from "jest-mock-extended";
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { NavigationModule } from "@bitwarden/components";
|
import { NavigationModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component";
|
||||||
|
|
||||||
import { DesktopLayoutComponent } from "./desktop-layout.component";
|
import { DesktopLayoutComponent } from "./desktop-layout.component";
|
||||||
|
|
||||||
|
// Mock the child component to isolate DesktopLayoutComponent testing
|
||||||
|
@Component({
|
||||||
|
selector: "app-send-filters-nav",
|
||||||
|
template: "",
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
class MockSendFiltersNavComponent {}
|
||||||
|
|
||||||
Object.defineProperty(window, "matchMedia", {
|
Object.defineProperty(window, "matchMedia", {
|
||||||
writable: true,
|
writable: true,
|
||||||
value: jest.fn().mockImplementation((query) => ({
|
value: jest.fn().mockImplementation((query) => ({
|
||||||
@@ -34,7 +45,12 @@ describe("DesktopLayoutComponent", () => {
|
|||||||
useValue: mock<I18nService>(),
|
useValue: mock<I18nService>(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
})
|
||||||
|
.overrideComponent(DesktopLayoutComponent, {
|
||||||
|
remove: { imports: [SendFiltersNavComponent] },
|
||||||
|
add: { imports: [MockSendFiltersNavComponent] },
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(DesktopLayoutComponent);
|
fixture = TestBed.createComponent(DesktopLayoutComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
@@ -58,4 +74,11 @@ describe("DesktopLayoutComponent", () => {
|
|||||||
|
|
||||||
expect(ngContent).toBeTruthy();
|
expect(ngContent).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders send filters navigation component", () => {
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
const sendFiltersNav = compiled.querySelector("app-send-filters-nav");
|
||||||
|
|
||||||
|
expect(sendFiltersNav).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,13 +5,22 @@ import { PasswordManagerLogo } from "@bitwarden/assets/svg";
|
|||||||
import { LayoutComponent, NavigationModule } from "@bitwarden/components";
|
import { LayoutComponent, NavigationModule } from "@bitwarden/components";
|
||||||
import { I18nPipe } from "@bitwarden/ui-common";
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
|
import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component";
|
||||||
|
|
||||||
import { DesktopSideNavComponent } from "./desktop-side-nav.component";
|
import { DesktopSideNavComponent } from "./desktop-side-nav.component";
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-layout",
|
selector: "app-layout",
|
||||||
imports: [RouterModule, I18nPipe, LayoutComponent, NavigationModule, DesktopSideNavComponent],
|
imports: [
|
||||||
|
RouterModule,
|
||||||
|
I18nPipe,
|
||||||
|
LayoutComponent,
|
||||||
|
NavigationModule,
|
||||||
|
DesktopSideNavComponent,
|
||||||
|
SendFiltersNavComponent,
|
||||||
|
],
|
||||||
templateUrl: "./desktop-layout.component.html",
|
templateUrl: "./desktop-layout.component.html",
|
||||||
})
|
})
|
||||||
export class DesktopLayoutComponent {
|
export class DesktopLayoutComponent {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import {
|
|||||||
} from "@bitwarden/common/auth/abstractions/auth.service";
|
} from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
|
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-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 { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
||||||
import { ClientType } from "@bitwarden/common/enums";
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
@@ -102,6 +103,7 @@ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/s
|
|||||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||||
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
import { GeneratorServicesModule } from "@bitwarden/generator-components";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
import {
|
import {
|
||||||
KdfConfigService,
|
KdfConfigService,
|
||||||
@@ -166,12 +168,12 @@ const safeProviders: SafeProvider[] = [
|
|||||||
safeProvider({
|
safeProvider({
|
||||||
provide: BiometricsService,
|
provide: BiometricsService,
|
||||||
useClass: RendererBiometricsService,
|
useClass: RendererBiometricsService,
|
||||||
deps: [],
|
deps: [TokenService],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: DesktopBiometricsService,
|
provide: DesktopBiometricsService,
|
||||||
useClass: RendererBiometricsService,
|
useClass: RendererBiometricsService,
|
||||||
deps: [],
|
deps: [TokenService],
|
||||||
}),
|
}),
|
||||||
safeProvider(NativeMessagingService),
|
safeProvider(NativeMessagingService),
|
||||||
safeProvider(BiometricMessageHandlerService),
|
safeProvider(BiometricMessageHandlerService),
|
||||||
@@ -201,8 +203,16 @@ const safeProviders: SafeProvider[] = [
|
|||||||
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
|
||||||
// the TokenService having to inject the PlatformUtilsService which introduces a
|
// the TokenService having to inject the PlatformUtilsService which introduces a
|
||||||
// circular dependency on Desktop only.
|
// circular dependency on Desktop only.
|
||||||
|
//
|
||||||
|
// For Windows portable builds, we disable secure storage to ensure tokens are
|
||||||
|
// stored on disk (in bitwarden-appdata) rather than in Windows Credential
|
||||||
|
// Manager, making them portable across machines. This allows users to move the USB drive
|
||||||
|
// between computers while maintaining authentication.
|
||||||
|
//
|
||||||
|
// Note: Portable mode does not use secure storage for read/write/clear operations,
|
||||||
|
// preventing any collision with tokens from a regular desktop installation.
|
||||||
provide: SUPPORTS_SECURE_STORAGE,
|
provide: SUPPORTS_SECURE_STORAGE,
|
||||||
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
|
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE && !ipc.platform.isWindowsPortable,
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: DEFAULT_VAULT_TIMEOUT,
|
provide: DEFAULT_VAULT_TIMEOUT,
|
||||||
@@ -499,7 +509,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [JslibServicesModule],
|
imports: [JslibServicesModule, GeneratorServicesModule],
|
||||||
declarations: [],
|
declarations: [],
|
||||||
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function
|
||||||
providers: safeProviders,
|
providers: safeProviders,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<bit-dialog #dialog dialogSize="large">
|
<bit-dialog #dialog dialogSize="large">
|
||||||
<span bitDialogTitle>{{ "exportVault" | i18n }}</span>
|
<span bitDialogTitle>{{ "export" | i18n }}</span>
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<tools-export
|
<tools-export
|
||||||
(formLoading)="this.loading = $event"
|
(formLoading)="this.loading = $event"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
bitFormButton
|
bitFormButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
>
|
>
|
||||||
{{ "exportVault" | i18n }}
|
{{ "export" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<bit-dialog #dialog dialogSize="large" background="alt">
|
<bit-dialog #dialog dialogSize="large" background="alt">
|
||||||
<span bitDialogTitle>{{ "importData" | i18n }}</span>
|
<span bitDialogTitle>{{ "import" | i18n }}</span>
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<div class="tw-relative">
|
<div class="tw-relative">
|
||||||
<tools-import
|
<tools-import
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
bitFormButton
|
bitFormButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
>
|
>
|
||||||
{{ "importData" | i18n }}
|
{{ "import" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
{{ "cancel" | i18n }}
|
{{ "cancel" | i18n }}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<bit-nav-group
|
||||||
|
icon="bwi-send"
|
||||||
|
[text]="'send' | i18n"
|
||||||
|
route="new-sends"
|
||||||
|
(click)="selectTypeAndNavigate()"
|
||||||
|
>
|
||||||
|
<bit-nav-item
|
||||||
|
icon="bwi-send"
|
||||||
|
[text]="'allSends' | i18n"
|
||||||
|
(click)="selectTypeAndNavigate(null); $event.stopPropagation()"
|
||||||
|
[forceActiveStyles]="activeSendType() === null"
|
||||||
|
></bit-nav-item>
|
||||||
|
<bit-nav-item
|
||||||
|
icon="bwi-file-text"
|
||||||
|
[text]="'sendTypeText' | i18n"
|
||||||
|
(click)="selectTypeAndNavigate(SendType.Text); $event.stopPropagation()"
|
||||||
|
[forceActiveStyles]="activeSendType() === SendType.Text"
|
||||||
|
></bit-nav-item>
|
||||||
|
<bit-nav-item
|
||||||
|
icon="bwi-file"
|
||||||
|
[text]="'sendTypeFile' | i18n"
|
||||||
|
(click)="selectTypeAndNavigate(SendType.File); $event.stopPropagation()"
|
||||||
|
[forceActiveStyles]="activeSendType() === SendType.File"
|
||||||
|
></bit-nav-item>
|
||||||
|
</bit-nav-group>
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { Router, provideRouter } from "@angular/router";
|
||||||
|
import { RouterTestingHarness } from "@angular/router/testing";
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
|
import { NavigationModule } from "@bitwarden/components";
|
||||||
|
import { SendListFiltersService } from "@bitwarden/send-ui";
|
||||||
|
|
||||||
|
import { SendFiltersNavComponent } from "./send-filters-nav.component";
|
||||||
|
|
||||||
|
@Component({ template: "", changeDetection: ChangeDetectionStrategy.OnPush })
|
||||||
|
class DummyComponent {}
|
||||||
|
|
||||||
|
Object.defineProperty(window, "matchMedia", {
|
||||||
|
writable: true,
|
||||||
|
value: jest.fn().mockImplementation((query) => ({
|
||||||
|
matches: true,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addListener: jest.fn(),
|
||||||
|
removeListener: jest.fn(),
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
dispatchEvent: jest.fn(),
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SendFiltersNavComponent", () => {
|
||||||
|
let component: SendFiltersNavComponent;
|
||||||
|
let fixture: ComponentFixture<SendFiltersNavComponent>;
|
||||||
|
let harness: RouterTestingHarness;
|
||||||
|
let filterFormValueSubject: BehaviorSubject<{ sendType: SendType | null }>;
|
||||||
|
let mockSendListFiltersService: Partial<SendListFiltersService>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
filterFormValueSubject = new BehaviorSubject<{ sendType: SendType | null }>({
|
||||||
|
sendType: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
mockSendListFiltersService = {
|
||||||
|
filterForm: {
|
||||||
|
value: { sendType: null },
|
||||||
|
valueChanges: filterFormValueSubject.asObservable(),
|
||||||
|
patchValue: jest.fn((value) => {
|
||||||
|
mockSendListFiltersService.filterForm.value = {
|
||||||
|
...mockSendListFiltersService.filterForm.value,
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
filterFormValueSubject.next(mockSendListFiltersService.filterForm.value);
|
||||||
|
}),
|
||||||
|
} as any,
|
||||||
|
filters$: filterFormValueSubject.asObservable(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [SendFiltersNavComponent, NavigationModule],
|
||||||
|
providers: [
|
||||||
|
provideRouter([
|
||||||
|
{ path: "vault", component: DummyComponent },
|
||||||
|
{ path: "new-sends", component: DummyComponent },
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
provide: SendListFiltersService,
|
||||||
|
useValue: mockSendListFiltersService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: I18nService,
|
||||||
|
useValue: {
|
||||||
|
t: jest.fn((key) => key),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
// Create harness and navigate to initial route
|
||||||
|
harness = await RouterTestingHarness.create("/vault");
|
||||||
|
|
||||||
|
// Create the component fixture separately (not a routed component)
|
||||||
|
fixture = TestBed.createComponent(SendFiltersNavComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates component", () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders bit-nav-group with Send icon and text", () => {
|
||||||
|
const compiled = fixture.nativeElement;
|
||||||
|
const navGroup = compiled.querySelector("bit-nav-group");
|
||||||
|
|
||||||
|
expect(navGroup).toBeTruthy();
|
||||||
|
expect(navGroup.getAttribute("icon")).toBe("bwi-send");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("component exposes SendType enum for template", () => {
|
||||||
|
expect(component["SendType"]).toBe(SendType);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isSendRouteActive", () => {
|
||||||
|
it("returns true when on /new-sends route", async () => {
|
||||||
|
await harness.navigateByUrl("/new-sends");
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component["isSendRouteActive"]()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns false when not on /new-sends route", () => {
|
||||||
|
expect(component["isSendRouteActive"]()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("activeSendType", () => {
|
||||||
|
it("returns the active send type when on send route and filter type is set", async () => {
|
||||||
|
await harness.navigateByUrl("/new-sends");
|
||||||
|
mockSendListFiltersService.filterForm.value = { sendType: SendType.Text };
|
||||||
|
filterFormValueSubject.next({ sendType: SendType.Text });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component["activeSendType"]()).toBe(SendType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined when not on send route", () => {
|
||||||
|
mockSendListFiltersService.filterForm.value = { sendType: SendType.Text };
|
||||||
|
filterFormValueSubject.next({ sendType: SendType.Text });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component["activeSendType"]()).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns null when on send route but no type is selected", async () => {
|
||||||
|
await harness.navigateByUrl("/new-sends");
|
||||||
|
mockSendListFiltersService.filterForm.value = { sendType: null };
|
||||||
|
filterFormValueSubject.next({ sendType: null });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component["activeSendType"]()).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("selectTypeAndNavigate", () => {
|
||||||
|
it("clears the sendType filter when called with no parameter", async () => {
|
||||||
|
await component["selectTypeAndNavigate"]();
|
||||||
|
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates filter form with Text type", async () => {
|
||||||
|
await component["selectTypeAndNavigate"](SendType.Text);
|
||||||
|
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: SendType.Text,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates filter form with File type", async () => {
|
||||||
|
await component["selectTypeAndNavigate"](SendType.File);
|
||||||
|
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: SendType.File,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("navigates to /new-sends when not on send route", async () => {
|
||||||
|
expect(harness.routeNativeElement?.textContent).toBeDefined();
|
||||||
|
|
||||||
|
await component["selectTypeAndNavigate"](SendType.Text);
|
||||||
|
|
||||||
|
const currentUrl = TestBed.inject(Router).url;
|
||||||
|
expect(currentUrl).toBe("/new-sends");
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: SendType.Text,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not navigate when already on send route (component is reactive)", async () => {
|
||||||
|
await harness.navigateByUrl("/new-sends");
|
||||||
|
const router = TestBed.inject(Router);
|
||||||
|
const navigateSpy = jest.spyOn(router, "navigate");
|
||||||
|
|
||||||
|
await component["selectTypeAndNavigate"](SendType.Text);
|
||||||
|
|
||||||
|
expect(navigateSpy).not.toHaveBeenCalled();
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: SendType.Text,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("navigates when clearing filter from different route", async () => {
|
||||||
|
await component["selectTypeAndNavigate"](); // No parameter = clear filter
|
||||||
|
|
||||||
|
const currentUrl = TestBed.inject(Router).url;
|
||||||
|
expect(currentUrl).toBe("/new-sends");
|
||||||
|
expect(mockSendListFiltersService.filterForm.patchValue).toHaveBeenCalledWith({
|
||||||
|
sendType: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
|
||||||
|
import { toSignal } from "@angular/core/rxjs-interop";
|
||||||
|
import { NavigationEnd, Router } from "@angular/router";
|
||||||
|
import { filter, map, startWith } from "rxjs";
|
||||||
|
|
||||||
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
|
import { NavigationModule } from "@bitwarden/components";
|
||||||
|
import { SendListFiltersService } from "@bitwarden/send-ui";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation component that renders Send filter options in the sidebar.
|
||||||
|
* Fully reactive using signals - no manual subscriptions or method-based computed values.
|
||||||
|
* - Parent "Send" nav-group clears filter (shows all sends)
|
||||||
|
* - Child "Text"/"File" items set filter to specific type
|
||||||
|
* - Active states computed reactively from filter signal + route signal
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: "app-send-filters-nav",
|
||||||
|
templateUrl: "./send-filters-nav.component.html",
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
imports: [CommonModule, NavigationModule, I18nPipe],
|
||||||
|
})
|
||||||
|
export class SendFiltersNavComponent {
|
||||||
|
protected readonly SendType = SendType;
|
||||||
|
private readonly filtersService = inject(SendListFiltersService);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
private readonly currentFilter = toSignal(this.filtersService.filters$);
|
||||||
|
|
||||||
|
// Track whether current route is the send route
|
||||||
|
private readonly isSendRouteActive = toSignal(
|
||||||
|
this.router.events.pipe(
|
||||||
|
filter((event) => event instanceof NavigationEnd),
|
||||||
|
map((event) => (event as NavigationEnd).urlAfterRedirects.includes("/new-sends")),
|
||||||
|
startWith(this.router.url.includes("/new-sends")),
|
||||||
|
),
|
||||||
|
{ initialValue: this.router.url.includes("/new-sends") },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Computed: Active send type (null when on send route with no filter, undefined when not on send route)
|
||||||
|
protected readonly activeSendType = computed(() => {
|
||||||
|
return this.isSendRouteActive() ? this.currentFilter()?.sendType : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update send filter and navigate to /new-sends (only if not already there - send-v2 component reacts to filter changes)
|
||||||
|
protected async selectTypeAndNavigate(type?: SendType): Promise<void> {
|
||||||
|
this.filtersService.filterForm.patchValue({ sendType: type !== undefined ? type : null });
|
||||||
|
|
||||||
|
if (!this.router.url.includes("/new-sends")) {
|
||||||
|
await this.router.navigate(["/new-sends"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
|
// @ts-strict-ignore
|
||||||
|
import { ChangeDetectorRef } from "@angular/core";
|
||||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject, of } from "rxjs";
|
import { BehaviorSubject, of } from "rxjs";
|
||||||
|
|
||||||
@@ -15,6 +19,7 @@ import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.s
|
|||||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
import { SendListFiltersService } from "@bitwarden/send-ui";
|
||||||
|
|
||||||
import * as utils from "../../../utils";
|
import * as utils from "../../../utils";
|
||||||
import { SearchBarService } from "../../layout/search/search-bar.service";
|
import { SearchBarService } from "../../layout/search/search-bar.service";
|
||||||
@@ -35,6 +40,8 @@ describe("SendV2Component", () => {
|
|||||||
let broadcasterService: MockProxy<BroadcasterService>;
|
let broadcasterService: MockProxy<BroadcasterService>;
|
||||||
let accountService: MockProxy<AccountService>;
|
let accountService: MockProxy<AccountService>;
|
||||||
let policyService: MockProxy<PolicyService>;
|
let policyService: MockProxy<PolicyService>;
|
||||||
|
let sendListFiltersService: SendListFiltersService;
|
||||||
|
let changeDetectorRef: MockProxy<ChangeDetectorRef>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
sendService = mock<SendService>();
|
sendService = mock<SendService>();
|
||||||
@@ -42,6 +49,13 @@ describe("SendV2Component", () => {
|
|||||||
broadcasterService = mock<BroadcasterService>();
|
broadcasterService = mock<BroadcasterService>();
|
||||||
accountService = mock<AccountService>();
|
accountService = mock<AccountService>();
|
||||||
policyService = mock<PolicyService>();
|
policyService = mock<PolicyService>();
|
||||||
|
changeDetectorRef = mock<ChangeDetectorRef>();
|
||||||
|
|
||||||
|
// Create real SendListFiltersService with mocked dependencies
|
||||||
|
const formBuilder = new FormBuilder();
|
||||||
|
const i18nService = mock<I18nService>();
|
||||||
|
i18nService.t.mockImplementation((key: string) => key);
|
||||||
|
sendListFiltersService = new SendListFiltersService(i18nService, formBuilder);
|
||||||
|
|
||||||
// Mock sendViews$ observable
|
// Mock sendViews$ observable
|
||||||
sendService.sendViews$ = of([]);
|
sendService.sendViews$ = of([]);
|
||||||
@@ -51,6 +65,10 @@ describe("SendV2Component", () => {
|
|||||||
accountService.activeAccount$ = of({ id: "test-user-id" } as any);
|
accountService.activeAccount$ = of({ id: "test-user-id" } as any);
|
||||||
policyService.policyAppliesToUser$ = jest.fn().mockReturnValue(of(false));
|
policyService.policyAppliesToUser$ = jest.fn().mockReturnValue(of(false));
|
||||||
|
|
||||||
|
// Mock SearchService methods needed by base component
|
||||||
|
const mockSearchService = mock<SearchService>();
|
||||||
|
mockSearchService.isSearchable.mockResolvedValue(false);
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [SendV2Component],
|
imports: [SendV2Component],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -59,7 +77,7 @@ describe("SendV2Component", () => {
|
|||||||
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
|
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
|
||||||
{ provide: EnvironmentService, useValue: mock<EnvironmentService>() },
|
{ provide: EnvironmentService, useValue: mock<EnvironmentService>() },
|
||||||
{ provide: BroadcasterService, useValue: broadcasterService },
|
{ provide: BroadcasterService, useValue: broadcasterService },
|
||||||
{ provide: SearchService, useValue: mock<SearchService>() },
|
{ provide: SearchService, useValue: mockSearchService },
|
||||||
{ provide: PolicyService, useValue: policyService },
|
{ provide: PolicyService, useValue: policyService },
|
||||||
{ provide: SearchBarService, useValue: searchBarService },
|
{ provide: SearchBarService, useValue: searchBarService },
|
||||||
{ provide: LogService, useValue: mock<LogService>() },
|
{ provide: LogService, useValue: mock<LogService>() },
|
||||||
@@ -67,6 +85,8 @@ describe("SendV2Component", () => {
|
|||||||
{ provide: DialogService, useValue: mock<DialogService>() },
|
{ provide: DialogService, useValue: mock<DialogService>() },
|
||||||
{ provide: ToastService, useValue: mock<ToastService>() },
|
{ provide: ToastService, useValue: mock<ToastService>() },
|
||||||
{ provide: AccountService, useValue: accountService },
|
{ provide: AccountService, useValue: accountService },
|
||||||
|
{ provide: SendListFiltersService, useValue: sendListFiltersService },
|
||||||
|
{ provide: ChangeDetectorRef, useValue: changeDetectorRef },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
@@ -331,7 +351,6 @@ describe("SendV2Component", () => {
|
|||||||
describe("load", () => {
|
describe("load", () => {
|
||||||
it("sets loading states correctly", async () => {
|
it("sets loading states correctly", async () => {
|
||||||
jest.spyOn(component, "search").mockResolvedValue();
|
jest.spyOn(component, "search").mockResolvedValue();
|
||||||
jest.spyOn(component, "selectAll");
|
|
||||||
|
|
||||||
expect(component.loaded).toBeFalsy();
|
expect(component.loaded).toBeFalsy();
|
||||||
|
|
||||||
@@ -341,14 +360,17 @@ describe("SendV2Component", () => {
|
|||||||
expect(component.loaded).toBe(true);
|
expect(component.loaded).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls selectAll when onSuccessfulLoad is not set", async () => {
|
it("sets up sendViews$ subscription", async () => {
|
||||||
|
const mockSends = [new SendView(), new SendView()];
|
||||||
|
sendService.sendViews$ = of(mockSends);
|
||||||
jest.spyOn(component, "search").mockResolvedValue();
|
jest.spyOn(component, "search").mockResolvedValue();
|
||||||
jest.spyOn(component, "selectAll");
|
|
||||||
component.onSuccessfulLoad = null;
|
|
||||||
|
|
||||||
await component.load();
|
await component.load();
|
||||||
|
|
||||||
expect(component.selectAll).toHaveBeenCalled();
|
// Give observable time to emit
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
|
|
||||||
|
expect(component.sends).toEqual(mockSends);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls onSuccessfulLoad when it is set", async () => {
|
it("calls onSuccessfulLoad when it is set", async () => {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, OnInit, OnDestroy, ViewChild, NgZone, ChangeDetectorRef } from "@angular/core";
|
import { Component, OnInit, OnDestroy, ViewChild, NgZone, ChangeDetectorRef } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { mergeMap } from "rxjs";
|
import { mergeMap, Subscription } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component";
|
import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component";
|
||||||
@@ -14,11 +15,13 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
import { SendListFiltersService } from "@bitwarden/send-ui";
|
||||||
|
|
||||||
import { invokeMenu, RendererMenuItem } from "../../../utils";
|
import { invokeMenu, RendererMenuItem } from "../../../utils";
|
||||||
import { SearchBarService } from "../../layout/search/search-bar.service";
|
import { SearchBarService } from "../../layout/search/search-bar.service";
|
||||||
@@ -55,6 +58,9 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
// Tracks the current UI state: viewing list (None), adding new Send (Add), or editing existing Send (Edit)
|
// Tracks the current UI state: viewing list (None), adding new Send (Add), or editing existing Send (Edit)
|
||||||
action: Action = Action.None;
|
action: Action = Action.None;
|
||||||
|
|
||||||
|
// Subscription for sendViews$ cleanup
|
||||||
|
private sendViewsSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
sendService: SendService,
|
sendService: SendService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
@@ -71,6 +77,7 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
toastService: ToastService,
|
toastService: ToastService,
|
||||||
accountService: AccountService,
|
accountService: AccountService,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
|
private sendListFiltersService: SendListFiltersService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
sendService,
|
sendService,
|
||||||
@@ -88,11 +95,16 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Listen to search bar changes and update the Send list filter
|
// Listen to search bar changes and update the Send list filter
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
this.searchBarService.searchText$.pipe(takeUntilDestroyed()).subscribe((searchText) => {
|
||||||
this.searchBarService.searchText$.subscribe((searchText) => {
|
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
this.searchTextChanged();
|
this.searchTextChanged();
|
||||||
setTimeout(() => this.cdr.detectChanges(), 250);
|
});
|
||||||
|
|
||||||
|
// Listen to filter changes from sidebar navigation
|
||||||
|
this.sendListFiltersService.filterForm.valueChanges
|
||||||
|
.pipe(takeUntilDestroyed())
|
||||||
|
.subscribe((filters) => {
|
||||||
|
this.applySendTypeFilter(filters);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +115,10 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
|
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
|
|
||||||
|
// Read current filter synchronously to avoid race condition on navigation
|
||||||
|
const currentFilter = this.sendListFiltersService.filterForm.value;
|
||||||
|
this.applySendTypeFilter(currentFilter);
|
||||||
|
|
||||||
// Listen for sync completion events to refresh the Send list
|
// Listen for sync completion events to refresh the Send list
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
@@ -118,8 +134,18 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply send type filter to display: centralized logic for initial load and filter changes
|
||||||
|
private applySendTypeFilter(filters: Partial<{ sendType: SendType | null }>): void {
|
||||||
|
if (filters.sendType === null || filters.sendType === undefined) {
|
||||||
|
this.selectAll();
|
||||||
|
} else {
|
||||||
|
this.selectType(filters.sendType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up subscriptions and disable search bar when component is destroyed
|
// Clean up subscriptions and disable search bar when component is destroyed
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
this.sendViewsSubscription?.unsubscribe();
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
||||||
this.searchBarService.setEnabled(false);
|
this.searchBarService.setEnabled(false);
|
||||||
}
|
}
|
||||||
@@ -130,7 +156,12 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
// Note: The filter parameter is ignored in this implementation for desktop-specific behavior.
|
// Note: The filter parameter is ignored in this implementation for desktop-specific behavior.
|
||||||
async load(filter: (send: SendView) => boolean = null) {
|
async load(filter: (send: SendView) => boolean = null) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.sendService.sendViews$
|
|
||||||
|
// Recreate subscription on each load (required for sync refresh)
|
||||||
|
// Manual cleanup in ngOnDestroy is intentional - load() is called multiple times
|
||||||
|
this.sendViewsSubscription?.unsubscribe();
|
||||||
|
|
||||||
|
this.sendViewsSubscription = this.sendService.sendViews$
|
||||||
.pipe(
|
.pipe(
|
||||||
mergeMap(async (sends) => {
|
mergeMap(async (sends) => {
|
||||||
this.sends = sends;
|
this.sends = sends;
|
||||||
@@ -143,9 +174,6 @@ export class SendV2Component extends BaseSendComponent implements OnInit, OnDest
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
if (this.onSuccessfulLoad != null) {
|
if (this.onSuccessfulLoad != null) {
|
||||||
await this.onSuccessfulLoad();
|
await this.onSuccessfulLoad();
|
||||||
} else {
|
|
||||||
// Default action
|
|
||||||
this.selectAll();
|
|
||||||
}
|
}
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ describe("DesktopLoginComponentService", () => {
|
|||||||
codeChallenge,
|
codeChallenge,
|
||||||
state,
|
state,
|
||||||
email,
|
email,
|
||||||
|
undefined,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(state);
|
expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(state);
|
||||||
@@ -145,4 +146,55 @@ describe("DesktopLoginComponentService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("redirectToSsoLoginWithOrganizationSsoIdentifier", () => {
|
||||||
|
// Array of all permutations of isAppImage and isDev
|
||||||
|
const permutations = [
|
||||||
|
[true, false], // Case 1: isAppImage true
|
||||||
|
[false, true], // Case 2: isDev true
|
||||||
|
[true, true], // Case 3: all true
|
||||||
|
[false, false], // Case 4: all false
|
||||||
|
];
|
||||||
|
|
||||||
|
permutations.forEach(([isAppImage, isDev]) => {
|
||||||
|
it("calls redirectToSso with orgSsoIdentifier", async () => {
|
||||||
|
(global as any).ipc.platform.isAppImage = isAppImage;
|
||||||
|
(global as any).ipc.platform.isDev = isDev;
|
||||||
|
|
||||||
|
const email = "test@bitwarden.com";
|
||||||
|
const state = "testState";
|
||||||
|
const codeVerifier = "testCodeVerifier";
|
||||||
|
const codeChallenge = "testCodeChallenge";
|
||||||
|
const orgSsoIdentifier = "orgSsoId";
|
||||||
|
|
||||||
|
passwordGenerationService.generatePassword.mockResolvedValueOnce(state);
|
||||||
|
passwordGenerationService.generatePassword.mockResolvedValueOnce(codeVerifier);
|
||||||
|
jest.spyOn(Utils, "fromBufferToUrlB64").mockReturnValue(codeChallenge);
|
||||||
|
|
||||||
|
await service.redirectToSsoLoginWithOrganizationSsoIdentifier(email, orgSsoIdentifier);
|
||||||
|
|
||||||
|
if (isAppImage || isDev) {
|
||||||
|
expect(ipc.platform.localhostCallbackService.openSsoPrompt).toHaveBeenCalledWith(
|
||||||
|
codeChallenge,
|
||||||
|
state,
|
||||||
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
expect(ssoUrlService.buildSsoUrl).toHaveBeenCalledWith(
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
expect.any(String),
|
||||||
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
|
);
|
||||||
|
expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(state);
|
||||||
|
expect(ssoLoginService.setCodeVerifier).toHaveBeenCalledWith(codeVerifier);
|
||||||
|
expect(platformUtilsService.launchUri).toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,11 +48,12 @@ export class DesktopLoginComponentService
|
|||||||
email: string,
|
email: string,
|
||||||
state: string,
|
state: string,
|
||||||
codeChallenge: string,
|
codeChallenge: string,
|
||||||
|
orgSsoIdentifier?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// For platforms that cannot support a protocol-based (e.g. bitwarden://) callback, we use a localhost callback
|
// For platforms that cannot support a protocol-based (e.g. bitwarden://) callback, we use a localhost callback
|
||||||
// Otherwise, we launch the SSO component in a browser window and wait for the callback
|
// Otherwise, we launch the SSO component in a browser window and wait for the callback
|
||||||
if (ipc.platform.isAppImage || ipc.platform.isDev) {
|
if (ipc.platform.isAppImage || ipc.platform.isDev) {
|
||||||
await this.initiateSsoThroughLocalhostCallback(email, state, codeChallenge);
|
await this.initiateSsoThroughLocalhostCallback(email, state, codeChallenge, orgSsoIdentifier);
|
||||||
} else {
|
} else {
|
||||||
const env = await firstValueFrom(this.environmentService.environment$);
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
const webVaultUrl = env.getWebVaultUrl();
|
const webVaultUrl = env.getWebVaultUrl();
|
||||||
@@ -66,6 +67,7 @@ export class DesktopLoginComponentService
|
|||||||
state,
|
state,
|
||||||
codeChallenge,
|
codeChallenge,
|
||||||
email,
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.platformUtilsService.launchUri(ssoWebAppUrl);
|
this.platformUtilsService.launchUri(ssoWebAppUrl);
|
||||||
@@ -76,9 +78,15 @@ export class DesktopLoginComponentService
|
|||||||
email: string,
|
email: string,
|
||||||
state: string,
|
state: string,
|
||||||
challenge: string,
|
challenge: string,
|
||||||
|
orgSsoIdentifier?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await ipc.platform.localhostCallbackService.openSsoPrompt(challenge, state, email);
|
await ipc.platform.localhostCallbackService.openSsoPrompt(
|
||||||
|
challenge,
|
||||||
|
state,
|
||||||
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
|
);
|
||||||
// FIXME: Remove when updating file. Eslint update
|
// FIXME: Remove when updating file. Eslint update
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export class MainSshAgentService {
|
|||||||
init() {
|
init() {
|
||||||
// handle sign request passing to UI
|
// handle sign request passing to UI
|
||||||
sshagent
|
sshagent
|
||||||
.serve(async (err: Error, sshUiRequest: sshagent.SshUiRequest) => {
|
.serve(async (err: Error | null, sshUiRequest: sshagent.SshUiRequest): Promise<boolean> => {
|
||||||
// clear all old (> SIGN_TIMEOUT) requests
|
// clear all old (> SIGN_TIMEOUT) requests
|
||||||
this.requestResponses = this.requestResponses.filter(
|
this.requestResponses = this.requestResponses.filter(
|
||||||
(response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT),
|
(response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { AccountService, Account } from "@bitwarden/common/auth/abstractions/acc
|
|||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { mockAccountInfoWith } 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 { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -40,9 +41,10 @@ describe("Fido2CreateComponent", () => {
|
|||||||
|
|
||||||
const activeAccountSubject = new BehaviorSubject<Account | null>({
|
const activeAccountSubject = new BehaviorSubject<Account | null>({
|
||||||
id: "test-user-id" as UserId,
|
id: "test-user-id" as UserId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
|||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { Account, UserId } from "@bitwarden/common/platform/models/domain/account";
|
import { Account, UserId } from "@bitwarden/common/platform/models/domain/account";
|
||||||
|
import { mockAccountInfoWith } from "@bitwarden/common/spec";
|
||||||
|
|
||||||
import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service";
|
import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service";
|
||||||
|
|
||||||
@@ -30,9 +31,10 @@ describe("DesktopAutotypeDefaultSettingPolicy", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockAccountSubject = new BehaviorSubject<Account | null>({
|
mockAccountSubject = new BehaviorSubject<Account | null>({
|
||||||
id: mockUserId,
|
id: mockUserId,
|
||||||
|
...mockAccountInfoWith({
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
emailVerified: true,
|
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
mockFeatureFlagSubject = new BehaviorSubject<boolean>(true);
|
mockFeatureFlagSubject = new BehaviorSubject<boolean>(true);
|
||||||
mockAuthStatusSubject = new BehaviorSubject<AuthenticationStatus>(
|
mockAuthStatusSubject = new BehaviorSubject<AuthenticationStatus>(
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ export type NativeWindowObject = {
|
|||||||
windowXy?: { x: number; y: number };
|
windowXy?: { x: number; y: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
export class DesktopFido2UserInterfaceService
|
export class DesktopFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction<NativeWindowObject> {
|
||||||
implements Fido2UserInterfaceServiceAbstraction<NativeWindowObject>
|
|
||||||
{
|
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { UserKey } from "@bitwarden/common/types/key";
|
import { UserKey } from "@bitwarden/common/types/key";
|
||||||
@@ -13,6 +15,10 @@ import { DesktopBiometricsService } from "./desktop.biometrics.service";
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RendererBiometricsService extends DesktopBiometricsService {
|
export class RendererBiometricsService extends DesktopBiometricsService {
|
||||||
|
constructor(private tokenService: TokenService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async authenticateWithBiometrics(): Promise<boolean> {
|
async authenticateWithBiometrics(): Promise<boolean> {
|
||||||
return await ipc.keyManagement.biometric.authenticateWithBiometrics();
|
return await ipc.keyManagement.biometric.authenticateWithBiometrics();
|
||||||
}
|
}
|
||||||
@@ -31,6 +37,10 @@ export class RendererBiometricsService extends DesktopBiometricsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBiometricsStatusForUser(id: UserId): Promise<BiometricsStatus> {
|
async getBiometricsStatusForUser(id: UserId): Promise<BiometricsStatus> {
|
||||||
|
if ((await firstValueFrom(this.tokenService.hasAccessToken$(id))) === false) {
|
||||||
|
return BiometricsStatus.NotEnabledInConnectedDesktopApp;
|
||||||
|
}
|
||||||
|
|
||||||
return await ipc.keyManagement.biometric.getBiometricsStatusForUser(id);
|
return await ipc.keyManagement.biometric.getBiometricsStatusForUser(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<div id="remove-password-page" *ngIf="!loading">
|
|
||||||
<div class="content">
|
|
||||||
<h1>{{ "removeMasterPassword" | i18n }}</h1>
|
|
||||||
<p>{{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "organizationName" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.name }}</p>
|
|
||||||
<p class="tw-mb-0">{{ "keyConnectorDomain" | i18n }}:</p>
|
|
||||||
<p class="tw-text-muted tw-mb-6">{{ organization.keyConnectorUrl }}</p>
|
|
||||||
<div class="buttons">
|
|
||||||
<button type="submit" class="btn primary block" [disabled]="action" (click)="convert()">
|
|
||||||
<b [hidden]="continuing">{{ "removeMasterPassword" | i18n }}</b>
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!continuing" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn secondary block" [disabled]="action" (click)="leave()">
|
|
||||||
<b [hidden]="leaving">{{ "leaveOrganization" | i18n }}</b>
|
|
||||||
<i class="bwi bwi-spinner bwi-spin" [hidden]="!leaving" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
|
|
||||||
import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui";
|
|
||||||
|
|
||||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
|
||||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
|
||||||
@Component({
|
|
||||||
selector: "app-remove-password",
|
|
||||||
templateUrl: "remove-password.component.html",
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class RemovePasswordComponent extends BaseRemovePasswordComponent {}
|
|
||||||
@@ -708,6 +708,9 @@
|
|||||||
"addAttachment": {
|
"addAttachment": {
|
||||||
"message": "Add attachment"
|
"message": "Add attachment"
|
||||||
},
|
},
|
||||||
|
"itemsTransferred": {
|
||||||
|
"message": "Items transferred"
|
||||||
|
},
|
||||||
"fixEncryption": {
|
"fixEncryption": {
|
||||||
"message": "Fix encryption"
|
"message": "Fix encryption"
|
||||||
},
|
},
|
||||||
@@ -1195,8 +1198,8 @@
|
|||||||
"followUs": {
|
"followUs": {
|
||||||
"message": "Follow us"
|
"message": "Follow us"
|
||||||
},
|
},
|
||||||
"syncVault": {
|
"syncNow": {
|
||||||
"message": "Sync vault"
|
"message": "Sync now"
|
||||||
},
|
},
|
||||||
"changeMasterPass": {
|
"changeMasterPass": {
|
||||||
"message": "Change master password"
|
"message": "Change master password"
|
||||||
@@ -1772,8 +1775,11 @@
|
|||||||
"exportFrom": {
|
"exportFrom": {
|
||||||
"message": "Export from"
|
"message": "Export from"
|
||||||
},
|
},
|
||||||
"exportVault": {
|
"export": {
|
||||||
"message": "Export vault"
|
"message": "Export"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"message": "Import"
|
||||||
},
|
},
|
||||||
"fileFormat": {
|
"fileFormat": {
|
||||||
"message": "File format"
|
"message": "File format"
|
||||||
@@ -2634,9 +2640,6 @@
|
|||||||
"removedMasterPassword": {
|
"removedMasterPassword": {
|
||||||
"message": "Master password removed"
|
"message": "Master password removed"
|
||||||
},
|
},
|
||||||
"removeMasterPasswordForOrganizationUserKeyConnector": {
|
|
||||||
"message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator."
|
|
||||||
},
|
|
||||||
"organizationName": {
|
"organizationName": {
|
||||||
"message": "Organization name"
|
"message": "Organization name"
|
||||||
},
|
},
|
||||||
@@ -3492,10 +3495,6 @@
|
|||||||
"aliasDomain": {
|
"aliasDomain": {
|
||||||
"message": "Alias domain"
|
"message": "Alias domain"
|
||||||
},
|
},
|
||||||
"importData": {
|
|
||||||
"message": "Import data",
|
|
||||||
"description": "Used for the desktop menu item and the header of the import dialog"
|
|
||||||
},
|
|
||||||
"importError": {
|
"importError": {
|
||||||
"message": "Import error"
|
"message": "Import error"
|
||||||
},
|
},
|
||||||
@@ -4334,6 +4333,45 @@
|
|||||||
"upgradeToPremium": {
|
"upgradeToPremium": {
|
||||||
"message": "Upgrade to Premium"
|
"message": "Upgrade to Premium"
|
||||||
},
|
},
|
||||||
|
"removeMasterPasswordForOrgUserKeyConnector":{
|
||||||
|
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
|
||||||
|
},
|
||||||
|
"continueWithLogIn": {
|
||||||
|
"message": "Continue with log in"
|
||||||
|
},
|
||||||
|
"doNotContinue": {
|
||||||
|
"message": "Do not continue"
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"message": "Domain"
|
||||||
|
},
|
||||||
|
"keyConnectorDomainTooltip": {
|
||||||
|
"message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin."
|
||||||
|
},
|
||||||
|
"verifyYourOrganization": {
|
||||||
|
"message": "Verify your organization to log in"
|
||||||
|
},
|
||||||
|
"organizationVerified":{
|
||||||
|
"message": "Organization verified"
|
||||||
|
},
|
||||||
|
"domainVerified":{
|
||||||
|
"message": "Domain verified"
|
||||||
|
},
|
||||||
|
"leaveOrganizationContent": {
|
||||||
|
"message": "If you don't verify your organization, your access to the organization will be revoked."
|
||||||
|
},
|
||||||
|
"leaveNow": {
|
||||||
|
"message": "Leave now"
|
||||||
|
},
|
||||||
|
"verifyYourDomainToLogin": {
|
||||||
|
"message": "Verify your domain to log in"
|
||||||
|
},
|
||||||
|
"verifyYourDomainDescription": {
|
||||||
|
"message": "To continue with log in, verify this domain."
|
||||||
|
},
|
||||||
|
"confirmKeyConnectorOrganizationUserDescription": {
|
||||||
|
"message": "To continue with log in, verify the organization and domain."
|
||||||
|
},
|
||||||
"sessionTimeoutSettingsAction": {
|
"sessionTimeoutSettingsAction": {
|
||||||
"message": "Timeout action"
|
"message": "Timeout action"
|
||||||
},
|
},
|
||||||
@@ -4395,5 +4433,53 @@
|
|||||||
},
|
},
|
||||||
"upgrade": {
|
"upgrade": {
|
||||||
"message": "Upgrade"
|
"message": "Upgrade"
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogTitle": {
|
||||||
|
"message": "Are you sure you want to leave?"
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogContentOne": {
|
||||||
|
"message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features."
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogContentTwo": {
|
||||||
|
"message": "Contact your admin to regain access."
|
||||||
|
},
|
||||||
|
"leaveConfirmationDialogConfirmButton": {
|
||||||
|
"message": "Leave $ORGANIZATION$",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"howToManageMyVault": {
|
||||||
|
"message": "How do I manage my vault?"
|
||||||
|
},
|
||||||
|
"transferItemsToOrganizationTitle": {
|
||||||
|
"message": "Transfer items to $ORGANIZATION$",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transferItemsToOrganizationContent": {
|
||||||
|
"message": "$ORGANIZATION$ is requiring all items to be owned by the organization for security and compliance. Click accept to transfer ownership of your items.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acceptTransfer": {
|
||||||
|
"message": "Accept transfer"
|
||||||
|
},
|
||||||
|
"declineAndLeave": {
|
||||||
|
"message": "Decline and leave"
|
||||||
|
},
|
||||||
|
"whyAmISeeingThis": {
|
||||||
|
"message": "Why am I seeing this?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,8 +146,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get syncVault(): MenuItemConstructorOptions {
|
private get syncVault(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: "syncVault",
|
id: "syncNow",
|
||||||
label: this.localize("syncVault"),
|
label: this.localize("syncNow"),
|
||||||
click: () => this.sendMessage("syncVault"),
|
click: () => this.sendMessage("syncVault"),
|
||||||
enabled: this.hasAuthenticatedAccounts,
|
enabled: this.hasAuthenticatedAccounts,
|
||||||
};
|
};
|
||||||
@@ -155,8 +155,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get importVault(): MenuItemConstructorOptions {
|
private get importVault(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: "importVault",
|
id: "import",
|
||||||
label: this.localize("importData"),
|
label: this.localize("import"),
|
||||||
click: () => this.sendMessage("importVault"),
|
click: () => this.sendMessage("importVault"),
|
||||||
enabled: !this._isLocked,
|
enabled: !this._isLocked,
|
||||||
};
|
};
|
||||||
@@ -164,8 +164,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu {
|
|||||||
|
|
||||||
private get exportVault(): MenuItemConstructorOptions {
|
private get exportVault(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: "exportVault",
|
id: "export",
|
||||||
label: this.localize("exportVault"),
|
label: this.localize("export"),
|
||||||
click: () => this.sendMessage("exportVault"),
|
click: () => this.sendMessage("exportVault"),
|
||||||
enabled: !this._isLocked,
|
enabled: !this._isLocked,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { isDev } from "../utils";
|
|||||||
import { WindowMain } from "./window.main";
|
import { WindowMain } from "./window.main";
|
||||||
|
|
||||||
export class NativeMessagingMain {
|
export class NativeMessagingMain {
|
||||||
private ipcServer: ipc.IpcServer | null;
|
private ipcServer: ipc.NativeIpcServer | null;
|
||||||
private connected: number[] = [];
|
private connected: number[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -78,7 +78,7 @@ export class NativeMessagingMain {
|
|||||||
this.ipcServer.stop();
|
this.ipcServer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ipcServer = await ipc.IpcServer.listen("bw", (error, msg) => {
|
this.ipcServer = await ipc.NativeIpcServer.listen("bw", (error, msg) => {
|
||||||
switch (msg.kind) {
|
switch (msg.kind) {
|
||||||
case ipc.IpcMessageType.Connected: {
|
case ipc.IpcMessageType.Connected: {
|
||||||
this.connected.push(msg.clientId);
|
this.connected.push(msg.clientId);
|
||||||
|
|||||||
4
apps/desktop/src/package-lock.json
generated
4
apps/desktop/src/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2025.12.0",
|
"version": "2025.12.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@bitwarden/desktop",
|
"name": "@bitwarden/desktop",
|
||||||
"version": "2025.12.0",
|
"version": "2025.12.1",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/desktop-napi": "file:../desktop_native/napi"
|
"@bitwarden/desktop-napi": "file:../desktop_native/napi"
|
||||||
|
|||||||
@@ -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": "2025.12.0",
|
"version": "2025.12.1",
|
||||||
"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",
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
<bit-dialog>
|
<bit-dialog>
|
||||||
<div class="tw-font-medium" bitDialogTitle>{{ "sshkeyApprovalTitle" | i18n }}</div>
|
<div class="tw-font-medium" bitDialogTitle>{{ "sshkeyApprovalTitle" | i18n }}</div>
|
||||||
<div bitDialogContent>
|
<div bitDialogContent>
|
||||||
<app-callout
|
@if (params.isAgentForwarding) {
|
||||||
type="warning"
|
<bit-callout type="warning" title="{{ 'agentForwardingWarningTitle' | i18n }}">
|
||||||
title="{{ 'agentForwardingWarningTitle' | i18n }}"
|
|
||||||
*ngIf="params.isAgentForwarding"
|
|
||||||
>
|
|
||||||
{{ 'agentForwardingWarningText' | i18n }}
|
{{ 'agentForwardingWarningText' | i18n }}
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
|
}
|
||||||
|
|
||||||
<b>{{params.applicationName}}</b> {{ "sshkeyApprovalMessageInfix" | i18n }}
|
<b>{{params.applicationName}}</b> {{ "sshkeyApprovalMessageInfix" | i18n }}
|
||||||
<b>{{params.cipherName}}</b>
|
<b>{{params.cipherName}}</b>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
FormFieldModule,
|
FormFieldModule,
|
||||||
IconButtonModule,
|
IconButtonModule,
|
||||||
DialogService,
|
DialogService,
|
||||||
|
CalloutModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
export interface ApproveSshRequestParams {
|
export interface ApproveSshRequestParams {
|
||||||
@@ -35,6 +36,7 @@ export interface ApproveSshRequestParams {
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
FormFieldModule,
|
FormFieldModule,
|
||||||
|
CalloutModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ApproveSshRequestComponent {
|
export class ApproveSshRequestComponent {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export type RunCommandParams<C extends CommandDefinition> = {
|
|||||||
export type RunCommandResult<C extends CommandDefinition> = C["output"];
|
export type RunCommandResult<C extends CommandDefinition> = C["output"];
|
||||||
|
|
||||||
export class NativeAutofillMain {
|
export class NativeAutofillMain {
|
||||||
private ipcServer: autofill.IpcServer | null;
|
private ipcServer?: autofill.AutofillIpcServer;
|
||||||
private messageBuffer: BufferedMessage[] = [];
|
private messageBuffer: BufferedMessage[] = [];
|
||||||
private listenerReady = false;
|
private listenerReady = false;
|
||||||
|
|
||||||
@@ -70,13 +70,13 @@ export class NativeAutofillMain {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
this.ipcServer = await autofill.IpcServer.listen(
|
this.ipcServer = await autofill.AutofillIpcServer.listen(
|
||||||
"af",
|
"af",
|
||||||
// RegistrationCallback
|
// RegistrationCallback
|
||||||
(error, clientId, sequenceNumber, request) => {
|
(error, clientId, sequenceNumber, request) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.logService.error("autofill.IpcServer.registration", error);
|
this.logService.error("autofill.IpcServer.registration", error);
|
||||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
this.ipcServer?.completeError(clientId, sequenceNumber, String(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.safeSend("autofill.passkeyRegistration", {
|
this.safeSend("autofill.passkeyRegistration", {
|
||||||
@@ -89,7 +89,7 @@ export class NativeAutofillMain {
|
|||||||
(error, clientId, sequenceNumber, request) => {
|
(error, clientId, sequenceNumber, request) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.logService.error("autofill.IpcServer.assertion", error);
|
this.logService.error("autofill.IpcServer.assertion", error);
|
||||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
this.ipcServer?.completeError(clientId, sequenceNumber, String(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.safeSend("autofill.passkeyAssertion", {
|
this.safeSend("autofill.passkeyAssertion", {
|
||||||
@@ -102,7 +102,7 @@ export class NativeAutofillMain {
|
|||||||
(error, clientId, sequenceNumber, request) => {
|
(error, clientId, sequenceNumber, request) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.logService.error("autofill.IpcServer.assertion", error);
|
this.logService.error("autofill.IpcServer.assertion", error);
|
||||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
this.ipcServer?.completeError(clientId, sequenceNumber, String(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.safeSend("autofill.passkeyAssertionWithoutUserInterface", {
|
this.safeSend("autofill.passkeyAssertionWithoutUserInterface", {
|
||||||
@@ -115,7 +115,7 @@ export class NativeAutofillMain {
|
|||||||
(error, clientId, sequenceNumber, status) => {
|
(error, clientId, sequenceNumber, status) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.logService.error("autofill.IpcServer.nativeStatus", error);
|
this.logService.error("autofill.IpcServer.nativeStatus", error);
|
||||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
this.ipcServer?.completeError(clientId, sequenceNumber, String(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.safeSend("autofill.nativeStatus", {
|
this.safeSend("autofill.nativeStatus", {
|
||||||
@@ -137,19 +137,19 @@ export class NativeAutofillMain {
|
|||||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||||
this.logService.debug("autofill.completePasskeyRegistration", data);
|
this.logService.debug("autofill.completePasskeyRegistration", data);
|
||||||
const { clientId, sequenceNumber, response } = data;
|
const { clientId, sequenceNumber, response } = data;
|
||||||
this.ipcServer.completeRegistration(clientId, sequenceNumber, response);
|
this.ipcServer?.completeRegistration(clientId, sequenceNumber, response);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("autofill.completePasskeyAssertion", (event, data) => {
|
ipcMain.on("autofill.completePasskeyAssertion", (event, data) => {
|
||||||
this.logService.debug("autofill.completePasskeyAssertion", data);
|
this.logService.debug("autofill.completePasskeyAssertion", data);
|
||||||
const { clientId, sequenceNumber, response } = data;
|
const { clientId, sequenceNumber, response } = data;
|
||||||
this.ipcServer.completeAssertion(clientId, sequenceNumber, response);
|
this.ipcServer?.completeAssertion(clientId, sequenceNumber, response);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on("autofill.completeError", (event, data) => {
|
ipcMain.on("autofill.completeError", (event, data) => {
|
||||||
this.logService.debug("autofill.completeError", data);
|
this.logService.debug("autofill.completeError", data);
|
||||||
const { clientId, sequenceNumber, error } = data;
|
const { clientId, sequenceNumber, error } = data;
|
||||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
this.ipcServer?.completeError(clientId, sequenceNumber, String(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
isFlatpak,
|
isFlatpak,
|
||||||
isMacAppStore,
|
isMacAppStore,
|
||||||
isSnapStore,
|
isSnapStore,
|
||||||
|
isWindowsPortable,
|
||||||
isWindowsStore,
|
isWindowsStore,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
|
|
||||||
@@ -108,8 +109,13 @@ const ephemeralStore = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const localhostCallbackService = {
|
const localhostCallbackService = {
|
||||||
openSsoPrompt: (codeChallenge: string, state: string, email: string): Promise<void> => {
|
openSsoPrompt: (
|
||||||
return ipcRenderer.invoke("openSsoPrompt", { codeChallenge, state, email });
|
codeChallenge: string,
|
||||||
|
state: string,
|
||||||
|
email: string,
|
||||||
|
orgSsoIdentifier?: string,
|
||||||
|
): Promise<void> => {
|
||||||
|
return ipcRenderer.invoke("openSsoPrompt", { codeChallenge, state, email, orgSsoIdentifier });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -128,6 +134,7 @@ export default {
|
|||||||
isDev: isDev(),
|
isDev: isDev(),
|
||||||
isMacAppStore: isMacAppStore(),
|
isMacAppStore: isMacAppStore(),
|
||||||
isWindowsStore: isWindowsStore(),
|
isWindowsStore: isWindowsStore(),
|
||||||
|
isWindowsPortable: isWindowsPortable(),
|
||||||
isFlatpak: isFlatpak(),
|
isFlatpak: isFlatpak(),
|
||||||
isSnapStore: isSnapStore(),
|
isSnapStore: isSnapStore(),
|
||||||
isAppImage: isAppImage(),
|
isAppImage: isAppImage(),
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export class ElectronLogMainService extends BaseLogService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.transports.file.level = "info";
|
log.transports.file.level = isDev() ? "debug" : "info";
|
||||||
if (this.logDir != null) {
|
if (this.logDir != null) {
|
||||||
log.transports.file.resolvePathFn = () => path.join(this.logDir, "app.log");
|
log.transports.file.resolvePathFn = () => path.join(this.logDir, "app.log");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,20 +25,25 @@ export class SSOLocalhostCallbackService {
|
|||||||
private messagingService: MessageSender,
|
private messagingService: MessageSender,
|
||||||
private ssoUrlService: SsoUrlService,
|
private ssoUrlService: SsoUrlService,
|
||||||
) {
|
) {
|
||||||
ipcMain.handle("openSsoPrompt", async (event, { codeChallenge, state, email }) => {
|
ipcMain.handle(
|
||||||
|
"openSsoPrompt",
|
||||||
|
async (event, { codeChallenge, state, email, orgSsoIdentifier }) => {
|
||||||
// Close any existing server before starting new one
|
// Close any existing server before starting new one
|
||||||
if (this.currentServer) {
|
if (this.currentServer) {
|
||||||
await this.closeCurrentServer();
|
await this.closeCurrentServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.openSsoPrompt(codeChallenge, state, email).then(({ ssoCode, recvState }) => {
|
return this.openSsoPrompt(codeChallenge, state, email, orgSsoIdentifier).then(
|
||||||
|
({ ssoCode, recvState }) => {
|
||||||
this.messagingService.send("ssoCallback", {
|
this.messagingService.send("ssoCallback", {
|
||||||
code: ssoCode,
|
code: ssoCode,
|
||||||
state: recvState,
|
state: recvState,
|
||||||
redirectUri: this.ssoRedirectUri,
|
redirectUri: this.ssoRedirectUri,
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
});
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async closeCurrentServer(): Promise<void> {
|
private async closeCurrentServer(): Promise<void> {
|
||||||
@@ -58,6 +63,7 @@ export class SSOLocalhostCallbackService {
|
|||||||
codeChallenge: string,
|
codeChallenge: string,
|
||||||
state: string,
|
state: string,
|
||||||
email: string,
|
email: string,
|
||||||
|
orgSsoIdentifier?: string,
|
||||||
): Promise<{ ssoCode: string; recvState: string }> {
|
): Promise<{ ssoCode: string; recvState: string }> {
|
||||||
const env = await firstValueFrom(this.environmentService.environment$);
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
|
|
||||||
@@ -121,6 +127,7 @@ export class SSOLocalhostCallbackService {
|
|||||||
state,
|
state,
|
||||||
codeChallenge,
|
codeChallenge,
|
||||||
email,
|
email,
|
||||||
|
orgSsoIdentifier,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set up error handler before attempting to listen
|
// Set up error handler before attempting to listen
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { NgZone } from "@angular/core";
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject, filter, firstValueFrom, of, take, timeout, timer } from "rxjs";
|
import { BehaviorSubject, filter, firstValueFrom, of, take, timeout, timer } from "rxjs";
|
||||||
|
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||||
@@ -10,7 +10,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { FakeAccountService } from "@bitwarden/common/spec";
|
import { mockAccountInfoWith, FakeAccountService } from "@bitwarden/common/spec";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
@@ -23,17 +23,15 @@ import { BiometricMessageHandlerService } from "./biometric-message-handler.serv
|
|||||||
|
|
||||||
const SomeUser = "SomeUser" as UserId;
|
const SomeUser = "SomeUser" as UserId;
|
||||||
const AnotherUser = "SomeOtherUser" as UserId;
|
const AnotherUser = "SomeOtherUser" as UserId;
|
||||||
const accounts: Record<UserId, AccountInfo> = {
|
const accounts = {
|
||||||
[SomeUser]: {
|
[SomeUser]: mockAccountInfoWith({
|
||||||
name: "some user",
|
name: "some user",
|
||||||
email: "some.user@example.com",
|
email: "some.user@example.com",
|
||||||
emailVerified: true,
|
}),
|
||||||
},
|
[AnotherUser]: mockAccountInfoWith({
|
||||||
[AnotherUser]: {
|
|
||||||
name: "some other user",
|
name: "some other user",
|
||||||
email: "some.other.user@example.com",
|
email: "some.other.user@example.com",
|
||||||
emailVerified: true,
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("BiometricMessageHandlerService", () => {
|
describe("BiometricMessageHandlerService", () => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const MaxCheckedCount = 500;
|
|||||||
* Maximum for bulk reinvite operations when the IncreaseBulkReinviteLimitForCloud
|
* Maximum for bulk reinvite operations when the IncreaseBulkReinviteLimitForCloud
|
||||||
* feature flag is enabled on cloud environments.
|
* feature flag is enabled on cloud environments.
|
||||||
*/
|
*/
|
||||||
export const CloudBulkReinviteLimit = 4000;
|
export const CloudBulkReinviteLimit = 8000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the user matches the status, or where the status is `null`, if the user is active (not revoked).
|
* Returns true if the user matches the status, or where the status is `null`, if the user is active (not revoked).
|
||||||
|
|||||||
@@ -104,12 +104,12 @@
|
|||||||
*ngIf="organization.use2fa && organization.isOwner"
|
*ngIf="organization.use2fa && organization.isOwner"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
[text]="'importData' | i18n"
|
[text]="'import' | i18n"
|
||||||
route="settings/tools/import"
|
route="settings/tools/import"
|
||||||
*ngIf="organization.canAccessImport"
|
*ngIf="organization.canAccessImport"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
[text]="'exportVault' | i18n"
|
[text]="'export' | i18n"
|
||||||
route="settings/tools/export"
|
route="settings/tools/export"
|
||||||
*ngIf="canAccessExport$ | async"
|
*ngIf="canAccessExport$ | async"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user