mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
[PM-9959] [PM-9962] Browser Refresh - Passkey Fixes (#10299)
* [PM-9959] Expose Fido2SessionData interface * [PM-9959] Ensure cipherType is passed during passkey creation * [PM-9959] Add beforeSubmit hook to cipherForm * [PM-9959] Add support for Fido2 credential creation in add-edit-v2 * [PM-9959] Ensure cipherType defaults to CipherType.Login if none is available * [PM-9959] Add support for name and username to be passed in as query params for initial form values * [PM-9962] Hide remove passkey button when cipher has "except passwords" permissions
This commit is contained in:
parent
00f6920a86
commit
ad01a529e8
@ -18,7 +18,7 @@ import { DomainSettingsService } from "@bitwarden/common/autofill/services/domai
|
|||||||
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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { SecureNoteType, CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType, SecureNoteType } from "@bitwarden/common/vault/enums";
|
||||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||||
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@ -311,6 +311,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
|||||||
queryParams: {
|
queryParams: {
|
||||||
name: data.credentialName || data.rpId,
|
name: data.credentialName || data.rpId,
|
||||||
uri: this.url,
|
uri: this.url,
|
||||||
|
type: CipherType.Login.toString(),
|
||||||
uilocation: "popout",
|
uilocation: "popout",
|
||||||
username: data.userName,
|
username: data.userName,
|
||||||
senderTabId: this.senderTabId,
|
senderTabId: this.senderTabId,
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
*ngIf="!loading"
|
*ngIf="!loading"
|
||||||
formId="cipherForm"
|
formId="cipherForm"
|
||||||
[config]="config"
|
[config]="config"
|
||||||
(cipherSaved)="onCipherSaved()"
|
(cipherSaved)="onCipherSaved($event)"
|
||||||
|
[beforeSubmit]="checkFido2UserVerification"
|
||||||
[submitBtn]="submitBtn"
|
[submitBtn]="submitBtn"
|
||||||
>
|
>
|
||||||
<app-open-attachments
|
<app-open-attachments
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { CommonModule, Location } from "@angular/common";
|
import { CommonModule, Location } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { FormsModule } from "@angular/forms";
|
import { FormsModule } from "@angular/forms";
|
||||||
import { ActivatedRoute, Params, Router } from "@angular/router";
|
import { ActivatedRoute, Params, Router } from "@angular/router";
|
||||||
import { map, switchMap } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { AsyncActionsModule, ButtonModule, SearchModule } from "@bitwarden/components";
|
import { AsyncActionsModule, ButtonModule, SearchModule } from "@bitwarden/components";
|
||||||
import {
|
import {
|
||||||
CipherFormConfig,
|
CipherFormConfig,
|
||||||
@ -19,10 +20,18 @@ import {
|
|||||||
TotpCaptureService,
|
TotpCaptureService,
|
||||||
} from "@bitwarden/vault";
|
} from "@bitwarden/vault";
|
||||||
|
|
||||||
|
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
|
||||||
import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup-footer.component";
|
import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup-footer.component";
|
||||||
import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component";
|
import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component";
|
||||||
import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component";
|
import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component";
|
||||||
|
import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service";
|
||||||
|
import { BrowserFido2UserInterfaceSession } from "../../../../fido2/browser-fido2-user-interface.service";
|
||||||
import { BrowserTotpCaptureService } from "../../../services/browser-totp-capture.service";
|
import { BrowserTotpCaptureService } from "../../../services/browser-totp-capture.service";
|
||||||
|
import {
|
||||||
|
fido2PopoutSessionData$,
|
||||||
|
Fido2SessionData,
|
||||||
|
} from "../../../utils/fido2-popout-session-data";
|
||||||
|
import { VaultPopoutType } from "../../../utils/vault-popout-window";
|
||||||
import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-attachments.component";
|
import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-attachments.component";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,12 +40,14 @@ import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-a
|
|||||||
class QueryParams {
|
class QueryParams {
|
||||||
constructor(params: Params) {
|
constructor(params: Params) {
|
||||||
this.cipherId = params.cipherId;
|
this.cipherId = params.cipherId;
|
||||||
this.type = parseInt(params.type, null);
|
this.type = params.type != undefined ? parseInt(params.type, null) : undefined;
|
||||||
this.clone = params.clone === "true";
|
this.clone = params.clone === "true";
|
||||||
this.folderId = params.folderId;
|
this.folderId = params.folderId;
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
this.collectionId = params.collectionId;
|
this.collectionId = params.collectionId;
|
||||||
this.uri = params.uri;
|
this.uri = params.uri;
|
||||||
|
this.username = params.username;
|
||||||
|
this.name = params.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +58,7 @@ class QueryParams {
|
|||||||
/**
|
/**
|
||||||
* The type of cipher to create.
|
* The type of cipher to create.
|
||||||
*/
|
*/
|
||||||
type: CipherType;
|
type?: CipherType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to clone the cipher.
|
* Whether to clone the cipher.
|
||||||
@ -73,6 +84,16 @@ class QueryParams {
|
|||||||
* Optional URI to pre-fill for login ciphers.
|
* Optional URI to pre-fill for login ciphers.
|
||||||
*/
|
*/
|
||||||
uri?: string;
|
uri?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional username to pre-fill for login/identity ciphers.
|
||||||
|
*/
|
||||||
|
username?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional name to pre-fill for the cipher.
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AddEditQueryParams = Partial<Record<keyof QueryParams, string>>;
|
export type AddEditQueryParams = Partial<Record<keyof QueryParams, string>>;
|
||||||
@ -99,7 +120,7 @@ export type AddEditQueryParams = Partial<Record<keyof QueryParams, string>>;
|
|||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AddEditV2Component {
|
export class AddEditV2Component implements OnInit {
|
||||||
headerText: string;
|
headerText: string;
|
||||||
config: CipherFormConfig;
|
config: CipherFormConfig;
|
||||||
|
|
||||||
@ -111,16 +132,50 @@ export class AddEditV2Component {
|
|||||||
return this.config?.originalCipher?.id as CipherId;
|
return this.config?.originalCipher?.id as CipherId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fido2PopoutSessionData$ = fido2PopoutSessionData$();
|
||||||
|
private fido2PopoutSessionData: Fido2SessionData;
|
||||||
|
|
||||||
|
private get inFido2PopoutWindow() {
|
||||||
|
return BrowserPopupUtils.inPopout(window) && this.fido2PopoutSessionData.isFido2Session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get inSingleActionPopout() {
|
||||||
|
return BrowserPopupUtils.inSingleActionPopout(window, VaultPopoutType.addEditVaultItem);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private location: Location,
|
private location: Location,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private addEditFormConfigService: CipherFormConfigService,
|
private addEditFormConfigService: CipherFormConfigService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private popupCloseWarningService: PopupCloseWarningService,
|
||||||
) {
|
) {
|
||||||
this.subscribeToParams();
|
this.subscribeToParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.fido2PopoutSessionData = await firstValueFrom(this.fido2PopoutSessionData$);
|
||||||
|
|
||||||
|
if (BrowserPopupUtils.inPopout(window)) {
|
||||||
|
this.popupCloseWarningService.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before the form is submitted, allowing us to handle Fido2 user verification.
|
||||||
|
*/
|
||||||
|
protected checkFido2UserVerification: () => Promise<boolean> = async () => {
|
||||||
|
if (!this.inFido2PopoutWindow) {
|
||||||
|
// Not in a Fido2 popout window, no need to handle user verification.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use fido2 user verification service once user verification for passkeys is approved for production.
|
||||||
|
// We are bypassing user verification pending approval for production.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigates to previous view or view-cipher path
|
* Navigates to previous view or view-cipher path
|
||||||
* depending on the history length.
|
* depending on the history length.
|
||||||
@ -129,6 +184,17 @@ export class AddEditV2Component {
|
|||||||
* forced into a popout window.
|
* forced into a popout window.
|
||||||
*/
|
*/
|
||||||
async handleBackButton() {
|
async handleBackButton() {
|
||||||
|
if (this.inFido2PopoutWindow) {
|
||||||
|
this.popupCloseWarningService.disable();
|
||||||
|
BrowserFido2UserInterfaceSession.abortPopout(this.fido2PopoutSessionData.sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inSingleActionPopout) {
|
||||||
|
await BrowserPopupUtils.closeSingleActionPopout(VaultPopoutType.addEditVaultItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (history.length === 1) {
|
if (history.length === 1) {
|
||||||
await this.router.navigate(["/view-cipher"], {
|
await this.router.navigate(["/view-cipher"], {
|
||||||
queryParams: { cipherId: this.originalCipherId },
|
queryParams: { cipherId: this.originalCipherId },
|
||||||
@ -138,7 +204,25 @@ export class AddEditV2Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCipherSaved() {
|
async onCipherSaved(cipher: CipherView) {
|
||||||
|
if (BrowserPopupUtils.inPopout(window)) {
|
||||||
|
this.popupCloseWarningService.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inFido2PopoutWindow) {
|
||||||
|
BrowserFido2UserInterfaceSession.confirmNewCredentialResponse(
|
||||||
|
this.fido2PopoutSessionData.sessionId,
|
||||||
|
cipher.id,
|
||||||
|
this.fido2PopoutSessionData.userVerification,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.inSingleActionPopout) {
|
||||||
|
await BrowserPopupUtils.closeSingleActionPopout(VaultPopoutType.addEditVaultItem, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,6 +273,12 @@ export class AddEditV2Component {
|
|||||||
if (params.uri) {
|
if (params.uri) {
|
||||||
config.initialValues.loginUri = params.uri;
|
config.initialValues.loginUri = params.uri;
|
||||||
}
|
}
|
||||||
|
if (params.username) {
|
||||||
|
config.initialValues.username = params.username;
|
||||||
|
}
|
||||||
|
if (params.name) {
|
||||||
|
config.initialValues.name = params.name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHeader(mode: CipherFormMode, type: CipherType) {
|
setHeader(mode: CipherFormMode, type: CipherType) {
|
||||||
|
@ -397,6 +397,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove and use fido2 user verification service once user verification for passkeys is approved for production.
|
// TODO: Remove and use fido2 user verification service once user verification for passkeys is approved for production.
|
||||||
|
// Be sure to make the same changes to add-edit-v2.component.ts if applicable
|
||||||
private async handleFido2UserVerification(
|
private async handleFido2UserVerification(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
userVerification: boolean,
|
userVerification: boolean,
|
||||||
|
@ -2,6 +2,18 @@ import { inject } from "@angular/core";
|
|||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { map } from "rxjs";
|
import { map } from "rxjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing the data that can be passed as query params for a FIDO2 session.
|
||||||
|
*/
|
||||||
|
export interface Fido2SessionData {
|
||||||
|
isFido2Session: boolean;
|
||||||
|
sessionId: string;
|
||||||
|
fallbackSupported: boolean;
|
||||||
|
userVerification: boolean;
|
||||||
|
senderUrl: string;
|
||||||
|
fromLock: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to retrieve FIDO2 session data from query parameters.
|
* Function to retrieve FIDO2 session data from query parameters.
|
||||||
* Expected to be used within components tied to routes with these query parameters.
|
* Expected to be used within components tied to routes with these query parameters.
|
||||||
@ -10,13 +22,16 @@ export function fido2PopoutSessionData$() {
|
|||||||
const route = inject(ActivatedRoute);
|
const route = inject(ActivatedRoute);
|
||||||
|
|
||||||
return route.queryParams.pipe(
|
return route.queryParams.pipe(
|
||||||
map((queryParams) => ({
|
map(
|
||||||
|
(queryParams) =>
|
||||||
|
<Fido2SessionData>{
|
||||||
isFido2Session: queryParams.sessionId != null,
|
isFido2Session: queryParams.sessionId != null,
|
||||||
sessionId: queryParams.sessionId as string,
|
sessionId: queryParams.sessionId as string,
|
||||||
fallbackSupported: queryParams.fallbackSupported === "true",
|
fallbackSupported: queryParams.fallbackSupported === "true",
|
||||||
userVerification: queryParams.userVerification === "true",
|
userVerification: queryParams.userVerification === "true",
|
||||||
senderUrl: queryParams.senderUrl as string,
|
senderUrl: queryParams.senderUrl as string,
|
||||||
fromLock: queryParams.fromLock === "true",
|
fromLock: queryParams.fromLock === "true",
|
||||||
})),
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ export type OptionalInitialValues = {
|
|||||||
organizationId?: OrganizationId;
|
organizationId?: OrganizationId;
|
||||||
collectionIds?: CollectionId[];
|
collectionIds?: CollectionId[];
|
||||||
loginUri?: string;
|
loginUri?: string;
|
||||||
|
username?: string;
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,6 +90,12 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
|
|||||||
@Input()
|
@Input()
|
||||||
submitBtn?: ButtonComponent;
|
submitBtn?: ButtonComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional function to call before submitting the form. If the function returns false, the form will not be submitted.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
beforeSubmit: () => Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event emitted when the cipher is saved successfully.
|
* Event emitted when the cipher is saved successfully.
|
||||||
*/
|
*/
|
||||||
@ -213,7 +219,17 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.addEditFormService.saveCipher(this.updatedCipherView, this.config);
|
if (this.beforeSubmit) {
|
||||||
|
const shouldSubmit = await this.beforeSubmit();
|
||||||
|
if (!shouldSubmit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedCipher = await this.addEditFormService.saveCipher(
|
||||||
|
this.updatedCipherView,
|
||||||
|
this.config,
|
||||||
|
);
|
||||||
|
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
@ -225,6 +241,6 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.cipherSaved.emit(this.updatedCipherView);
|
this.cipherSaved.emit(savedCipher);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,10 @@ export class IdentitySectionComponent implements OnInit {
|
|||||||
|
|
||||||
if (this.originalCipherView && this.originalCipherView.id) {
|
if (this.originalCipherView && this.originalCipherView.id) {
|
||||||
this.populateFormData();
|
this.populateFormData();
|
||||||
|
} else {
|
||||||
|
this.identityForm.patchValue({
|
||||||
|
username: this.cipherFormContainer.config.initialValues?.username || "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ export class ItemDetailsSectionComponent implements OnInit {
|
|||||||
await this.initFromExistingCipher();
|
await this.initFromExistingCipher();
|
||||||
} else {
|
} else {
|
||||||
this.itemDetailsForm.setValue({
|
this.itemDetailsForm.setValue({
|
||||||
name: "",
|
name: this.initialValues?.name || "",
|
||||||
organizationId: this.initialValues?.organizationId || this.defaultOwner,
|
organizationId: this.initialValues?.organizationId || this.defaultOwner,
|
||||||
folderId: this.initialValues?.folderId || null,
|
folderId: this.initialValues?.folderId || null,
|
||||||
collectionIds: [],
|
collectionIds: [],
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
bitIconButton="bwi-minus-circle"
|
bitIconButton="bwi-minus-circle"
|
||||||
buttonType="danger"
|
buttonType="danger"
|
||||||
bitSuffix
|
bitSuffix
|
||||||
*ngIf="loginDetailsForm.enabled"
|
*ngIf="loginDetailsForm.enabled && viewHiddenFields"
|
||||||
[bitAction]="removePasskey"
|
[bitAction]="removePasskey"
|
||||||
data-testid="remove-passkey-button"
|
data-testid="remove-passkey-button"
|
||||||
[appA11yTitle]="'removePasskey' | i18n"
|
[appA11yTitle]="'removePasskey' | i18n"
|
||||||
|
@ -452,6 +452,8 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
|
|
||||||
fixture = TestBed.createComponent(LoginDetailsSectionComponent);
|
fixture = TestBed.createComponent(LoginDetailsSectionComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
jest.spyOn(component, "viewHiddenFields", "get").mockReturnValue(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders the passkey field when available", () => {
|
it("renders the passkey field when available", () => {
|
||||||
@ -469,7 +471,7 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
it("renders the passkey remove button when editable", () => {
|
it("renders the passkey remove button when editable", () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(getRemovePasskeyBtn).not.toBeNull();
|
expect(getRemovePasskeyBtn()).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render the passkey remove button when not editable", () => {
|
it("does not render the passkey remove button when not editable", () => {
|
||||||
@ -480,6 +482,14 @@ describe("LoginDetailsSectionComponent", () => {
|
|||||||
expect(getRemovePasskeyBtn()).toBeNull();
|
expect(getRemovePasskeyBtn()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not render the passkey remove button when viewHiddenFields is false", () => {
|
||||||
|
jest.spyOn(component, "viewHiddenFields", "get").mockReturnValue(false);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(getRemovePasskeyBtn()).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("hides the passkey field when missing a passkey", () => {
|
it("hides the passkey field when missing a passkey", () => {
|
||||||
(cipherFormContainer.originalCipherView as CipherView).login.fido2Credentials = [];
|
(cipherFormContainer.originalCipherView as CipherView).login.fido2Credentials = [];
|
||||||
|
|
||||||
|
@ -144,9 +144,10 @@ export class LoginDetailsSectionComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async initNewCipher() {
|
private async initNewCipher() {
|
||||||
this.loginDetailsForm.controls.password.patchValue(
|
this.loginDetailsForm.patchValue({
|
||||||
await this.generationService.generateInitialPassword(),
|
username: this.cipherFormContainer.config.initialValues?.username || "",
|
||||||
);
|
password: await this.generationService.generateInitialPassword(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
captureTotp = async () => {
|
captureTotp = async () => {
|
||||||
|
@ -48,7 +48,7 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
cipherType,
|
cipherType: cipher?.type ?? cipherType ?? CipherType.Login,
|
||||||
admin: false,
|
admin: false,
|
||||||
allowPersonalOwnership,
|
allowPersonalOwnership,
|
||||||
originalCipher: cipher,
|
originalCipher: cipher,
|
||||||
|
Loading…
Reference in New Issue
Block a user