1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-22 11:45:59 +01:00

[PM-4260] [BEEEP] Mask TOTP seeds in cipher edit view - similar to how the password is hidden (#6649)

* PoC disallow changing masked values in edit mode and mask TOTP with password

* toggle totp seed visibility independently from password visibility in edit mode

* cleanup

* add fallback value for when a cipher returns a null value for maskedPassword

* toggle masks off for maskable login properties with no value on load

* do not show mask toggle for password or totp if no value is present
This commit is contained in:
Jonathan Prusik 2024-01-12 22:35:30 -05:00 committed by GitHub
parent c53db92f8a
commit eae845d900
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 6 deletions

View File

@ -70,15 +70,18 @@
<div class="row-main"> <div class="row-main">
<label for="loginPassword">{{ "password" | i18n }}</label> <label for="loginPassword">{{ "password" | i18n }}</label>
<input <input
*ngIf="showPassword"
id="loginPassword" id="loginPassword"
class="monospaced" class="monospaced"
type="{{ showPassword ? 'text' : 'password' }}" type="text"
name="Login.Password" name="Login.Password"
[(ngModel)]="cipher.login.password" [(ngModel)]="cipher.login.password"
appInputVerbatim appInputVerbatim
[disabled]="!cipher.viewPassword"
[readonly]="!cipher.edit && editMode" [readonly]="!cipher.edit && editMode"
/> />
<div *ngIf="!showPassword" class="monospaced">
{{ cipher.login.maskedPassword || "••••••••" }}
</div>
</div> </div>
<div class="action-buttons"> <div class="action-buttons">
<button <button
@ -108,7 +111,7 @@
appStopClick appStopClick
appA11yTitle="{{ 'toggleVisibility' | i18n }}" appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePassword()" (click)="togglePassword()"
*ngIf="cipher.viewPassword" *ngIf="cipher.viewPassword && cipher.login.password"
[attr.aria-pressed]="showPassword" [attr.aria-pressed]="showPassword"
> >
<i <i
@ -150,17 +153,35 @@
<div class="row-main"> <div class="row-main">
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label> <label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
<input <input
*ngIf="showTotpSeed"
id="loginTotp" id="loginTotp"
type="{{ cipher.viewPassword ? 'text' : 'password' }}" type="text"
name="Login.Totp" name="Login.Totp"
class="monospaced" class="monospaced"
[(ngModel)]="cipher.login.totp" [(ngModel)]="cipher.login.totp"
appInputVerbatim appInputVerbatim
[disabled]="!cipher.viewPassword"
[readonly]="!cipher.edit && editMode" [readonly]="!cipher.edit && editMode"
/> />
<div *ngIf="!showTotpSeed" class="monospaced">
{{ cipher.login.maskedPassword || "••••••••" }}
</div>
</div> </div>
<div class="action-buttons"> <div class="action-buttons">
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="toggleTotpSeed()"
*ngIf="cipher.viewPassword && cipher.login.totp"
[attr.aria-pressed]="showTotpSeed"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showTotpSeed, 'bwi-eye-slash': showTotpSeed }"
></i>
</button>
<button <button
type="button" type="button"
class="row-btn" class="row-btn"

View File

@ -64,6 +64,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
restorePromise: Promise<any>; restorePromise: Promise<any>;
checkPasswordPromise: Promise<number>; checkPasswordPromise: Promise<number>;
showPassword = false; showPassword = false;
showTotpSeed = false;
showCardNumber = false; showCardNumber = false;
showCardCode = false; showCardCode = false;
cipherType = CipherType; cipherType = CipherType;
@ -216,6 +217,8 @@ export class AddEditComponent implements OnInit, OnDestroy {
if (!this.allowPersonal && this.organizationId == undefined) { if (!this.allowPersonal && this.organizationId == undefined) {
this.organizationId = this.defaultOwnerId; this.organizationId = this.defaultOwnerId;
} }
this.resetMaskState();
} }
async load() { async load() {
@ -262,6 +265,8 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.cipher.secureNote.type = SecureNoteType.Generic; this.cipher.secureNote.type = SecureNoteType.Generic;
this.cipher.reprompt = CipherRepromptType.None; this.cipher.reprompt = CipherRepromptType.None;
} }
this.resetMaskState();
} }
if (this.cipher != null && (!this.editMode || loadedAddEditCipherInfo || this.cloneMode)) { if (this.cipher != null && (!this.editMode || loadedAddEditCipherInfo || this.cloneMode)) {
@ -501,10 +506,18 @@ export class AddEditComponent implements OnInit, OnDestroy {
return true; return true;
} }
resetMaskState() {
// toggle masks off for maskable login properties with no value on init/load
this.showTotpSeed = !this.cipher.login?.totp;
this.showPassword = !this.cipher.login?.password;
}
togglePassword() { togglePassword() {
this.showPassword = !this.showPassword; this.showPassword = !this.showPassword;
document.getElementById("loginPassword").focus();
if (this.editMode && this.showPassword) { if (this.editMode && this.showPassword) {
document.getElementById("loginPassword")?.focus();
this.eventCollectionService.collect( this.eventCollectionService.collect(
EventType.Cipher_ClientToggledPasswordVisible, EventType.Cipher_ClientToggledPasswordVisible,
this.cipherId, this.cipherId,
@ -512,6 +525,19 @@ export class AddEditComponent implements OnInit, OnDestroy {
} }
} }
toggleTotpSeed() {
this.showTotpSeed = !this.showTotpSeed;
if (this.editMode && this.showTotpSeed) {
document.getElementById("loginTotp")?.focus();
this.eventCollectionService.collect(
EventType.Cipher_ClientToggledTOTPSeedVisible,
this.cipherId,
);
}
}
async toggleCardNumber() { async toggleCardNumber() {
this.showCardNumber = !this.showCardNumber; this.showCardNumber = !this.showCardNumber;
if (this.showCardNumber) { if (this.showCardNumber) {

View File

@ -30,6 +30,7 @@ export enum EventType {
Cipher_SoftDeleted = 1115, Cipher_SoftDeleted = 1115,
Cipher_Restored = 1116, Cipher_Restored = 1116,
Cipher_ClientToggledCardNumberVisible = 1117, Cipher_ClientToggledCardNumberVisible = 1117,
Cipher_ClientToggledTOTPSeedVisible = 1118,
Collection_Created = 1300, Collection_Created = 1300,
Collection_Updated = 1301, Collection_Updated = 1301,