mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 23:33:31 +00:00
[PM-16470] Remove v1 autofill owned refresh code (#12579)
* Remove v1 autofill owned settings * Remove Fido2 v1 components * Remove conditional to enable AutofillOnPageLoadOrgPolicy --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
fe9b0976ee
commit
22f4822efc
@@ -1,36 +0,0 @@
|
|||||||
<div
|
|
||||||
role="group"
|
|
||||||
appA11yTitle="{{ cipher.name }} {{ cipher.subTitle }}"
|
|
||||||
class="virtual-scroll-item"
|
|
||||||
[ngClass]="{ 'override-last': !last }"
|
|
||||||
>
|
|
||||||
<div class="box-content-row box-content-row-flex">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
(click)="selectCipher(cipher)"
|
|
||||||
tabindex="0"
|
|
||||||
appStopClick
|
|
||||||
title="{{ title }} - {{ cipher.name }}"
|
|
||||||
[ngClass]="{ 'row-main': true, 'row-selected': isSelected && !isSearching }"
|
|
||||||
>
|
|
||||||
<app-vault-icon [cipher]="cipher"></app-vault-icon>
|
|
||||||
<div class="row-main-content">
|
|
||||||
<span class="text">
|
|
||||||
<span class="truncate-box">
|
|
||||||
<span class="truncate">{{ cipher.name }}</span>
|
|
||||||
<ng-container *ngIf="cipher.organizationId">
|
|
||||||
<i
|
|
||||||
class="bwi bwi-collection text-muted"
|
|
||||||
title="{{ 'shared' | i18n }}"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<span class="sr-only">{{ "shared" | i18n }}</span>
|
|
||||||
</ng-container>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span class="detail" *ngIf="getSubName(cipher)">{{ getSubName(cipher) }}</span>
|
|
||||||
<span class="detail" *ngIf="cipher.subTitle">{{ cipher.subTitle }}</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from "@angular/core";
|
|
||||||
|
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-fido2-cipher-row-v1",
|
|
||||||
templateUrl: "fido2-cipher-row-v1.component.html",
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
|
||||||
export class Fido2CipherRowV1Component {
|
|
||||||
@Output() onSelected = new EventEmitter<CipherView>();
|
|
||||||
@Input() cipher: CipherView;
|
|
||||||
@Input() last: boolean;
|
|
||||||
@Input() title: string;
|
|
||||||
@Input() isSearching: boolean;
|
|
||||||
@Input() isSelected: boolean;
|
|
||||||
|
|
||||||
protected selectCipher(c: CipherView) {
|
|
||||||
this.onSelected.emit(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a subname for the cipher.
|
|
||||||
* If this has a FIDO2 credential, and the cipher.name is different from the FIDO2 credential's rpId, return the rpId.
|
|
||||||
* @param c Cipher
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
protected getSubName(c: CipherView): string | null {
|
|
||||||
const fido2Credentials = c.login?.fido2Credentials;
|
|
||||||
|
|
||||||
if (!fido2Credentials || fido2Credentials.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [fido2Credential] = fido2Credentials;
|
|
||||||
|
|
||||||
return c.name !== fido2Credential.rpId ? fido2Credential.rpId : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<ng-container *ngIf="(fido2PopoutSessionData$ | async).fallbackSupported">
|
|
||||||
<div class="useBrowserlink">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
(click)="toggle()"
|
|
||||||
cdkOverlayOrigin
|
|
||||||
#trigger="cdkOverlayOrigin"
|
|
||||||
aria-haspopup="dialog"
|
|
||||||
aria-controls="cdk-overlay-container"
|
|
||||||
>
|
|
||||||
<span class="text-primary">
|
|
||||||
{{ "useDeviceOrHardwareKey" | i18n }}
|
|
||||||
</span>
|
|
||||||
<i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-template
|
|
||||||
cdkConnectedOverlay
|
|
||||||
[cdkConnectedOverlayOrigin]="trigger"
|
|
||||||
[cdkConnectedOverlayOpen]="isOpen"
|
|
||||||
[cdkConnectedOverlayPositions]="overlayPosition"
|
|
||||||
[cdkConnectedOverlayHasBackdrop]="true"
|
|
||||||
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
|
|
||||||
(backdropClick)="isOpen = false"
|
|
||||||
(detach)="close()"
|
|
||||||
>
|
|
||||||
<div class="box-content">
|
|
||||||
<div
|
|
||||||
class="fido2-browser-selector-dropdown"
|
|
||||||
[@transformPanel]="'open'"
|
|
||||||
cdkTrapFocus
|
|
||||||
cdkTrapFocusAutoCapture
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
>
|
|
||||||
<button type="button" class="fido2-browser-selector-dropdown-item" (click)="abort(false)">
|
|
||||||
<span>{{ "justOnce" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
<br />
|
|
||||||
<button type="button" class="fido2-browser-selector-dropdown-item" (click)="abort()">
|
|
||||||
<span>{{ "alwaysForThisSite" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<div
|
|
||||||
*ngIf="showOverlay"
|
|
||||||
class="tw-absolute tw-w-full tw-h-full tw-bg-background-alt tw-inset-0 tw-bg-opacity-80 tw-z-50"
|
|
||||||
></div>
|
|
||||||
</ng-container>
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { animate, state, style, transition, trigger } from "@angular/animations";
|
|
||||||
import { ConnectedPosition } from "@angular/cdk/overlay";
|
|
||||||
import { Component } from "@angular/core";
|
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
|
||||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
||||||
|
|
||||||
import { fido2PopoutSessionData$ } from "../../../vault/popup/utils/fido2-popout-session-data";
|
|
||||||
import { BrowserFido2UserInterfaceSession } from "../../fido2/services/browser-fido2-user-interface.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-fido2-use-browser-link-v1",
|
|
||||||
templateUrl: "fido2-use-browser-link-v1.component.html",
|
|
||||||
animations: [
|
|
||||||
trigger("transformPanel", [
|
|
||||||
state(
|
|
||||||
"void",
|
|
||||||
style({
|
|
||||||
opacity: 0,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
transition(
|
|
||||||
"void => open",
|
|
||||||
animate(
|
|
||||||
"100ms linear",
|
|
||||||
style({
|
|
||||||
opacity: 1,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
transition("* => void", animate("100ms linear", style({ opacity: 0 }))),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class Fido2UseBrowserLinkV1Component {
|
|
||||||
showOverlay = false;
|
|
||||||
isOpen = false;
|
|
||||||
overlayPosition: ConnectedPosition[] = [
|
|
||||||
{
|
|
||||||
originX: "start",
|
|
||||||
originY: "bottom",
|
|
||||||
overlayX: "start",
|
|
||||||
overlayY: "top",
|
|
||||||
offsetY: 5,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
protected fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private domainSettingsService: DomainSettingsService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private i18nService: I18nService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
toggle() {
|
|
||||||
this.isOpen = !this.isOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.isOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Aborts the current FIDO2 session and fallsback to the browser.
|
|
||||||
* @param excludeDomain - Identifies if the domain should be excluded from future FIDO2 prompts.
|
|
||||||
*/
|
|
||||||
protected async abort(excludeDomain = true) {
|
|
||||||
this.close();
|
|
||||||
const sessionData = await firstValueFrom(this.fido2PopoutSessionData$);
|
|
||||||
|
|
||||||
if (!excludeDomain) {
|
|
||||||
this.abortSession(sessionData.sessionId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Show overlay to prevent the user from interacting with the page.
|
|
||||||
this.showOverlay = true;
|
|
||||||
await this.handleDomainExclusion(sessionData.senderUrl);
|
|
||||||
// Give the user a chance to see the toast before closing the popout.
|
|
||||||
await Utils.delay(2000);
|
|
||||||
this.abortSession(sessionData.sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Excludes the domain from future FIDO2 prompts.
|
|
||||||
* @param uri - The domain uri to exclude from future FIDO2 prompts.
|
|
||||||
*/
|
|
||||||
private async handleDomainExclusion(uri: string) {
|
|
||||||
const existingDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
|
|
||||||
|
|
||||||
const validDomain = Utils.getHostname(uri);
|
|
||||||
const savedDomains: NeverDomains = {
|
|
||||||
...existingDomains,
|
|
||||||
};
|
|
||||||
savedDomains[validDomain] = null;
|
|
||||||
|
|
||||||
await this.domainSettingsService.setNeverDomains(savedDomains);
|
|
||||||
|
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"success",
|
|
||||||
null,
|
|
||||||
this.i18nService.t("domainAddedToExcludedDomains", validDomain),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private abortSession(sessionId: string) {
|
|
||||||
BrowserFido2UserInterfaceSession.abortPopout(sessionId, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<ng-container *ngIf="data$ | async as data">
|
|
||||||
<div class="auth-wrapper">
|
|
||||||
<div class="auth-header">
|
|
||||||
<div class="left">
|
|
||||||
<ng-container *ngIf="data.message.type != BrowserFido2MessageTypes.PickCredentialRequest">
|
|
||||||
<div class="logo">
|
|
||||||
<i class="bwi bwi-shield"></i>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="data.message.type === BrowserFido2MessageTypes.PickCredentialRequest">
|
|
||||||
<div class="logo">
|
|
||||||
<i class="bwi bwi-shield"></i><span><strong>bit</strong>warden</span>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
<ng-container
|
|
||||||
*ngIf="data.message.type === BrowserFido2MessageTypes.ConfirmNewCredentialRequest"
|
|
||||||
>
|
|
||||||
<div class="search">
|
|
||||||
<input
|
|
||||||
type="{{ searchTypeSearch ? 'search' : 'text' }}"
|
|
||||||
placeholder="{{ 'searchVault' | i18n }}"
|
|
||||||
id="search"
|
|
||||||
[(ngModel)]="searchText"
|
|
||||||
(input)="search()"
|
|
||||||
autocomplete="off"
|
|
||||||
appAutofocus
|
|
||||||
/>
|
|
||||||
<i class="bwi bwi-search" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<button type="button" (click)="addCipher()" appA11yTitle="{{ 'addItem' | i18n }}">
|
|
||||||
<i class="bwi bwi-plus bwi-lg bwi-fw" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-container>
|
|
||||||
<ng-container
|
|
||||||
*ngIf="
|
|
||||||
data.message.type === BrowserFido2MessageTypes.PickCredentialRequest ||
|
|
||||||
data.message.type === BrowserFido2MessageTypes.ConfirmNewCredentialRequest
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="auth-flow">
|
|
||||||
<p class="subtitle" appA11yTitle="{{ subtitleText | i18n }}">
|
|
||||||
{{ subtitleText | i18n }}
|
|
||||||
</p>
|
|
||||||
<!-- Display when ciphers exist -->
|
|
||||||
<ng-container *ngIf="displayedCiphers.length > 0">
|
|
||||||
<div class="box list">
|
|
||||||
<div class="box-content">
|
|
||||||
<app-fido2-cipher-row-v1
|
|
||||||
*ngFor="let cipherItem of displayedCiphers"
|
|
||||||
[cipher]="cipherItem"
|
|
||||||
[isSearching]="searchPending"
|
|
||||||
title="{{ 'passkeyItem' | i18n }}"
|
|
||||||
(onSelected)="selectedPasskey($event)"
|
|
||||||
[isSelected]="cipher === cipherItem"
|
|
||||||
></app-fido2-cipher-row-v1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
(click)="submit()"
|
|
||||||
class="btn primary block"
|
|
||||||
appA11yTitle="{{ credentialText | i18n }}"
|
|
||||||
>
|
|
||||||
<span [hidden]="loading">
|
|
||||||
{{ credentialText | i18n }}
|
|
||||||
</span>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-lg bwi-spin"
|
|
||||||
[hidden]="!loading"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-container *ngIf="!displayedCiphers.length">
|
|
||||||
<div class="box">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
(click)="saveNewLogin()"
|
|
||||||
class="btn primary block"
|
|
||||||
appA11yTitle="{{ 'savePasskeyNewLogin' | i18n }}"
|
|
||||||
>
|
|
||||||
<span [hidden]="loading">
|
|
||||||
{{ "savePasskeyNewLogin" | i18n }}
|
|
||||||
</span>
|
|
||||||
<i
|
|
||||||
class="bwi bwi-spinner bwi-lg bwi-spin"
|
|
||||||
[hidden]="!loading"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container
|
|
||||||
*ngIf="data.message.type === BrowserFido2MessageTypes.InformExcludedCredentialRequest"
|
|
||||||
>
|
|
||||||
<div class="auth-flow">
|
|
||||||
<p class="subtitle">{{ "passkeyAlreadyExists" | i18n }}</p>
|
|
||||||
<div class="box list">
|
|
||||||
<div class="box-content">
|
|
||||||
<app-fido2-cipher-row-v1
|
|
||||||
*ngFor="let cipherItem of displayedCiphers"
|
|
||||||
[cipher]="cipherItem"
|
|
||||||
title="{{ 'passkeyItem' | i18n }}"
|
|
||||||
(onSelected)="selectedPasskey($event)"
|
|
||||||
[isSelected]="cipher === cipherItem"
|
|
||||||
></app-fido2-cipher-row-v1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn primary block" (click)="viewPasskey()">
|
|
||||||
<span [hidden]="loading">{{ "viewItem" | i18n }}</span>
|
|
||||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!loading" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container
|
|
||||||
*ngIf="data.message.type === BrowserFido2MessageTypes.InformCredentialNotFoundRequest"
|
|
||||||
>
|
|
||||||
<div class="auth-flow">
|
|
||||||
<p class="subtitle">{{ "noPasskeysFoundForThisApplication" | i18n }}</p>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn primary block" (click)="abort(false)">
|
|
||||||
<span [hidden]="loading">{{ "close" | i18n }}</span>
|
|
||||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!loading" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<app-fido2-use-browser-link-v1></app-fido2-use-browser-link-v1>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
@@ -1,445 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
import {
|
|
||||||
BehaviorSubject,
|
|
||||||
combineLatest,
|
|
||||||
concatMap,
|
|
||||||
filter,
|
|
||||||
firstValueFrom,
|
|
||||||
map,
|
|
||||||
Observable,
|
|
||||||
Subject,
|
|
||||||
take,
|
|
||||||
takeUntil,
|
|
||||||
} from "rxjs";
|
|
||||||
|
|
||||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
|
||||||
import { CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
|
|
||||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
|
||||||
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
|
|
||||||
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
|
|
||||||
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
|
|
||||||
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
|
||||||
import { PasswordRepromptService } from "@bitwarden/vault";
|
|
||||||
|
|
||||||
import { ZonedMessageListenerService } from "../../../platform/browser/zoned-message-listener.service";
|
|
||||||
import { VaultPopoutType } from "../../../vault/popup/utils/vault-popout-window";
|
|
||||||
import { Fido2UserVerificationService } from "../../../vault/services/fido2-user-verification.service";
|
|
||||||
import {
|
|
||||||
BrowserFido2Message,
|
|
||||||
BrowserFido2UserInterfaceSession,
|
|
||||||
BrowserFido2MessageTypes,
|
|
||||||
} from "../../fido2/services/browser-fido2-user-interface.service";
|
|
||||||
|
|
||||||
interface ViewData {
|
|
||||||
message: BrowserFido2Message;
|
|
||||||
fallbackSupported: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-fido2-v1",
|
|
||||||
templateUrl: "fido2-v1.component.html",
|
|
||||||
styleUrls: [],
|
|
||||||
})
|
|
||||||
export class Fido2V1Component implements OnInit, OnDestroy {
|
|
||||||
private destroy$ = new Subject<void>();
|
|
||||||
private hasSearched = false;
|
|
||||||
|
|
||||||
protected cipher: CipherView;
|
|
||||||
protected searchTypeSearch = false;
|
|
||||||
protected searchPending = false;
|
|
||||||
protected searchText: string;
|
|
||||||
protected url: string;
|
|
||||||
protected hostname: string;
|
|
||||||
protected data$: Observable<ViewData>;
|
|
||||||
protected sessionId?: string;
|
|
||||||
protected senderTabId?: string;
|
|
||||||
protected ciphers?: CipherView[] = [];
|
|
||||||
protected displayedCiphers?: CipherView[] = [];
|
|
||||||
protected loading = false;
|
|
||||||
protected subtitleText: string;
|
|
||||||
protected credentialText: string;
|
|
||||||
protected BrowserFido2MessageTypes = BrowserFido2MessageTypes;
|
|
||||||
|
|
||||||
private message$ = new BehaviorSubject<BrowserFido2Message>(null);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private router: Router,
|
|
||||||
private activatedRoute: ActivatedRoute,
|
|
||||||
private cipherService: CipherService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private domainSettingsService: DomainSettingsService,
|
|
||||||
private searchService: SearchService,
|
|
||||||
private logService: LogService,
|
|
||||||
private dialogService: DialogService,
|
|
||||||
private browserMessagingApi: ZonedMessageListenerService,
|
|
||||||
private passwordRepromptService: PasswordRepromptService,
|
|
||||||
private fido2UserVerificationService: Fido2UserVerificationService,
|
|
||||||
private accountService: AccountService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.searchTypeSearch = !this.platformUtilsService.isSafari();
|
|
||||||
|
|
||||||
const queryParams$ = this.activatedRoute.queryParamMap.pipe(
|
|
||||||
take(1),
|
|
||||||
map((queryParamMap) => ({
|
|
||||||
sessionId: queryParamMap.get("sessionId"),
|
|
||||||
senderTabId: queryParamMap.get("senderTabId"),
|
|
||||||
senderUrl: queryParamMap.get("senderUrl"),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
combineLatest([
|
|
||||||
queryParams$,
|
|
||||||
this.browserMessagingApi.messageListener$() as Observable<BrowserFido2Message>,
|
|
||||||
])
|
|
||||||
.pipe(
|
|
||||||
concatMap(async ([queryParams, message]) => {
|
|
||||||
this.sessionId = queryParams.sessionId;
|
|
||||||
this.senderTabId = queryParams.senderTabId;
|
|
||||||
this.url = queryParams.senderUrl;
|
|
||||||
// For a 'NewSessionCreatedRequest', abort if it doesn't belong to the current session.
|
|
||||||
if (
|
|
||||||
message.type === BrowserFido2MessageTypes.NewSessionCreatedRequest &&
|
|
||||||
message.sessionId !== queryParams.sessionId
|
|
||||||
) {
|
|
||||||
this.abort(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore messages that don't belong to the current session.
|
|
||||||
if (message.sessionId !== queryParams.sessionId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.type === BrowserFido2MessageTypes.AbortRequest) {
|
|
||||||
this.abort(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}),
|
|
||||||
filter((message) => !!message),
|
|
||||||
takeUntil(this.destroy$),
|
|
||||||
)
|
|
||||||
.subscribe((message) => {
|
|
||||||
this.message$.next(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.data$ = this.message$.pipe(
|
|
||||||
filter((message) => message != undefined),
|
|
||||||
concatMap(async (message) => {
|
|
||||||
switch (message.type) {
|
|
||||||
case BrowserFido2MessageTypes.ConfirmNewCredentialRequest: {
|
|
||||||
const equivalentDomains = await firstValueFrom(
|
|
||||||
this.domainSettingsService.getUrlEquivalentDomains(this.url),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.ciphers = (await this.cipherService.getAllDecrypted()).filter(
|
|
||||||
(cipher) => cipher.type === CipherType.Login && !cipher.isDeleted,
|
|
||||||
);
|
|
||||||
this.displayedCiphers = this.ciphers.filter(
|
|
||||||
(cipher) =>
|
|
||||||
cipher.login.matchesUri(this.url, equivalentDomains) &&
|
|
||||||
this.hasNoOtherPasskeys(cipher, message.userHandle),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.displayedCiphers.length > 0) {
|
|
||||||
this.selectedPasskey(this.displayedCiphers[0]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BrowserFido2MessageTypes.PickCredentialRequest: {
|
|
||||||
const activeUserId = await firstValueFrom(
|
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.ciphers = await Promise.all(
|
|
||||||
message.cipherIds.map(async (cipherId) => {
|
|
||||||
const cipher = await this.cipherService.get(cipherId);
|
|
||||||
return cipher.decrypt(
|
|
||||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.displayedCiphers = [...this.ciphers];
|
|
||||||
if (this.displayedCiphers.length > 0) {
|
|
||||||
this.selectedPasskey(this.displayedCiphers[0]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BrowserFido2MessageTypes.InformExcludedCredentialRequest: {
|
|
||||||
const activeUserId = await firstValueFrom(
|
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.ciphers = await Promise.all(
|
|
||||||
message.existingCipherIds.map(async (cipherId) => {
|
|
||||||
const cipher = await this.cipherService.get(cipherId);
|
|
||||||
return cipher.decrypt(
|
|
||||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.displayedCiphers = [...this.ciphers];
|
|
||||||
|
|
||||||
if (this.displayedCiphers.length > 0) {
|
|
||||||
this.selectedPasskey(this.displayedCiphers[0]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.subtitleText =
|
|
||||||
this.displayedCiphers.length > 0
|
|
||||||
? this.getCredentialSubTitleText(message.type)
|
|
||||||
: "noMatchingPasskeyLogin";
|
|
||||||
|
|
||||||
this.credentialText = this.getCredentialButtonText(message.type);
|
|
||||||
return {
|
|
||||||
message,
|
|
||||||
fallbackSupported: "fallbackSupported" in message && message.fallbackSupported,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
takeUntil(this.destroy$),
|
|
||||||
);
|
|
||||||
|
|
||||||
queryParams$.pipe(takeUntil(this.destroy$)).subscribe((queryParams) => {
|
|
||||||
this.send({
|
|
||||||
sessionId: queryParams.sessionId,
|
|
||||||
type: BrowserFido2MessageTypes.ConnectResponse,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async submit() {
|
|
||||||
const data = this.message$.value;
|
|
||||||
if (data?.type === BrowserFido2MessageTypes.PickCredentialRequest) {
|
|
||||||
// TODO: Revert to use fido2 user verification service once user verification for passkeys is approved for production.
|
|
||||||
// PM-4577 - https://github.com/bitwarden/clients/pull/8746
|
|
||||||
const userVerified = await this.handleUserVerification(data.userVerification, this.cipher);
|
|
||||||
|
|
||||||
this.send({
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
cipherId: this.cipher.id,
|
|
||||||
type: BrowserFido2MessageTypes.PickCredentialResponse,
|
|
||||||
userVerified,
|
|
||||||
});
|
|
||||||
} else if (data?.type === BrowserFido2MessageTypes.ConfirmNewCredentialRequest) {
|
|
||||||
if (this.cipher.login.hasFido2Credentials) {
|
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
|
||||||
title: { key: "overwritePasskey" },
|
|
||||||
content: { key: "overwritePasskeyAlert" },
|
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Revert to use fido2 user verification service once user verification for passkeys is approved for production.
|
|
||||||
// PM-4577 - https://github.com/bitwarden/clients/pull/8746
|
|
||||||
const userVerified = await this.handleUserVerification(data.userVerification, this.cipher);
|
|
||||||
|
|
||||||
this.send({
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
cipherId: this.cipher.id,
|
|
||||||
type: BrowserFido2MessageTypes.ConfirmNewCredentialResponse,
|
|
||||||
userVerified,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async saveNewLogin() {
|
|
||||||
const data = this.message$.value;
|
|
||||||
if (data?.type === BrowserFido2MessageTypes.ConfirmNewCredentialRequest) {
|
|
||||||
const name = data.credentialName || data.rpId;
|
|
||||||
// TODO: Revert to check for user verification once user verification for passkeys is approved for production.
|
|
||||||
// PM-4577 - https://github.com/bitwarden/clients/pull/8746
|
|
||||||
await this.createNewCipher(name, data.userName);
|
|
||||||
|
|
||||||
// We are bypassing user verification pending approval.
|
|
||||||
this.send({
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
cipherId: this.cipher?.id,
|
|
||||||
type: BrowserFido2MessageTypes.ConfirmNewCredentialResponse,
|
|
||||||
userVerified: data.userVerification,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCredentialSubTitleText(messageType: string): string {
|
|
||||||
return messageType == BrowserFido2MessageTypes.ConfirmNewCredentialRequest
|
|
||||||
? "chooseCipherForPasskeySave"
|
|
||||||
: "logInWithPasskeyQuestion";
|
|
||||||
}
|
|
||||||
|
|
||||||
getCredentialButtonText(messageType: string): string {
|
|
||||||
return messageType == BrowserFido2MessageTypes.ConfirmNewCredentialRequest
|
|
||||||
? "savePasskey"
|
|
||||||
: "confirm";
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedPasskey(item: CipherView) {
|
|
||||||
this.cipher = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
viewPasskey() {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["/view-cipher"], {
|
|
||||||
queryParams: {
|
|
||||||
cipherId: this.cipher.id,
|
|
||||||
uilocation: "popout",
|
|
||||||
senderTabId: this.senderTabId,
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
singleActionPopout: `${VaultPopoutType.fido2Popout}_${this.sessionId}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addCipher() {
|
|
||||||
const data = this.message$.value;
|
|
||||||
|
|
||||||
if (data?.type !== BrowserFido2MessageTypes.ConfirmNewCredentialRequest) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["/add-cipher"], {
|
|
||||||
queryParams: {
|
|
||||||
name: data.credentialName || data.rpId,
|
|
||||||
uri: this.url,
|
|
||||||
type: CipherType.Login.toString(),
|
|
||||||
uilocation: "popout",
|
|
||||||
username: data.userName,
|
|
||||||
senderTabId: this.senderTabId,
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
userVerification: data.userVerification,
|
|
||||||
singleActionPopout: `${VaultPopoutType.fido2Popout}_${this.sessionId}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async search() {
|
|
||||||
this.hasSearched = await this.searchService.isSearchable(this.searchText);
|
|
||||||
this.searchPending = true;
|
|
||||||
if (this.hasSearched) {
|
|
||||||
this.displayedCiphers = await this.searchService.searchCiphers(
|
|
||||||
this.searchText,
|
|
||||||
null,
|
|
||||||
this.ciphers,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const equivalentDomains = await firstValueFrom(
|
|
||||||
this.domainSettingsService.getUrlEquivalentDomains(this.url),
|
|
||||||
);
|
|
||||||
this.displayedCiphers = this.ciphers.filter((cipher) =>
|
|
||||||
cipher.login.matchesUri(this.url, equivalentDomains),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.searchPending = false;
|
|
||||||
this.selectedPasskey(this.displayedCiphers[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
abort(fallback: boolean) {
|
|
||||||
this.unload(fallback);
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
unload(fallback = false) {
|
|
||||||
this.send({
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
type: BrowserFido2MessageTypes.AbortResponse,
|
|
||||||
fallbackRequested: fallback,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildCipher(name: string, username: string) {
|
|
||||||
this.cipher = new CipherView();
|
|
||||||
this.cipher.name = name;
|
|
||||||
|
|
||||||
this.cipher.type = CipherType.Login;
|
|
||||||
this.cipher.login = new LoginView();
|
|
||||||
this.cipher.login.username = username;
|
|
||||||
this.cipher.login.uris = [new LoginUriView()];
|
|
||||||
this.cipher.login.uris[0].uri = this.url;
|
|
||||||
this.cipher.card = new CardView();
|
|
||||||
this.cipher.identity = new IdentityView();
|
|
||||||
this.cipher.secureNote = new SecureNoteView();
|
|
||||||
this.cipher.secureNote.type = SecureNoteType.Generic;
|
|
||||||
this.cipher.reprompt = CipherRepromptType.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createNewCipher(name: string, username: string) {
|
|
||||||
const activeUserId = await firstValueFrom(
|
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.buildCipher(name, username);
|
|
||||||
const cipher = await this.cipherService.encrypt(this.cipher, activeUserId);
|
|
||||||
try {
|
|
||||||
await this.cipherService.createWithServer(cipher);
|
|
||||||
this.cipher.id = cipher.id;
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove and use fido2 user verification service once user verification for passkeys is approved for production.
|
|
||||||
private async handleUserVerification(
|
|
||||||
userVerificationRequested: boolean,
|
|
||||||
cipher: CipherView,
|
|
||||||
): Promise<boolean> {
|
|
||||||
const masterPasswordRepromptRequired = cipher && cipher.reprompt !== 0;
|
|
||||||
|
|
||||||
if (masterPasswordRepromptRequired) {
|
|
||||||
return await this.passwordRepromptService.showPasswordPrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
return userVerificationRequested;
|
|
||||||
}
|
|
||||||
|
|
||||||
private send(msg: BrowserFido2Message) {
|
|
||||||
BrowserFido2UserInterfaceSession.sendMessage({
|
|
||||||
sessionId: this.sessionId,
|
|
||||||
...msg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This methods returns true if a cipher either has no passkeys, or has a passkey matching with userHandle
|
|
||||||
* @param userHandle
|
|
||||||
*/
|
|
||||||
private hasNoOtherPasskeys(cipher: CipherView, userHandle: string): boolean {
|
|
||||||
if (cipher.login.fido2Credentials == null || cipher.login.fido2Credentials.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cipher.login.fido2Credentials.some((passkey) => passkey.userHandle === userHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
<header>
|
|
||||||
<div class="left">
|
|
||||||
<button type="button" routerLink="/tabs/settings">
|
|
||||||
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
|
|
||||||
<span>{{ "back" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<h1 class="center">
|
|
||||||
<span class="title">{{ "autofill" | i18n }}</span>
|
|
||||||
</h1>
|
|
||||||
<div class="right"></div>
|
|
||||||
</header>
|
|
||||||
<main tabindex="-1">
|
|
||||||
<div class="box tw-mt-4">
|
|
||||||
<div class="box-content">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="box-content-row box-content-row-link box-content-row-flex"
|
|
||||||
(click)="commandSettings()"
|
|
||||||
>
|
|
||||||
<div class="row-main">{{ "autofillShortcut" | i18n }}</div>
|
|
||||||
<i class="bwi bwi-external-link bwi-lg bwi-fw" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="autofillKeyboardHelp" class="box-footer">
|
|
||||||
{{ autofillKeyboardHelperText }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row" appBoxRow>
|
|
||||||
<label for="autofill-overlay-settings">{{ "showAutoFillMenuOnFormFields" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="autofill-overlay-settings"
|
|
||||||
name="autofill-overlay-settings"
|
|
||||||
[(ngModel)]="autoFillOverlayVisibility"
|
|
||||||
(change)="updateAutoFillOverlayVisibility()"
|
|
||||||
>
|
|
||||||
<option *ngFor="let o of autoFillOverlayVisibilityOptions" [ngValue]="o.value">
|
|
||||||
{{ o.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="box-footer" *ngIf="accountSwitcherEnabled || !canOverrideBrowserAutofillSetting">
|
|
||||||
<span *ngIf="accountSwitcherEnabled">{{ "showInlineMenuOnFormFieldsDescAlt" | i18n }}</span>
|
|
||||||
<span *ngIf="!canOverrideBrowserAutofillSetting">
|
|
||||||
{{ "turnOffBrowserBuiltInPasswordManagerSettings" | i18n }}
|
|
||||||
<a
|
|
||||||
[attr.href]="disablePasswordManagerLink"
|
|
||||||
(click)="openDisablePasswordManagerLink($event)"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{{ "turnOffBrowserBuiltInPasswordManagerSettingsLink" | i18n }}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box tw-mb-5" *ngIf="inlineMenuPositioningImprovementsEnabled && inlineMenuIsEnabled">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="show-inline-menu-identities" class="!tw-mr-0">{{
|
|
||||||
"showInlineMenuIdentitiesLabel" | i18n
|
|
||||||
}}</label>
|
|
||||||
<input
|
|
||||||
id="show-inline-menu-identities"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="updateShowInlineMenuIdentities()"
|
|
||||||
[(ngModel)]="showInlineMenuIdentities"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="show-inline-menu-cards" class="!tw-mr-0">{{
|
|
||||||
"showInlineMenuCardsLabel" | i18n
|
|
||||||
}}</label>
|
|
||||||
<input
|
|
||||||
id="show-inline-menu-cards"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="updateShowInlineMenuCards()"
|
|
||||||
[(ngModel)]="showInlineMenuCards"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content" *ngIf="canOverrideBrowserAutofillSetting">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="overrideBrowserAutofill" class="!tw-mr-0">{{
|
|
||||||
"overrideDefaultBrowserAutoFillSettings" | i18n
|
|
||||||
}}</label>
|
|
||||||
<input
|
|
||||||
id="overrideBrowserAutofill"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="updateDefaultBrowserAutofillDisabled()"
|
|
||||||
[(ngModel)]="defaultBrowserAutofillDisabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box-footer" *ngIf="canOverrideBrowserAutofillSetting">
|
|
||||||
<span *ngIf="accountSwitcherEnabled">{{ "showInlineMenuOnFormFieldsDescAlt" | i18n }}</span>
|
|
||||||
{{ "turnOffBrowserBuiltInPasswordManagerSettings" | i18n }}
|
|
||||||
<a
|
|
||||||
[attr.href]="disablePasswordManagerLink"
|
|
||||||
(click)="openDisablePasswordManagerLink($event)"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{{ "turnOffBrowserBuiltInPasswordManagerSettingsLink" | i18n }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box tw-mt-4">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="autofill">{{ "enableAutoFillOnPageLoad" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="autofill"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="autofillHelp"
|
|
||||||
(change)="updateAutoFillOnPageLoad()"
|
|
||||||
[(ngModel)]="enableAutoFillOnPageLoad"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="autofillHelp" class="box-footer">
|
|
||||||
{{ "enableAutoFillOnPageLoadDesc" | i18n }}
|
|
||||||
<b>{{ "warning" | i18n }}</b
|
|
||||||
>: {{ "experimentalFeature" | i18n }}
|
|
||||||
<a href="https://bitwarden.com/help/auto-fill-browser/" target="_blank" rel="noreferrer">
|
|
||||||
{{ "learnMoreAboutAutofill" | i18n }}.
|
|
||||||
<i
|
|
||||||
[attr.aria-label]="'opensInANewWindow' | i18n"
|
|
||||||
class="bwi bwi-external-link bwi-sm bwi-fw"
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row" appBoxRow>
|
|
||||||
<label for="defaultAutofill">{{ "defaultAutoFillOnPageLoad" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="defaultAutofill"
|
|
||||||
name="DefaultAutofill"
|
|
||||||
aria-describedby="defaultAutofillHelp"
|
|
||||||
[(ngModel)]="autoFillOnPageLoadDefault"
|
|
||||||
(change)="updateAutoFillOnPageLoadDefault()"
|
|
||||||
[disabled]="!enableAutoFillOnPageLoad"
|
|
||||||
>
|
|
||||||
<option *ngFor="let o of autoFillOnPageLoadOptions" [ngValue]="o.value">
|
|
||||||
{{ o.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="defaultAutofillHelp" class="box-footer">
|
|
||||||
{{ "defaultAutoFillOnPageLoadDesc" | i18n }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<h2 class="box-header">{{ "additionalOptions" | i18n }}</h2>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="context-menu">{{ "enableContextMenuItem" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="context-menu"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="context-menuHelp"
|
|
||||||
(change)="updateContextMenuItem()"
|
|
||||||
[(ngModel)]="enableContextMenuItem"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="context-menuHelp" class="box-footer">
|
|
||||||
{{
|
|
||||||
accountSwitcherEnabled ? ("contextMenuItemDescAlt" | i18n) : ("contextMenuItemDesc" | i18n)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="totp">{{ "enableAutoTotpCopy" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="totp"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="totpHelp"
|
|
||||||
(change)="updateAutoTotpCopy()"
|
|
||||||
[(ngModel)]="enableAutoTotpCopy"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="totpHelp" class="box-footer">{{ "disableAutoTotpCopyDesc" | i18n }}</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row" appBoxRow>
|
|
||||||
<label for="clearClipboard">{{ "clearClipboard" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="clearClipboard"
|
|
||||||
name="ClearClipboard"
|
|
||||||
aria-describedby="clearClipboardHelp"
|
|
||||||
[(ngModel)]="clearClipboard"
|
|
||||||
(change)="saveClearClipboard()"
|
|
||||||
>
|
|
||||||
<option *ngFor="let o of clearClipboardOptions" [ngValue]="o.value">
|
|
||||||
{{ o.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="clearClipboardHelp" class="box-footer">{{ "clearClipboardDesc" | i18n }}</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row" appBoxRow>
|
|
||||||
<label for="defaultUriMatch">{{ "defaultUriMatchDetection" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="defaultUriMatch"
|
|
||||||
name="DefaultUriMatch"
|
|
||||||
aria-describedby="defaultUriMatchHelp"
|
|
||||||
[(ngModel)]="defaultUriMatch"
|
|
||||||
(change)="saveDefaultUriMatch()"
|
|
||||||
>
|
|
||||||
<option *ngFor="let o of uriMatchOptions" [ngValue]="o.value">{{ o.name }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="defaultUriMatchHelp" class="box-footer">
|
|
||||||
{{ "defaultUriMatchDetectionDesc" | i18n }}
|
|
||||||
</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="showCardsCurrentTab">{{ "showCardsCurrentTab" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="showCardsCurrentTab"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="showCardsCurrentTabHelp"
|
|
||||||
(change)="updateShowCardsCurrentTab()"
|
|
||||||
[(ngModel)]="showCardsCurrentTab"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="showCardsCurrentTabHelp" class="box-footer">
|
|
||||||
{{ "showCardsCurrentTabDesc" | i18n }}
|
|
||||||
</div>
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="showIdentitiesCurrentTab">{{ "showIdentitiesCurrentTab" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="showIdentitiesCurrentTab"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="showIdentitiesCurrentTabHelp"
|
|
||||||
(change)="updateShowIdentitiesCurrentTab()"
|
|
||||||
[(ngModel)]="showIdentitiesCurrentTab"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="showIdentitiesCurrentTabHelp" class="box-footer">
|
|
||||||
{{ "showIdentitiesCurrentTabDesc" | i18n }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box list" *ngIf="blockBrowserInjectionsByDomainEnabled">
|
|
||||||
<div class="box-content single-line">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="box-content-row box-content-row-flex text-default"
|
|
||||||
routerLink="/blocked-domains"
|
|
||||||
>
|
|
||||||
<div class="row-main">{{ "blockedDomains" | i18n }}</div>
|
|
||||||
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
@@ -1,344 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
|
|
||||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
|
||||||
import {
|
|
||||||
InlineMenuVisibilitySetting,
|
|
||||||
ClearClipboardDelaySetting,
|
|
||||||
} from "@bitwarden/common/autofill/types";
|
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import {
|
|
||||||
UriMatchStrategy,
|
|
||||||
UriMatchStrategySetting,
|
|
||||||
} from "@bitwarden/common/models/domain/domain-service";
|
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
|
||||||
|
|
||||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
|
||||||
import { enableAccountSwitching } from "../../../platform/flags";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-autofill-v1",
|
|
||||||
templateUrl: "autofill-v1.component.html",
|
|
||||||
})
|
|
||||||
export class AutofillV1Component implements OnInit {
|
|
||||||
protected canOverrideBrowserAutofillSetting = false;
|
|
||||||
protected defaultBrowserAutofillDisabled = false;
|
|
||||||
protected autoFillOverlayVisibility: InlineMenuVisibilitySetting;
|
|
||||||
protected autoFillOverlayVisibilityOptions: any[];
|
|
||||||
protected disablePasswordManagerLink: string;
|
|
||||||
protected inlineMenuPositioningImprovementsEnabled: boolean = false;
|
|
||||||
protected blockBrowserInjectionsByDomainEnabled: boolean = false;
|
|
||||||
protected showInlineMenuIdentities: boolean = true;
|
|
||||||
protected showInlineMenuCards: boolean = true;
|
|
||||||
inlineMenuIsEnabled: boolean = false;
|
|
||||||
enableAutoFillOnPageLoad = false;
|
|
||||||
autoFillOnPageLoadDefault = false;
|
|
||||||
autoFillOnPageLoadOptions: any[];
|
|
||||||
enableContextMenuItem = false;
|
|
||||||
enableAutoTotpCopy = false; // TODO: Does it matter if this is set to false or true?
|
|
||||||
clearClipboard: ClearClipboardDelaySetting;
|
|
||||||
clearClipboardOptions: any[];
|
|
||||||
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
|
|
||||||
uriMatchOptions: any[];
|
|
||||||
showCardsCurrentTab = false;
|
|
||||||
showIdentitiesCurrentTab = false;
|
|
||||||
autofillKeyboardHelperText: string;
|
|
||||||
accountSwitcherEnabled = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private domainSettingsService: DomainSettingsService,
|
|
||||||
private configService: ConfigService,
|
|
||||||
private dialogService: DialogService,
|
|
||||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
|
||||||
private messagingService: MessagingService,
|
|
||||||
private vaultSettingsService: VaultSettingsService,
|
|
||||||
) {
|
|
||||||
this.autoFillOverlayVisibilityOptions = [
|
|
||||||
{
|
|
||||||
name: i18nService.t("autofillOverlayVisibilityOff"),
|
|
||||||
value: AutofillOverlayVisibility.Off,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18nService.t("autofillOverlayVisibilityOnFieldFocus"),
|
|
||||||
value: AutofillOverlayVisibility.OnFieldFocus,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: i18nService.t("autofillOverlayVisibilityOnButtonClick"),
|
|
||||||
value: AutofillOverlayVisibility.OnButtonClick,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
this.autoFillOnPageLoadOptions = [
|
|
||||||
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
|
|
||||||
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
|
|
||||||
];
|
|
||||||
this.clearClipboardOptions = [
|
|
||||||
{ name: i18nService.t("never"), value: null },
|
|
||||||
{ name: i18nService.t("tenSeconds"), value: 10 },
|
|
||||||
{ name: i18nService.t("twentySeconds"), value: 20 },
|
|
||||||
{ name: i18nService.t("thirtySeconds"), value: 30 },
|
|
||||||
{ name: i18nService.t("oneMinute"), value: 60 },
|
|
||||||
{ name: i18nService.t("twoMinutes"), value: 120 },
|
|
||||||
{ name: i18nService.t("fiveMinutes"), value: 300 },
|
|
||||||
];
|
|
||||||
this.uriMatchOptions = [
|
|
||||||
{ name: i18nService.t("baseDomainOptionRecommended"), value: UriMatchStrategy.Domain },
|
|
||||||
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
|
|
||||||
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
|
|
||||||
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
|
|
||||||
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
|
|
||||||
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
|
|
||||||
];
|
|
||||||
|
|
||||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
|
||||||
this.disablePasswordManagerLink = this.getDisablePasswordManagerLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.canOverrideBrowserAutofillSetting =
|
|
||||||
this.platformUtilsService.isChrome() ||
|
|
||||||
this.platformUtilsService.isEdge() ||
|
|
||||||
this.platformUtilsService.isOpera() ||
|
|
||||||
this.platformUtilsService.isVivaldi();
|
|
||||||
|
|
||||||
this.defaultBrowserAutofillDisabled = await this.browserAutofillSettingCurrentlyOverridden();
|
|
||||||
|
|
||||||
this.autoFillOverlayVisibility = await firstValueFrom(
|
|
||||||
this.autofillSettingsService.inlineMenuVisibility$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.inlineMenuPositioningImprovementsEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.InlineMenuPositioningImprovements,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.blockBrowserInjectionsByDomainEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.BlockBrowserInjectionsByDomain,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.inlineMenuIsEnabled = this.isInlineMenuEnabled();
|
|
||||||
|
|
||||||
this.showInlineMenuIdentities =
|
|
||||||
this.inlineMenuPositioningImprovementsEnabled &&
|
|
||||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuIdentities$));
|
|
||||||
|
|
||||||
this.showInlineMenuCards =
|
|
||||||
this.inlineMenuPositioningImprovementsEnabled &&
|
|
||||||
(await firstValueFrom(this.autofillSettingsService.showInlineMenuCards$));
|
|
||||||
|
|
||||||
this.enableAutoFillOnPageLoad = await firstValueFrom(
|
|
||||||
this.autofillSettingsService.autofillOnPageLoad$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.autoFillOnPageLoadDefault = await firstValueFrom(
|
|
||||||
this.autofillSettingsService.autofillOnPageLoadDefault$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.enableContextMenuItem = await firstValueFrom(
|
|
||||||
this.autofillSettingsService.enableContextMenu$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
|
|
||||||
|
|
||||||
this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$);
|
|
||||||
|
|
||||||
const defaultUriMatch = await firstValueFrom(
|
|
||||||
this.domainSettingsService.defaultUriMatchStrategy$,
|
|
||||||
);
|
|
||||||
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
|
|
||||||
|
|
||||||
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
|
|
||||||
await this.setAutofillKeyboardHelperText(command);
|
|
||||||
|
|
||||||
this.showCardsCurrentTab = await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$);
|
|
||||||
|
|
||||||
this.showIdentitiesCurrentTab = await firstValueFrom(
|
|
||||||
this.vaultSettingsService.showIdentitiesCurrentTab$,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
isInlineMenuEnabled() {
|
|
||||||
return (
|
|
||||||
this.autoFillOverlayVisibility === AutofillOverlayVisibility.OnFieldFocus ||
|
|
||||||
this.autoFillOverlayVisibility === AutofillOverlayVisibility.OnButtonClick
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoFillOverlayVisibility() {
|
|
||||||
await this.autofillSettingsService.setInlineMenuVisibility(this.autoFillOverlayVisibility);
|
|
||||||
await this.requestPrivacyPermission();
|
|
||||||
|
|
||||||
this.inlineMenuIsEnabled = this.isInlineMenuEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoFillOnPageLoad() {
|
|
||||||
await this.autofillSettingsService.setAutofillOnPageLoad(this.enableAutoFillOnPageLoad);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoFillOnPageLoadDefault() {
|
|
||||||
await this.autofillSettingsService.setAutofillOnPageLoadDefault(this.autoFillOnPageLoadDefault);
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveDefaultUriMatch() {
|
|
||||||
await this.domainSettingsService.setDefaultUriMatchStrategy(this.defaultUriMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setAutofillKeyboardHelperText(command: string) {
|
|
||||||
if (command) {
|
|
||||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutText", command);
|
|
||||||
} else {
|
|
||||||
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutNotSet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async commandSettings() {
|
|
||||||
if (this.platformUtilsService.isChrome()) {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab("chrome://extensions/shortcuts");
|
|
||||||
} else if (this.platformUtilsService.isOpera()) {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab("opera://extensions/shortcuts");
|
|
||||||
} else if (this.platformUtilsService.isEdge()) {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab("edge://extensions/shortcuts");
|
|
||||||
} else if (this.platformUtilsService.isVivaldi()) {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab("vivaldi://extensions/shortcuts");
|
|
||||||
} else {
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab("https://bitwarden.com/help/keyboard-shortcuts");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDisablePasswordManagerLink(): string {
|
|
||||||
if (this.platformUtilsService.isChrome()) {
|
|
||||||
return "chrome://settings/autofill";
|
|
||||||
}
|
|
||||||
if (this.platformUtilsService.isOpera()) {
|
|
||||||
return "opera://settings/autofill";
|
|
||||||
}
|
|
||||||
if (this.platformUtilsService.isEdge()) {
|
|
||||||
return "edge://settings/passwords";
|
|
||||||
}
|
|
||||||
if (this.platformUtilsService.isVivaldi()) {
|
|
||||||
return "vivaldi://settings/autofill";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "https://bitwarden.com/help/disable-browser-autofill/";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected openDisablePasswordManagerLink(event: Event) {
|
|
||||||
event.preventDefault();
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
BrowserApi.createNewTab(this.disablePasswordManagerLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
async requestPrivacyPermission() {
|
|
||||||
if (
|
|
||||||
this.autoFillOverlayVisibility === AutofillOverlayVisibility.Off ||
|
|
||||||
!this.canOverrideBrowserAutofillSetting ||
|
|
||||||
(await this.browserAutofillSettingCurrentlyOverridden())
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.dialogService.openSimpleDialog({
|
|
||||||
title: { key: "overrideDefaultBrowserAutofillTitle" },
|
|
||||||
content: { key: "overrideDefaultBrowserAutofillDescription" },
|
|
||||||
acceptButtonText: { key: "makeDefault" },
|
|
||||||
acceptAction: async () => await this.handleOverrideDialogAccept(),
|
|
||||||
cancelButtonText: { key: "ignore" },
|
|
||||||
type: "info",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDefaultBrowserAutofillDisabled() {
|
|
||||||
const privacyPermissionGranted = await this.privacyPermissionGranted();
|
|
||||||
if (!this.defaultBrowserAutofillDisabled && !privacyPermissionGranted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!privacyPermissionGranted &&
|
|
||||||
!(await BrowserApi.requestPermission({ permissions: ["privacy"] }))
|
|
||||||
) {
|
|
||||||
await this.dialogService.openSimpleDialog({
|
|
||||||
title: { key: "privacyPermissionAdditionNotGrantedTitle" },
|
|
||||||
content: { key: "privacyPermissionAdditionNotGrantedDescription" },
|
|
||||||
acceptButtonText: { key: "ok" },
|
|
||||||
cancelButtonText: null,
|
|
||||||
type: "warning",
|
|
||||||
});
|
|
||||||
this.defaultBrowserAutofillDisabled = false;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowserApi.updateDefaultBrowserAutofillSettings(!this.defaultBrowserAutofillDisabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleOverrideDialogAccept = async () => {
|
|
||||||
this.defaultBrowserAutofillDisabled = true;
|
|
||||||
await this.updateDefaultBrowserAutofillDisabled();
|
|
||||||
};
|
|
||||||
|
|
||||||
async browserAutofillSettingCurrentlyOverridden() {
|
|
||||||
if (!this.canOverrideBrowserAutofillSetting) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(await this.privacyPermissionGranted())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await BrowserApi.browserAutofillSettingsOverridden();
|
|
||||||
}
|
|
||||||
|
|
||||||
async privacyPermissionGranted(): Promise<boolean> {
|
|
||||||
return await BrowserApi.permissionsGranted(["privacy"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateContextMenuItem() {
|
|
||||||
await this.autofillSettingsService.setEnableContextMenu(this.enableContextMenuItem);
|
|
||||||
this.messagingService.send("bgUpdateContextMenu");
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAutoTotpCopy() {
|
|
||||||
await this.autofillSettingsService.setAutoCopyTotp(this.enableAutoTotpCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveClearClipboard() {
|
|
||||||
await this.autofillSettingsService.setClearClipboardDelay(this.clearClipboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateShowCardsCurrentTab() {
|
|
||||||
await this.vaultSettingsService.setShowCardsCurrentTab(this.showCardsCurrentTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateShowIdentitiesCurrentTab() {
|
|
||||||
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateShowInlineMenuCards() {
|
|
||||||
await this.autofillSettingsService.setShowInlineMenuCards(this.showInlineMenuCards);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateShowInlineMenuIdentities() {
|
|
||||||
await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
<form #form (ngSubmit)="submit()">
|
|
||||||
<header>
|
|
||||||
<div class="left">
|
|
||||||
<button type="button" routerLink="/notifications">
|
|
||||||
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
|
|
||||||
<span>{{ "back" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<h1 class="center">
|
|
||||||
<span class="title">{{ "excludedDomains" | i18n }}</span>
|
|
||||||
</h1>
|
|
||||||
<div class="right">
|
|
||||||
<button type="submit">{{ "save" | i18n }}</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main tabindex="-1">
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-footer" [ngStyle]="{ marginTop: '10px' }">
|
|
||||||
{{
|
|
||||||
accountSwitcherEnabled
|
|
||||||
? ("excludedDomainsDescAlt" | i18n)
|
|
||||||
: ("excludedDomainsDesc" | i18n)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<ng-container *ngIf="excludedDomains">
|
|
||||||
<div
|
|
||||||
class="box-content-row box-content-row-multi"
|
|
||||||
appBoxRow
|
|
||||||
*ngFor="let domain of excludedDomains; let i = index; trackBy: trackByFunction"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
appStopClick
|
|
||||||
(click)="removeUri(i)"
|
|
||||||
appA11yTitle="{{ 'remove' | i18n }}"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-minus-circle bwi-lg" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<div class="row-main">
|
|
||||||
<label for="excludedDomain{{ i }}">{{ "uriPosition" | i18n: i + 1 }}</label>
|
|
||||||
<input
|
|
||||||
id="excludedDomain{{ i }}"
|
|
||||||
name="excludedDomain{{ i }}"
|
|
||||||
type="text"
|
|
||||||
[(ngModel)]="domain.uri"
|
|
||||||
placeholder="{{ 'ex' | i18n }} https://google.com"
|
|
||||||
inputmode="url"
|
|
||||||
appInputVerbatim
|
|
||||||
/>
|
|
||||||
<label for="currentUris{{ i }}" class="sr-only">
|
|
||||||
{{ "currentUri" | i18n }} {{ i + 1 }}
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
*ngIf="currentUris && currentUris.length"
|
|
||||||
id="currentUris{{ i }}"
|
|
||||||
name="currentUris{{ i }}"
|
|
||||||
[(ngModel)]="domain.uri"
|
|
||||||
[hidden]="!domain.showCurrentUris"
|
|
||||||
>
|
|
||||||
<option [ngValue]="null">-- {{ "select" | i18n }} --</option>
|
|
||||||
<option *ngFor="let u of currentUris" [ngValue]="u">{{ u }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="action-buttons">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
*ngIf="currentUris && currentUris.length"
|
|
||||||
class="row-btn"
|
|
||||||
appStopClick
|
|
||||||
appA11yTitle="{{ 'toggleCurrentUris' | i18n }}"
|
|
||||||
(click)="toggleUriInput(domain)"
|
|
||||||
[attr.aria-pressed]="domain.showCurrentUris === true"
|
|
||||||
>
|
|
||||||
<i aria-hidden="true" class="bwi bwi-lg bwi-list"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
appStopClick
|
|
||||||
(click)="addUri()"
|
|
||||||
class="box-content-row box-content-row-newmulti single-line"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-plus-circle bwi-fw bwi-lg" aria-hidden="true"></i> {{ "newUri" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</form>
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
|
||||||
import { Router } from "@angular/router";
|
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
|
||||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
||||||
|
|
||||||
import { BrowserApi } from "../../../platform/browser/browser-api";
|
|
||||||
import { enableAccountSwitching } from "../../../platform/flags";
|
|
||||||
|
|
||||||
interface ExcludedDomain {
|
|
||||||
uri: string;
|
|
||||||
showCurrentUris: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "excludedDomains";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-excluded-domains-v1",
|
|
||||||
templateUrl: "excluded-domains-v1.component.html",
|
|
||||||
})
|
|
||||||
export class ExcludedDomainsV1Component implements OnInit, OnDestroy {
|
|
||||||
excludedDomains: ExcludedDomain[] = [];
|
|
||||||
existingExcludedDomains: ExcludedDomain[] = [];
|
|
||||||
currentUris: string[];
|
|
||||||
loadCurrentUrisTimeout: number;
|
|
||||||
accountSwitcherEnabled = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private domainSettingsService: DomainSettingsService,
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private router: Router,
|
|
||||||
private broadcasterService: BroadcasterService,
|
|
||||||
private ngZone: NgZone,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
) {
|
|
||||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
const savedDomains = await firstValueFrom(this.domainSettingsService.neverDomains$);
|
|
||||||
if (savedDomains) {
|
|
||||||
for (const uri of Object.keys(savedDomains)) {
|
|
||||||
this.excludedDomains.push({ uri: uri, showCurrentUris: false });
|
|
||||||
this.existingExcludedDomains.push({ uri: uri, showCurrentUris: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.loadCurrentUris();
|
|
||||||
|
|
||||||
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.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.ngZone.run(async () => {
|
|
||||||
switch (message.command) {
|
|
||||||
case "tabChanged":
|
|
||||||
case "windowChanged":
|
|
||||||
if (this.loadCurrentUrisTimeout != null) {
|
|
||||||
window.clearTimeout(this.loadCurrentUrisTimeout);
|
|
||||||
}
|
|
||||||
this.loadCurrentUrisTimeout = window.setTimeout(
|
|
||||||
async () => await this.loadCurrentUris(),
|
|
||||||
500,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async addUri() {
|
|
||||||
this.excludedDomains.push({ uri: "", showCurrentUris: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeUri(i: number) {
|
|
||||||
this.excludedDomains.splice(i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
async submit() {
|
|
||||||
const savedDomains: { [name: string]: null } = {};
|
|
||||||
const newExcludedDomains = this.getNewlyAddedDomains(this.excludedDomains);
|
|
||||||
for (const domain of this.excludedDomains) {
|
|
||||||
const resp = newExcludedDomains.filter((e) => e.uri === domain.uri);
|
|
||||||
if (resp.length === 0) {
|
|
||||||
savedDomains[domain.uri] = null;
|
|
||||||
} else {
|
|
||||||
if (domain.uri && domain.uri !== "") {
|
|
||||||
const validDomain = Utils.getHostname(domain.uri);
|
|
||||||
if (!validDomain) {
|
|
||||||
this.platformUtilsService.showToast(
|
|
||||||
"error",
|
|
||||||
null,
|
|
||||||
this.i18nService.t("excludedDomainsInvalidDomain", domain.uri),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
savedDomains[validDomain] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.domainSettingsService.setNeverDomains(savedDomains);
|
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.router.navigate(["/tabs/settings"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
trackByFunction(index: number, item: any) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
getNewlyAddedDomains(domain: ExcludedDomain[]): ExcludedDomain[] {
|
|
||||||
const result = this.excludedDomains.filter(
|
|
||||||
(newDomain) =>
|
|
||||||
!this.existingExcludedDomains.some((oldDomain) => newDomain.uri === oldDomain.uri),
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleUriInput(domain: ExcludedDomain) {
|
|
||||||
domain.showCurrentUris = !domain.showCurrentUris;
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadCurrentUris() {
|
|
||||||
const tabs = await BrowserApi.tabsQuery({ windowType: "normal" });
|
|
||||||
if (tabs) {
|
|
||||||
const uriSet = new Set(tabs.map((tab) => Utils.getHostname(tab.url)));
|
|
||||||
uriSet.delete(null);
|
|
||||||
this.currentUris = Array.from(uriSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
<header>
|
|
||||||
<div class="left">
|
|
||||||
<button type="button" routerLink="/tabs/settings">
|
|
||||||
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
|
|
||||||
<span>{{ "back" | i18n }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<h1 class="center">
|
|
||||||
<span class="title">{{ "notifications" | i18n }}</span>
|
|
||||||
</h1>
|
|
||||||
<div class="right">
|
|
||||||
<app-pop-out></app-pop-out>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<main tabindex="-1">
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="use-passkeys">{{ "enableUsePasskeys" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="use-passkeys"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="use-passkeysHelp"
|
|
||||||
(change)="updateEnablePasskeys()"
|
|
||||||
[(ngModel)]="enablePasskeys"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="use-passkeysHelp" class="box-footer">
|
|
||||||
{{ "usePasskeysDesc" | i18n }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="addlogin-notification-bar">{{ "enableAddLoginNotification" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="addlogin-notification-bar"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="addlogin-notification-barHelp"
|
|
||||||
(change)="updateAddLoginNotification()"
|
|
||||||
[(ngModel)]="enableAddLoginNotification"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="addlogin-notification-barHelp" class="box-footer">
|
|
||||||
{{
|
|
||||||
accountSwitcherEnabled
|
|
||||||
? ("addLoginNotificationDescAlt" | i18n)
|
|
||||||
: ("addLoginNotificationDesc" | i18n)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="changedpass-notification-bar">{{
|
|
||||||
"enableChangedPasswordNotification" | i18n
|
|
||||||
}}</label>
|
|
||||||
<input
|
|
||||||
id="changedpass-notification-bar"
|
|
||||||
type="checkbox"
|
|
||||||
aria-describedby="changedpass-notification-barHelp"
|
|
||||||
(change)="updateChangedPasswordNotification()"
|
|
||||||
[(ngModel)]="enableChangedPasswordNotification"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="changedpass-notification-barHelp" class="box-footer">
|
|
||||||
{{
|
|
||||||
accountSwitcherEnabled
|
|
||||||
? ("changedPasswordNotificationDescAlt" | i18n)
|
|
||||||
: ("changedPasswordNotificationDesc" | i18n)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box list">
|
|
||||||
<div class="box-content single-line">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="box-content-row box-content-row-flex text-default"
|
|
||||||
routerLink="/excluded-domains"
|
|
||||||
>
|
|
||||||
<div class="row-main">{{ "excludedDomains" | i18n }}</div>
|
|
||||||
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
|
||||||
|
|
||||||
import { enableAccountSwitching } from "../../../platform/flags";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "autofill-notification-v1-settings",
|
|
||||||
templateUrl: "notifications-v1.component.html",
|
|
||||||
})
|
|
||||||
export class NotificationsSettingsV1Component implements OnInit {
|
|
||||||
enableAddLoginNotification = false;
|
|
||||||
enableChangedPasswordNotification = false;
|
|
||||||
enablePasskeys = true;
|
|
||||||
accountSwitcherEnabled = false;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
|
||||||
private vaultSettingsService: VaultSettingsService,
|
|
||||||
) {
|
|
||||||
this.accountSwitcherEnabled = enableAccountSwitching();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.enableAddLoginNotification = await firstValueFrom(
|
|
||||||
this.userNotificationSettingsService.enableAddedLoginPrompt$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.enableChangedPasswordNotification = await firstValueFrom(
|
|
||||||
this.userNotificationSettingsService.enableChangedPasswordPrompt$,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.enablePasskeys = await firstValueFrom(this.vaultSettingsService.enablePasskeys$);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateAddLoginNotification() {
|
|
||||||
await this.userNotificationSettingsService.setEnableAddedLoginPrompt(
|
|
||||||
this.enableAddLoginNotification,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateChangedPasswordNotification() {
|
|
||||||
await this.userNotificationSettingsService.setEnableChangedPasswordPrompt(
|
|
||||||
this.enableChangedPasswordNotification,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateEnablePasskeys() {
|
|
||||||
await this.vaultSettingsService.setEnablePasskeys(this.enablePasskeys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -259,9 +259,7 @@ export default class RuntimeBackground {
|
|||||||
await this.main.refreshBadge();
|
await this.main.refreshBadge();
|
||||||
await this.main.refreshMenu(false);
|
await this.main.refreshMenu(false);
|
||||||
|
|
||||||
if (await this.configService.getFeatureFlag(FeatureFlag.ExtensionRefresh)) {
|
await this.autofillService.setAutoFillOnPageLoadOrgPolicy();
|
||||||
await this.autofillService.setAutoFillOnPageLoadOrgPolicy();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "addToLockedVaultPendingNotifications":
|
case "addToLockedVaultPendingNotifications":
|
||||||
@@ -288,9 +286,7 @@ export default class RuntimeBackground {
|
|||||||
await this.configService.ensureConfigFetched();
|
await this.configService.ensureConfigFetched();
|
||||||
await this.main.updateOverlayCiphers();
|
await this.main.updateOverlayCiphers();
|
||||||
|
|
||||||
if (await this.configService.getFeatureFlag(FeatureFlag.ExtensionRefresh)) {
|
await this.autofillService.setAutoFillOnPageLoadOrgPolicy();
|
||||||
await this.autofillService.setAutoFillOnPageLoadOrgPolicy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "openPopup":
|
case "openPopup":
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
unauthGuardFn,
|
unauthGuardFn,
|
||||||
} from "@bitwarden/angular/auth/guards";
|
} from "@bitwarden/angular/auth/guards";
|
||||||
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
|
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
|
||||||
import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap";
|
|
||||||
import { twofactorRefactorSwap } from "@bitwarden/angular/utils/two-factor-component-refactor-route-swap";
|
import { twofactorRefactorSwap } from "@bitwarden/angular/utils/two-factor-component-refactor-route-swap";
|
||||||
import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards";
|
import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards";
|
||||||
import {
|
import {
|
||||||
@@ -71,14 +70,10 @@ import { TwoFactorAuthComponent } from "../auth/popup/two-factor-auth.component"
|
|||||||
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
||||||
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
||||||
import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
|
import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
|
||||||
import { Fido2V1Component } from "../autofill/popup/fido2/fido2-v1.component";
|
|
||||||
import { Fido2Component } from "../autofill/popup/fido2/fido2.component";
|
import { Fido2Component } from "../autofill/popup/fido2/fido2.component";
|
||||||
import { AutofillV1Component } from "../autofill/popup/settings/autofill-v1.component";
|
|
||||||
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
||||||
import { BlockedDomainsComponent } from "../autofill/popup/settings/blocked-domains.component";
|
import { BlockedDomainsComponent } from "../autofill/popup/settings/blocked-domains.component";
|
||||||
import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded-domains-v1.component";
|
|
||||||
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
|
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
|
||||||
import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component";
|
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
||||||
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component";
|
||||||
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
||||||
@@ -148,11 +143,12 @@ const routes: Routes = [
|
|||||||
canActivate: [unauthGuardFn(unauthRouteOverrides), unauthUiRefreshRedirect("/login")],
|
canActivate: [unauthGuardFn(unauthRouteOverrides), unauthUiRefreshRedirect("/login")],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
},
|
},
|
||||||
...extensionRefreshSwap(Fido2V1Component, Fido2Component, {
|
{
|
||||||
path: "fido2",
|
path: "fido2",
|
||||||
|
component: Fido2Component,
|
||||||
canActivate: [fido2AuthGuard],
|
canActivate: [fido2AuthGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
}),
|
},
|
||||||
...twofactorRefactorSwap(
|
...twofactorRefactorSwap(
|
||||||
TwoFactorComponent,
|
TwoFactorComponent,
|
||||||
AnonLayoutWrapperComponent,
|
AnonLayoutWrapperComponent,
|
||||||
@@ -321,22 +317,24 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 2 } satisfies RouteDataProperties,
|
data: { elevation: 2 } satisfies RouteDataProperties,
|
||||||
},
|
},
|
||||||
...extensionRefreshSwap(AutofillV1Component, AutofillComponent, {
|
{
|
||||||
path: "autofill",
|
path: "autofill",
|
||||||
|
component: AutofillComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
}),
|
},
|
||||||
{
|
{
|
||||||
path: "account-security",
|
path: "account-security",
|
||||||
component: AccountSecurityComponent,
|
component: AccountSecurityComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
},
|
},
|
||||||
...extensionRefreshSwap(NotificationsSettingsV1Component, NotificationsSettingsComponent, {
|
{
|
||||||
path: "notifications",
|
path: "notifications",
|
||||||
|
component: NotificationsSettingsComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 1 } satisfies RouteDataProperties,
|
data: { elevation: 1 } satisfies RouteDataProperties,
|
||||||
}),
|
},
|
||||||
{
|
{
|
||||||
path: "vault-settings",
|
path: "vault-settings",
|
||||||
component: VaultSettingsV2Component,
|
component: VaultSettingsV2Component,
|
||||||
@@ -355,11 +353,12 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 2 } satisfies RouteDataProperties,
|
data: { elevation: 2 } satisfies RouteDataProperties,
|
||||||
},
|
},
|
||||||
...extensionRefreshSwap(ExcludedDomainsV1Component, ExcludedDomainsComponent, {
|
{
|
||||||
path: "excluded-domains",
|
path: "excluded-domains",
|
||||||
|
component: ExcludedDomainsComponent,
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { elevation: 2 } satisfies RouteDataProperties,
|
data: { elevation: 2 } satisfies RouteDataProperties,
|
||||||
}),
|
},
|
||||||
{
|
{
|
||||||
path: "premium",
|
path: "premium",
|
||||||
component: PremiumV2Component,
|
component: PremiumV2Component,
|
||||||
|
|||||||
@@ -35,17 +35,7 @@ import { SsoComponentV1 } from "../auth/popup/sso-v1.component";
|
|||||||
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.component";
|
||||||
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
import { TwoFactorComponent } from "../auth/popup/two-factor.component";
|
||||||
import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
|
import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component";
|
||||||
import { Fido2CipherRowV1Component } from "../autofill/popup/fido2/fido2-cipher-row-v1.component";
|
|
||||||
import { Fido2CipherRowComponent } from "../autofill/popup/fido2/fido2-cipher-row.component";
|
|
||||||
import { Fido2UseBrowserLinkV1Component } from "../autofill/popup/fido2/fido2-use-browser-link-v1.component";
|
|
||||||
import { Fido2UseBrowserLinkComponent } from "../autofill/popup/fido2/fido2-use-browser-link.component";
|
|
||||||
import { Fido2V1Component } from "../autofill/popup/fido2/fido2-v1.component";
|
|
||||||
import { Fido2Component } from "../autofill/popup/fido2/fido2.component";
|
|
||||||
import { AutofillV1Component } from "../autofill/popup/settings/autofill-v1.component";
|
|
||||||
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
||||||
import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded-domains-v1.component";
|
|
||||||
import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component";
|
|
||||||
import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component";
|
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
||||||
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
import { PopOutComponent } from "../platform/popup/components/pop-out.component";
|
||||||
import { HeaderComponent } from "../platform/popup/header.component";
|
import { HeaderComponent } from "../platform/popup/header.component";
|
||||||
@@ -87,10 +77,6 @@ import "../platform/popup/locales";
|
|||||||
ScrollingModule,
|
ScrollingModule,
|
||||||
ServicesModule,
|
ServicesModule,
|
||||||
DialogModule,
|
DialogModule,
|
||||||
ExcludedDomainsComponent,
|
|
||||||
Fido2CipherRowComponent,
|
|
||||||
Fido2Component,
|
|
||||||
Fido2UseBrowserLinkComponent,
|
|
||||||
FilePopoutCalloutComponent,
|
FilePopoutCalloutComponent,
|
||||||
AvatarModule,
|
AvatarModule,
|
||||||
AccountComponent,
|
AccountComponent,
|
||||||
@@ -112,15 +98,11 @@ import "../platform/popup/locales";
|
|||||||
ColorPasswordPipe,
|
ColorPasswordPipe,
|
||||||
ColorPasswordCountPipe,
|
ColorPasswordCountPipe,
|
||||||
EnvironmentComponent,
|
EnvironmentComponent,
|
||||||
ExcludedDomainsV1Component,
|
|
||||||
Fido2CipherRowV1Component,
|
|
||||||
Fido2UseBrowserLinkV1Component,
|
|
||||||
HintComponent,
|
HintComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
LoginViaAuthRequestComponentV1,
|
LoginViaAuthRequestComponentV1,
|
||||||
LoginComponentV1,
|
LoginComponentV1,
|
||||||
LoginDecryptionOptionsComponentV1,
|
LoginDecryptionOptionsComponentV1,
|
||||||
NotificationsSettingsV1Component,
|
|
||||||
RegisterComponent,
|
RegisterComponent,
|
||||||
SetPasswordComponent,
|
SetPasswordComponent,
|
||||||
SsoComponentV1,
|
SsoComponentV1,
|
||||||
@@ -131,8 +113,6 @@ import "../platform/popup/locales";
|
|||||||
UserVerificationComponent,
|
UserVerificationComponent,
|
||||||
VaultTimeoutInputComponent,
|
VaultTimeoutInputComponent,
|
||||||
RemovePasswordComponent,
|
RemovePasswordComponent,
|
||||||
Fido2V1Component,
|
|
||||||
AutofillV1Component,
|
|
||||||
EnvironmentSelectorComponent,
|
EnvironmentSelectorComponent,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user