1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-20 09:23:23 +00:00

[PM-1976] Display passkeys properly on the browser (#5616)

* Removed passkeys from the vault types filter and added fucntion to get the count of Fido2keys and Login types

* Updated build filter to take Fido2key type as a Login type

* Updated icon font files

* Updated vault items and view to handle changes with fido2keys

* Updated add edit view for fido2keys

* Prevent moving passkeys to an organization where it exists

* Prevent moving passkeys to an organization where it exists

* Added view for non-discoverable passkeys

* Added diaglog to inform user that passkey won't be copied when cloning a non discoverable key

* Muted text that shows cipher item is available for 2fa

* Changed conditional to check if an organization already has the same passkey item

* Muted text to align with figma designs and used rpId for the application input value

* Modified checkFido2KeyExistsInOrg function to workk with discoverable and non discoverable keys

* Differentiate between non-discoverable and discoverable keys when moving to an organization

* Added suggested changes from PR review

* Updated font files css changes

* Fixed bug preventing launch bitton from working for Login types (#5639)

* [PM-1574] Display passkeys on web (#5651)

* Allowed discoverable Fido2key type to be displayed alongside Login type

* Added view during edit for discoverable and non-discoverable passkeys

* Fixed PR comments, added relvant tests to domain changes

* Fixed imports and updated the launch function to use the Launchable interface

* Added launch on vault filter for fido2key types

* Added missing passkey text field in edit view (#5800)

* [PM-1977] Display passkeys properly on the desktop (#5763)

* Allowed discoverable Fido2key type to be displayed alongside Login type

* Added view during edit for discoverable and non-discoverable passkeys

* Fixed PR comments, added relvant tests to domain changes

* Fixed imports and updated the launch function to use the Launchable interface

* Added fido2key to login filter and added view display for fido2key

* Added passkeys view for non discoverable passkeys and edit view for passkeys

* Fixed PR comments

* switched date format to short

* [PM-3046] [PM-3047] Defects for discoverable and non-discoverable passkeys on desktop and web (#5847)

* Added missing passkey text field in edit view (#5800)

* Added dialog to clone no discoverable passkeys on web and desktop.Also, removed clone on the desktop for discoverable passkeys and added passkey view to non- discoverable passkeys on desktop during edit

* Prevent cloning dialog on non fido2key ciphers

* Made fido2key use website favicon if avaialble instead of the passkey icon

* Do not display passkey view on clone edit for dekstop

* Do not display passkey view on clone edit for browser

* Prevented movement of passkeys ND or D to an organization once one exists and also made it possible for org memebers with user roles to move passkeys to an organization. (#5868)

* two step passkey view was outside the conditional (#5872)
This commit is contained in:
SmithThe4th
2023-07-24 09:34:10 -04:00
committed by GitHub
parent ca5b033cfd
commit 31276a92f8
36 changed files with 644 additions and 76 deletions

View File

@@ -2249,5 +2249,26 @@
},
"accessDenied": {
"message": "Access denied. You do not have permission to view this page."
},
"loginPasskey": {
"message": "This login uses a passkey"
},
"application": {
"message": "Application"
},
"passkeyEditInformation": {
"message": "You cannot edit passkey application because it would invalidate the passkey."
},
"duplicatePasskey": {
"message": "A passkey with this ID already exists in this organization."
},
"passkeyTwoStepLogin": {
"message": "Available for two-step login"
},
"passkeyNotCopied": {
"message": "Passkey will not be copied"
},
"passkeyNotCopiedAlert": {
"message": "The passkey will not be copied to the cloned item. Do you want to continue cloning this item?"
}
}

View File

@@ -100,3 +100,41 @@
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</ng-container>
<ng-container *ngIf="cipher.type === cipherType.Fido2Key">
<button
type="button"
class="row-btn"
appStopClick
appStopProp
appA11yTitle="{{ 'launch' | i18n }}"
(click)="launchCipher()"
*ngIf="!showView"
[ngClass]="{ disabled: !cipher.fido2Key.canLaunch }"
[attr.disabled]="!cipher.fido2Key.canLaunch ? '' : null"
>
<i class="bwi bwi-lg bwi-share-square" aria-hidden="true"></i>
</button>
<button
type="button"
class="row-btn"
appStopClick
appStopProp
appA11yTitle="{{ 'copyUsername' | i18n }}"
(click)="copy(cipher, cipher.fido2Key.userName, 'username', 'Username')"
[ngClass]="{ disabled: !cipher.fido2Key.userName }"
[attr.disabled]="!cipher.fido2Key.userName ? '' : null"
>
<i class="bwi bwi-lg bwi-user" aria-hidden="true"></i>
</button>
<button
type="button"
class="row-btn"
appStopClick
appStopProp
appA11yTitle="{{ 'loginPasskey' | i18n }}"
[ngClass]="{ disabled: true }"
disabled
>
<i class="bwi bwi-lg bwi-passkey" aria-hidden="true"></i>
</button>
</ng-container>

View File

@@ -21,6 +21,9 @@
<h2 class="box-header">
{{ "itemInformation" | i18n }}
</h2>
<div class="box-footer" *ngIf="cipher.type === cipherType.Fido2Key">
{{ "passkeyEditInformation" | i18n }}
</div>
<div class="box-content">
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
<label for="type">{{ "type" | i18n }}</label>
@@ -129,6 +132,17 @@
</button>
</div>
</div>
<!--Passkey-->
<div class="box" *ngIf="cipher.login.fido2Key && !cloneMode">
<div class="box-content">
<div class="box-content-row text-muted">
<span class="row-label">{{ "typePasskey" | i18n }}</span>
{{ "passkeyTwoStepLogin" | i18n }}
</div>
</div>
</div>
<div class="box-content-row" appBoxRow>
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
<input
@@ -461,6 +475,39 @@
/>
</div>
</div>
<!-- Fido2Key -->
<div *ngIf="cipher.type === cipherType.Fido2Key">
<div class="box-content-row" appBoxRow>
<label for="fido2KeyUsername">{{ "username" | i18n }}</label>
<input
id="fido2KeyUsername"
type="text"
name="Fido2Key.Username"
[(ngModel)]="cipher.fido2Key.userName"
appInputVerbatim
[readonly]="!cipher.edit && editMode"
/>
</div>
<div class="box-content-row text-muted" appBoxRow>
<span class="row-label">{{ "typePasskey" | i18n }}</span>
{{ "dateCreated" | i18n }} {{ cipher.creationDate | date : "short" }}
</div>
<div class="box" *ngIf="cipher.fido2Key.canLaunch">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="fido2KeyApplication">{{ "application" | i18n }}</label>
<input
class="text-muted"
id="fido2KeyApplication"
type="text"
[value]="cipher.fido2Key.rpId"
readonly
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box" *ngIf="cipher.type === cipherType.Login">

View File

@@ -70,7 +70,9 @@
<div class="icon"><i class="bwi bwi-fw bwi-lg bwi-globe"></i></div>
<span class="text">{{ "typeLogin" | i18n }}</span>
</div>
<span class="row-sub-label">{{ typeCounts.get(cipherType.Login) || 0 }}</span>
<span class="row-sub-label">{{
getTypeCountsSum(typeCounts, cipherType.Login, cipherType.Fido2Key)
}}</span>
<span><i class="bwi bwi-angle-right bwi-lg row-sub-icon"></i></span>
</button>
<button
@@ -112,19 +114,6 @@
<span class="row-sub-label">{{ typeCounts.get(cipherType.SecureNote) || 0 }}</span>
<span><i class="bwi bwi-angle-right bwi-lg row-sub-icon"></i></span>
</button>
<button
type="button"
class="box-content-row"
appStopClick
(click)="selectType(cipherType.Fido2Key)"
>
<div class="row-main">
<div class="icon"><i class="bwi bwi-fw bwi-lg bwi-key"></i></div>
<span class="text">{{ "typePasskey" | i18n }}</span>
</div>
<span class="row-sub-label">{{ typeCounts.get(cipherType.Fido2Key) || 0 }}</span>
<span><i class="bwi bwi-angle-right bwi-lg row-sub-icon"></i></span>
</button>
</div>
</div>
<div class="box list" *ngIf="nestedFolders?.length">

View File

@@ -255,7 +255,14 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
}
async launchCipher(cipher: CipherView) {
if (cipher.type !== CipherType.Login || !cipher.login.canLaunch) {
let launchUri: string;
if (cipher.type === CipherType.Login && cipher.login.canLaunch) {
launchUri = cipher.login.launchUri;
} else if (cipher.type === CipherType.Fido2Key && cipher.fido2Key.canLaunch) {
launchUri = cipher.fido2Key.launchUri;
}
if (!launchUri) {
return;
}
@@ -264,7 +271,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
}
this.preventSelected = true;
await this.cipherService.updateLastLaunchedDate(cipher.id);
BrowserApi.createNewTab(cipher.login.launchUri);
BrowserApi.createNewTab(launchUri);
if (this.popupUtils.inPopup(window)) {
BrowserApi.closePopup(window);
}
@@ -355,6 +362,10 @@ export class VaultFilterComponent implements OnInit, OnDestroy {
this.collectionCounts = collectionCounts;
}
getTypeCountsSum(typeCounts: Map<CipherType, number>, ...types: CipherType[]): number {
return types.reduce((sum, type) => sum + (typeCounts.get(type) || 0), 0);
}
showSearching() {
return (
this.hasSearched || (!this.searchPending && this.searchService.isSearchable(this.searchText))

View File

@@ -99,6 +99,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
this.type = parseInt(params.type, null);
switch (this.type) {
case CipherType.Login:
case CipherType.Fido2Key:
this.groupingTitle = this.i18nService.t("logins");
break;
case CipherType.Card:
@@ -209,7 +210,15 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
}
async launchCipher(cipher: CipherView) {
if (cipher.type !== CipherType.Login || !cipher.login.canLaunch) {
let launchUri: string;
if (cipher.type === CipherType.Login && cipher.login.canLaunch) {
launchUri = cipher.login.launchUri;
} else if (cipher.type === CipherType.Fido2Key && cipher.fido2Key.canLaunch) {
launchUri = cipher.fido2Key.launchUri;
}
if (!launchUri) {
return;
}
@@ -218,7 +227,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
}
this.preventSelected = true;
await this.cipherService.updateLastLaunchedDate(cipher.id);
BrowserApi.createNewTab(cipher.login.launchUri);
BrowserApi.createNewTab(launchUri);
if (this.popupUtils.inPopup(window)) {
BrowserApi.closePopup(window);
}
@@ -264,7 +273,12 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
cipherPassesFilter = cipher.isDeleted;
}
if (this.type != null && cipherPassesFilter) {
cipherPassesFilter = cipher.type === this.type;
//Fido2Key's should also be included in the Login type
if (this.type === CipherType.Login) {
cipherPassesFilter = cipher.type === this.type || cipher.type === CipherType.Fido2Key;
} else {
cipherPassesFilter = cipher.type === this.type;
}
}
if (this.folderId != null && this.folderId != "none" && cipherPassesFilter) {
cipherPassesFilter = cipher.folderId === this.folderId;

View File

@@ -201,6 +201,16 @@
</span>
</div>
</div>
<!--Passkey-->
<div class="box" *ngIf="cipher.login.fido2Key">
<div class="box-content">
<div class="box-content-row text-muted">
<span class="row-label">{{ "typePasskey" | i18n }}</span>
{{ "passkeyTwoStepLogin" | i18n }}
</div>
</div>
</div>
</div>
<!-- Card -->
<div *ngIf="cipher.card">
@@ -416,25 +426,54 @@
</div>
<!-- Fido2Key -->
<div *ngIf="cipher.type == cipherType.Fido2Key">
<div class="box-content-row">
<span class="row-label">{{ "keyType" | i18n }}</span>
{{ cipher.fido2Key.keyType }}
</div>
<div class="box-content-row">
<span class="row-label">{{ "keyCurve" | i18n }}</span>
{{ cipher.fido2Key.keyCurve }}
</div>
<div class="box-content-row">
<span class="row-label">{{ "rpName" | i18n }}</span>
{{ cipher.fido2Key.rpName }}
</div>
<div class="box-content-row">
<span class="row-label">{{ "username" | i18n }}</span>
{{ cipher.fido2Key.userName }}
</div>
<div class="box-content-row">
<span class="row-label">{{ "typePasskey" | i18n }}</span>
{{ "dateCreated" | i18n }} {{ cipher.creationDate | date : "short" }}
</div>
<div class="box" *ngIf="cipher.fido2Key.canLaunch">
<div class="box-content">
<div class="box-content-row box-content-row-flex">
<div class="row-main">
<label
class="row-label draggable"
draggable="true"
(dragstart)="setTextDataOnDrag($event, cipher.fido2Key.launchUri)"
>{{ "application" | i18n }}</label
>
<span title="{{ cipher.fido2Key.launchUri }}">
<input type="text" [value]="cipher.fido2Key.rpId" readonly aria-readonly="true" />
</span>
</div>
<div class="action-buttons">
<button
type="button"
class="row-btn"
appStopClick
(click)="launch(cipher.fido2Key)"
attr.aria-label="{{ 'launch' | i18n }} {{ cipher.fido2Key.launchUri }}"
[appA11yTitle]="'launch' | i18n"
>
<i class="bwi bwi-lg bwi-share-square" aria-hidden="true"></i>
</button>
<button
type="button"
class="row-btn"
appStopClick
attr.aria-label="{{ 'copyUri' | i18n }} {{ cipher.fido2Key.launchUri }}"
[appA11yTitle]="'copyUri' | i18n"
(click)="copy(cipher.fido2Key.launchUri, 'uri', 'URI')"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -21,7 +21,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
@@ -41,7 +40,6 @@ export class ViewComponent extends BaseViewComponent {
tab: any;
loadPageDetailsTimeout: number;
inPopout = false;
cipherType = CipherType;
constructor(
cipherService: CipherService,