bitwarden-estensione-browser/apps/web/src/app/settings/emergency-access.component.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

319 lines
11 KiB
TypeScript
Raw Normal View History

import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
2022-06-14 17:10:53 +02:00
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { EmergencyAccessStatusType } from "@bitwarden/common/enums/emergencyAccessStatusType";
import { EmergencyAccessType } from "@bitwarden/common/enums/emergencyAccessType";
import { Utils } from "@bitwarden/common/misc/utils";
import { EmergencyAccessConfirmRequest } from "@bitwarden/common/models/request/emergencyAccessConfirmRequest";
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
import {
EmergencyAccessGranteeDetailsResponse,
EmergencyAccessGrantorDetailsResponse,
2022-06-14 17:10:53 +02:00
} from "@bitwarden/common/models/response/emergencyAccessResponse";
import { EmergencyAccessAddEditComponent } from "./emergency-access-add-edit.component";
import { EmergencyAccessConfirmComponent } from "./emergency-access-confirm.component";
import { EmergencyAccessTakeoverComponent } from "./emergency-access-takeover.component";
@Component({
selector: "emergency-access",
templateUrl: "emergency-access.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class EmergencyAccessComponent implements OnInit {
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
@ViewChild("takeoverTemplate", { read: ViewContainerRef, static: true })
takeoverModalRef: ViewContainerRef;
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
confirmModalRef: ViewContainerRef;
2021-12-17 15:57:11 +01:00
loaded = false;
canAccessPremium: boolean;
trustedContacts: EmergencyAccessGranteeDetailsResponse[];
grantedContacts: EmergencyAccessGrantorDetailsResponse[];
emergencyAccessType = EmergencyAccessType;
emergencyAccessStatusType = EmergencyAccessStatusType;
actionPromise: Promise<any>;
isOrganizationOwner: boolean;
2021-12-17 15:57:11 +01:00
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
constructor(
private apiService: ApiService,
private i18nService: I18nService,
private modalService: ModalService,
private platformUtilsService: PlatformUtilsService,
2021-12-07 20:41:45 +01:00
private cryptoService: CryptoService,
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
private messagingService: MessagingService,
private userNamePipe: UserNamePipe,
private logService: LogService,
private stateService: StateService,
private organizationService: OrganizationService
) {}
2021-12-17 15:57:11 +01:00
async ngOnInit() {
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
this.canAccessPremium = await this.stateService.getCanAccessPremium();
const orgs = await this.organizationService.getAll();
this.isOrganizationOwner = orgs.some((o) => o.isOwner);
this.load();
2021-12-17 15:57:11 +01:00
}
async load() {
this.trustedContacts = (await this.apiService.getEmergencyAccessTrusted()).data;
this.grantedContacts = (await this.apiService.getEmergencyAccessGranted()).data;
this.loaded = true;
2021-12-17 15:57:11 +01:00
}
async premiumRequired() {
if (!this.canAccessPremium) {
this.messagingService.send("premiumRequired");
2021-12-17 15:57:11 +01:00
return;
}
2021-12-17 15:57:11 +01:00
}
async edit(details: EmergencyAccessGranteeDetailsResponse) {
const [modal] = await this.modalService.openViewRef(
EmergencyAccessAddEditComponent,
this.addEditModalRef,
(comp) => {
comp.name = this.userNamePipe.transform(details);
comp.emergencyAccessId = details?.id;
comp.readOnly = !this.canAccessPremium;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
comp.onSaved.subscribe(() => {
modal.close();
this.load();
2021-12-17 15:57:11 +01:00
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
comp.onDeleted.subscribe(() => {
modal.close();
this.remove(details);
2021-12-17 15:57:11 +01:00
});
}
);
}
invite() {
this.edit(null);
}
async reinvite(contact: EmergencyAccessGranteeDetailsResponse) {
if (this.actionPromise != null) {
return;
}
this.actionPromise = this.apiService.postEmergencyAccessReinvite(contact.id);
await this.actionPromise;
this.platformUtilsService.showToast(
2021-12-17 15:57:11 +01:00
"success",
null,
this.i18nService.t("hasBeenReinvited", contact.email)
);
this.actionPromise = null;
2021-12-17 15:57:11 +01:00
}
async confirm(contact: EmergencyAccessGranteeDetailsResponse) {
function updateUser() {
contact.status = EmergencyAccessStatusType.Confirmed;
}
if (this.actionPromise != null) {
return;
}
const autoConfirm = await this.stateService.getAutoConfirmFingerPrints();
if (autoConfirm == null || !autoConfirm) {
const [modal] = await this.modalService.openViewRef(
EmergencyAccessConfirmComponent,
this.confirmModalRef,
(comp) => {
comp.name = this.userNamePipe.transform(contact);
comp.emergencyAccessId = contact.id;
comp.userId = contact?.granteeId;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
comp.onConfirmed.subscribe(async () => {
modal.close();
2021-12-17 15:57:11 +01:00
comp.formPromise = this.doConfirmation(contact);
await comp.formPromise;
2021-12-17 15:57:11 +01:00
updateUser();
2021-12-07 20:41:45 +01:00
this.platformUtilsService.showToast(
2021-12-17 15:57:11 +01:00
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact))
);
2021-12-17 15:57:11 +01:00
});
}
);
2021-12-07 20:41:45 +01:00
return;
}
[Account Switching] [Refactor] Implement new account centric services (#1220) * [chore] updated services.module to use account services * [refactor] sorted services provided by services.module * [chore] removed references to deleted jslib services * [chore] used activeAccount over storageService for account level storage items * [chore] resolved linter warnings * Refactor activeAccountService to stateService * [bug] Remove uneeded calls to state service on logout This was causing console erros on logout. Clearing of data is handled fully in dedicated services, clearing them in state afterwards is essentially a redundant call. * [bug] Add back null locked callback to VaultTimeoutService * Move call to get showUpdateKey * [bug] Ensure HtmlStorageService does not override StateService options and locations * [bug] Adjust theme logic to pull from the new storage locations * [bug] Correct theme not sticking on refresh * [bug] Add enableFullWidth to the account model * [bug] fix theme option empty when light is selected * [bug] init state on application start * [bug] Reinit state when coming back from a lock * [style] Fix lint complaints * [bug] Clean state on logout * [chore] Resolved merge issues * [bug] Correct default for enableGravitars * Bump angular to 12. * Remove angular.json * Bump rxjs * Fix build errors, remove file-loader with asset/resource * Use contenthash * Bump jslib * Bump ngx-toastr * [chore] resolve issues from merge * [chore] resolve issues from merge * [bug] Add missing bracket * Use newer import syntax * [bug] Correct service orge * [style] Fix lint complaints * [chore] update jslib * [review] Address code review * [review] Address code review * [review] Rename providerService to webProviderService Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com> Co-authored-by: Hinton <oscar@oscarhinton.com>
2021-12-14 17:10:26 +01:00
this.actionPromise = this.doConfirmation(contact);
await this.actionPromise;
updateUser();
2021-12-17 15:57:11 +01:00
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(contact))
);
this.actionPromise = null;
2021-12-17 15:57:11 +01:00
}
async remove(
2021-12-07 20:41:45 +01:00
details: EmergencyAccessGranteeDetailsResponse | EmergencyAccessGrantorDetailsResponse
2021-12-17 15:57:11 +01:00
) {
2021-12-07 20:41:45 +01:00
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("removeUserConfirmation"),
this.userNamePipe.transform(details),
this.i18nService.t("yes"),
this.i18nService.t("no"),
2021-12-17 15:57:11 +01:00
"warning"
);
if (!confirmed) {
return false;
}
try {
await this.apiService.deleteEmergencyAccess(details.id);
2021-12-07 20:41:45 +01:00
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(details))
);
2021-12-17 15:57:11 +01:00
if (details instanceof EmergencyAccessGranteeDetailsResponse) {
this.removeGrantee(details);
} else {
this.removeGrantor(details);
}
} catch (e) {
this.logService.error(e);
}
2021-12-17 15:57:11 +01:00
}
async requestAccess(details: EmergencyAccessGrantorDetailsResponse) {
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("requestAccessConfirmation", details.waitTimeDays.toString()),
2021-07-19 10:47:34 +02:00
this.userNamePipe.transform(details),
this.i18nService.t("requestAccess"),
this.i18nService.t("no"),
"warning"
);
2021-12-17 15:57:11 +01:00
if (!confirmed) {
return false;
}
await this.apiService.postEmergencyAccessInitiate(details.id);
2021-12-17 15:57:11 +01:00
details.status = EmergencyAccessStatusType.RecoveryInitiated;
this.platformUtilsService.showToast(
2021-12-17 15:57:11 +01:00
"success",
null,
2021-12-07 20:41:45 +01:00
this.i18nService.t("requestSent", this.userNamePipe.transform(details))
2021-12-17 15:57:11 +01:00
);
}
async approve(details: EmergencyAccessGranteeDetailsResponse) {
const type = this.i18nService.t(
details.type === EmergencyAccessType.View ? "view" : "takeover"
2021-12-07 20:41:45 +01:00
);
2021-12-17 15:57:11 +01:00
2021-12-07 20:41:45 +01:00
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("approveAccessConfirmation", this.userNamePipe.transform(details), type),
2021-07-19 10:47:34 +02:00
this.userNamePipe.transform(details),
2021-12-07 20:41:45 +01:00
this.i18nService.t("approve"),
this.i18nService.t("no"),
2021-12-17 15:57:11 +01:00
"warning"
);
if (!confirmed) {
2021-12-07 20:41:45 +01:00
return false;
}
await this.apiService.postEmergencyAccessApprove(details.id);
details.status = EmergencyAccessStatusType.RecoveryApproved;
2021-12-17 15:57:11 +01:00
2021-12-07 20:41:45 +01:00
this.platformUtilsService.showToast(
2021-12-17 15:57:11 +01:00
"success",
null,
2021-12-07 20:41:45 +01:00
this.i18nService.t("emergencyApproved", this.userNamePipe.transform(details))
2021-12-17 15:57:11 +01:00
);
}
async reject(details: EmergencyAccessGranteeDetailsResponse) {
await this.apiService.postEmergencyAccessReject(details.id);
details.status = EmergencyAccessStatusType.Confirmed;
2021-12-17 15:57:11 +01:00
2021-12-07 20:41:45 +01:00
this.platformUtilsService.showToast(
2021-12-17 15:57:11 +01:00
"success",
null,
2021-12-07 20:41:45 +01:00
this.i18nService.t("emergencyRejected", this.userNamePipe.transform(details))
2021-12-17 15:57:11 +01:00
);
}
async takeover(details: EmergencyAccessGrantorDetailsResponse) {
const [modal] = await this.modalService.openViewRef(
EmergencyAccessTakeoverComponent,
this.takeoverModalRef,
(comp) => {
comp.name = this.userNamePipe.transform(details);
comp.email = details.email;
comp.emergencyAccessId = details != null ? details.id : null;
2021-12-17 15:57:11 +01:00
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
comp.onDone.subscribe(() => {
modal.close();
2021-12-07 20:41:45 +01:00
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("passwordResetFor", this.userNamePipe.transform(details))
);
});
2021-12-17 15:57:11 +01:00
}
);
}
private removeGrantee(details: EmergencyAccessGranteeDetailsResponse) {
const index = this.trustedContacts.indexOf(details);
if (index > -1) {
this.trustedContacts.splice(index, 1);
}
2021-12-17 15:57:11 +01:00
}
private removeGrantor(details: EmergencyAccessGrantorDetailsResponse) {
const index = this.grantedContacts.indexOf(details);
if (index > -1) {
this.grantedContacts.splice(index, 1);
}
2021-12-17 15:57:11 +01:00
}
// Encrypt the master password hash using the grantees public key, and send it to bitwarden for escrow.
private async doConfirmation(details: EmergencyAccessGranteeDetailsResponse) {
const encKey = await this.cryptoService.getEncKey();
const publicKeyResponse = await this.apiService.getUserPublicKey(details.granteeId);
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
2021-12-17 15:57:11 +01:00
try {
this.logService.debug(
"User's fingerprint: " +
(await this.cryptoService.getFingerprint(details.granteeId, publicKey.buffer)).join("-")
2021-12-17 15:57:11 +01:00
);
} catch {
// Ignore errors since it's just a debug message
}
const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer);
const request = new EmergencyAccessConfirmRequest();
request.key = encryptedKey.encryptedString;
await this.apiService.postEmergencyAccessConfirm(details.id, request);
}
}