mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 09:13:33 +00:00
Password reprompt (#357)
This commit is contained in:
@@ -42,6 +42,7 @@ import { LoginUriView } from '../../models/view/loginUriView';
|
||||
import { LoginView } from '../../models/view/loginView';
|
||||
import { SecureNoteView } from '../../models/view/secureNoteView';
|
||||
|
||||
import { CipherRepromptType } from '../../enums/cipherRepromptType';
|
||||
import { Utils } from '../../misc/utils';
|
||||
|
||||
@Directive()
|
||||
@@ -71,6 +72,7 @@ export class AddEditComponent implements OnInit {
|
||||
restorePromise: Promise<any>;
|
||||
checkPasswordPromise: Promise<number>;
|
||||
showPassword: boolean = false;
|
||||
showCardNumber: boolean = false;
|
||||
showCardCode: boolean = false;
|
||||
cipherType = CipherType;
|
||||
fieldType = FieldType;
|
||||
@@ -84,6 +86,7 @@ export class AddEditComponent implements OnInit {
|
||||
ownershipOptions: any[] = [];
|
||||
currentDate = new Date();
|
||||
allowPersonal = true;
|
||||
reprompt: boolean = false;
|
||||
|
||||
protected writeableCollections: CollectionView[];
|
||||
private previousCipherId: string;
|
||||
@@ -245,6 +248,7 @@ export class AddEditComponent implements OnInit {
|
||||
this.eventService.collect(EventType.Cipher_ClientViewed, this.cipherId);
|
||||
}
|
||||
this.previousCipherId = this.cipherId;
|
||||
this.reprompt = this.cipher.reprompt !== CipherRepromptType.None;
|
||||
}
|
||||
|
||||
async submit(): Promise<boolean> {
|
||||
@@ -422,6 +426,13 @@ export class AddEditComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
async toggleCardNumber() {
|
||||
this.showCardNumber = !this.showCardNumber;
|
||||
if (this.showCardNumber) {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledCardCodeVisible, this.cipherId);
|
||||
}
|
||||
}
|
||||
|
||||
toggleCardCode() {
|
||||
this.showCardCode = !this.showCardCode;
|
||||
document.getElementById('cardCode').focus();
|
||||
@@ -488,6 +499,15 @@ export class AddEditComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
repromptChanged() {
|
||||
this.reprompt = !this.reprompt;
|
||||
if (this.reprompt) {
|
||||
this.cipher.reprompt = CipherRepromptType.Password;
|
||||
} else {
|
||||
this.cipher.reprompt = CipherRepromptType.None;
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadCollections() {
|
||||
const allCollections = await this.collectionService.getAllDecrypted();
|
||||
return allCollections.filter(c => !c.readOnly);
|
||||
|
||||
@@ -19,6 +19,7 @@ import { CipherService } from '../../abstractions/cipher.service';
|
||||
import { CryptoService } from '../../abstractions/crypto.service';
|
||||
import { EventService } from '../../abstractions/event.service';
|
||||
import { I18nService } from '../../abstractions/i18n.service';
|
||||
import { PasswordRepromptService } from '../../abstractions/passwordReprompt.service';
|
||||
import { PlatformUtilsService } from '../../abstractions/platformUtils.service';
|
||||
import { TokenService } from '../../abstractions/token.service';
|
||||
import { TotpService } from '../../abstractions/totp.service';
|
||||
@@ -26,6 +27,7 @@ import { UserService } from '../../abstractions/user.service';
|
||||
|
||||
import { ErrorResponse } from '../../models/response/errorResponse';
|
||||
|
||||
import { CipherRepromptType } from '../../enums/cipherRepromptType';
|
||||
import { AttachmentView } from '../../models/view/attachmentView';
|
||||
import { CipherView } from '../../models/view/cipherView';
|
||||
import { FieldView } from '../../models/view/fieldView';
|
||||
@@ -45,6 +47,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
|
||||
cipher: CipherView;
|
||||
showPassword: boolean;
|
||||
showCardNumber: boolean;
|
||||
showCardCode: boolean;
|
||||
canAccessPremium: boolean;
|
||||
totpCode: string;
|
||||
@@ -57,6 +60,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
|
||||
private totpInterval: any;
|
||||
private previousCipherId: string;
|
||||
private passwordReprompted: boolean = false;
|
||||
|
||||
constructor(protected cipherService: CipherService, protected totpService: TotpService,
|
||||
protected tokenService: TokenService, protected i18nService: I18nService,
|
||||
@@ -64,7 +68,8 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
protected auditService: AuditService, protected win: Window,
|
||||
protected broadcasterService: BroadcasterService, protected ngZone: NgZone,
|
||||
protected changeDetectorRef: ChangeDetectorRef, protected userService: UserService,
|
||||
protected eventService: EventService, protected apiService: ApiService) { }
|
||||
protected eventService: EventService, protected apiService: ApiService,
|
||||
protected passwordRepromptService: PasswordRepromptService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
@@ -110,19 +115,38 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
this.previousCipherId = this.cipherId;
|
||||
}
|
||||
|
||||
edit() {
|
||||
this.onEditCipher.emit(this.cipher);
|
||||
async edit() {
|
||||
if (await this.promptPassword()) {
|
||||
this.onEditCipher.emit(this.cipher);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
clone() {
|
||||
this.onCloneCipher.emit(this.cipher);
|
||||
async clone() {
|
||||
if (await this.promptPassword()) {
|
||||
this.onCloneCipher.emit(this.cipher);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
share() {
|
||||
this.onShareCipher.emit(this.cipher);
|
||||
async share() {
|
||||
if (await this.promptPassword()) {
|
||||
this.onShareCipher.emit(this.cipher);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async delete(): Promise<boolean> {
|
||||
if (!await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.platformUtilsService.showDialog(
|
||||
this.i18nService.t(this.cipher.isDeleted ? 'permanentlyDeleteItemConfirmation' : 'deleteItemConfirmation'),
|
||||
this.i18nService.t('deleteItem'), this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||
@@ -161,14 +185,33 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
return true;
|
||||
}
|
||||
|
||||
togglePassword() {
|
||||
async togglePassword() {
|
||||
if (!await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showPassword = !this.showPassword;
|
||||
if (this.showPassword) {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledPasswordVisible, this.cipherId);
|
||||
}
|
||||
}
|
||||
|
||||
toggleCardCode() {
|
||||
async toggleCardNumber() {
|
||||
if (!await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showCardNumber = !this.showCardNumber;
|
||||
if (this.showCardNumber) {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledCardCodeVisible, this.cipherId);
|
||||
}
|
||||
}
|
||||
|
||||
async toggleCardCode() {
|
||||
if (!await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.showCardCode = !this.showCardCode;
|
||||
if (this.showCardCode) {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledCardCodeVisible, this.cipherId);
|
||||
@@ -191,7 +234,11 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
toggleFieldValue(field: FieldView) {
|
||||
async toggleFieldValue(field: FieldView) {
|
||||
if (!await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f = (field as any);
|
||||
f.showValue = !f.showValue;
|
||||
if (f.showValue) {
|
||||
@@ -211,11 +258,15 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
this.platformUtilsService.launchUri(uri.launchUri);
|
||||
}
|
||||
|
||||
copy(value: string, typeI18nKey: string, aType: string) {
|
||||
async copy(value: string, typeI18nKey: string, aType: string) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.passwordRepromptService.protectedFields().includes(aType) && !await this.promptPassword()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const copyOptions = this.win != null ? { window: this.win } : null;
|
||||
this.platformUtilsService.copyToClipboard(value, copyOptions);
|
||||
this.platformUtilsService.showToast('info', null,
|
||||
@@ -290,6 +341,14 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
return this.cipherService.restoreWithServer(this.cipher.id);
|
||||
}
|
||||
|
||||
protected async promptPassword() {
|
||||
if (this.cipher.reprompt === CipherRepromptType.None || this.passwordReprompted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.passwordReprompted = await this.passwordRepromptService.showPasswordPrompt();
|
||||
}
|
||||
|
||||
private cleanUp() {
|
||||
this.totpCode = null;
|
||||
this.cipher = null;
|
||||
|
||||
Reference in New Issue
Block a user