1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-21 21:11:35 +01:00

move takeover logic to service

This commit is contained in:
Jacob Fink 2023-10-05 17:55:29 -04:00
parent ed04bc65ec
commit 11a5f1a8a2
4 changed files with 95 additions and 68 deletions

View File

@ -1,6 +1,7 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -25,6 +26,7 @@ import { EmergencyAccessApiService } from "./emergency-access-api.service";
import { EmergencyAccessAcceptRequest } from "./request/emergency-access-accept.request"; import { EmergencyAccessAcceptRequest } from "./request/emergency-access-accept.request";
import { EmergencyAccessConfirmRequest } from "./request/emergency-access-confirm.request"; import { EmergencyAccessConfirmRequest } from "./request/emergency-access-confirm.request";
import { EmergencyAccessInviteRequest } from "./request/emergency-access-invite.request"; import { EmergencyAccessInviteRequest } from "./request/emergency-access-invite.request";
import { EmergencyAccessPasswordRequest } from "./request/emergency-access-password.request";
import { EmergencyAccessUpdateRequest } from "./request/emergency-access-update.request"; import { EmergencyAccessUpdateRequest } from "./request/emergency-access-update.request";
@Injectable() @Injectable()
@ -39,23 +41,23 @@ export class EmergencyAccessService {
) {} ) {}
/** /**
* Gets all emergency access that the user has been granted * Gets all emergency access that the user has been granted.
*/ */
async getEmergencyAccessTrusted(): Promise<EmergencyAccessGranteeView[]> { async getEmergencyAccessTrusted(): Promise<EmergencyAccessGranteeView[]> {
return (await this.emergencyAccessApiService.getEmergencyAccessTrusted()).data; return (await this.emergencyAccessApiService.getEmergencyAccessTrusted()).data;
} }
/** /**
* Gets all emergency access that the user has granted * Gets all emergency access that the user has granted.
*/ */
async getEmergencyAccessGranted(): Promise<EmergencyAccessGrantorView[]> { async getEmergencyAccessGranted(): Promise<EmergencyAccessGrantorView[]> {
return (await this.emergencyAccessApiService.getEmergencyAccessGranted()).data; return (await this.emergencyAccessApiService.getEmergencyAccessGranted()).data;
} }
/** /**
* Invites the email address to be an emergency contact * Invites the email address to be an emergency contact.
* Step 1 of the 3 step setup flow * Step 1 of the 3 step setup flow.
* Intended for grantor * Intended for grantor.
* @param email email address of trusted emergency contact * @param email email address of trusted emergency contact
* @param type type of emergency access * @param type type of emergency access
* @param waitTimeDays number of days to wait before granting access * @param waitTimeDays number of days to wait before granting access
@ -70,8 +72,17 @@ export class EmergencyAccessService {
} }
/** /**
* Edits an existing emergency access * Sends another email for an existing emergency access invitation.
* Intended for grantor * Intended for grantor.
* @param id emergency access id
*/
reinvite(id: string): Promise<void> {
return this.emergencyAccessApiService.postEmergencyAccessReinvite(id);
}
/**
* Edits an existing emergency access.
* Intended for grantor.
* @param id emergency access id * @param id emergency access id
* @param type type of emergency access * @param type type of emergency access
* @param waitTimeDays number of days to wait before granting access * @param waitTimeDays number of days to wait before granting access
@ -85,9 +96,9 @@ export class EmergencyAccessService {
} }
/** /**
* Accepts an emergency access invitation * Accepts an emergency access invitation.
* Step 2 of the 3 step setup flow * Step 2 of the 3 step setup flow.
* Intended for grantee * Intended for grantee.
* @param id emergency access id * @param id emergency access id
* @param token secret token provided in email * @param token secret token provided in email
*/ */
@ -99,9 +110,9 @@ export class EmergencyAccessService {
} }
/** /**
* Encrypts user key with grantee's public key and sends to bitwarden * Encrypts user key with grantee's public key and sends to bitwarden.
* Step 3 of the 3 step setup flow * Step 3 of the 3 step setup flow.
* Intended for grantor * Intended for grantor.
* @param id emergency access id * @param id emergency access id
* @param token secret token provided in email * @param token secret token provided in email
*/ */
@ -128,8 +139,17 @@ export class EmergencyAccessService {
} }
/** /**
* Requests access to grantor's vault * Deletes an existing emergency access.
* Intended for grantee * Intended for either grantor or grantee.
* @param id emergency access id
*/
delete(id: string): Promise<void> {
return this.emergencyAccessApiService.deleteEmergencyAccess(id);
}
/**
* Requests access to grantor's vault.
* Intended for grantee.
* @param id emergency access id * @param id emergency access id
*/ */
requestAccess(id: string): Promise<void> { requestAccess(id: string): Promise<void> {
@ -137,8 +157,8 @@ export class EmergencyAccessService {
} }
/** /**
* Approves access to grantor's vault * Approves access to grantor's vault.
* Intended for grantor * Intended for grantor.
* @param id emergency access id * @param id emergency access id
*/ */
approve(id: string): Promise<void> { approve(id: string): Promise<void> {
@ -146,8 +166,8 @@ export class EmergencyAccessService {
} }
/** /**
* Rejects access to grantor's vault * Rejects access to grantor's vault.
* Intended for grantor * Intended for grantor.
* @param id emergency access id * @param id emergency access id
*/ */
reject(id: string): Promise<void> { reject(id: string): Promise<void> {
@ -155,8 +175,8 @@ export class EmergencyAccessService {
} }
/** /**
* Gets the grantor ciphers for an emergency access in view mode * Gets the grantor ciphers for an emergency access in view mode.
* Intended for grantee * Intended for grantee.
* @param id emergency access id * @param id emergency access id
*/ */
async getViewOnlyCiphers(id: string): Promise<CipherView[]> { async getViewOnlyCiphers(id: string): Promise<CipherView[]> {
@ -172,6 +192,44 @@ export class EmergencyAccessService {
return ciphers.sort(this.cipherService.getLocaleSortingFunction()); return ciphers.sort(this.cipherService.getLocaleSortingFunction());
} }
/**
* Changes the password for an emergency access.
* Intended for grantee.
* @param id emergency access id
* @param masterPassword new master password
* @param email email address of grantee (must be consistent or login will fail)
*/
async takeover(id: string, masterPassword: string, email: string) {
const takeoverResponse = await this.emergencyAccessApiService.postEmergencyAccessTakeover(id);
const grantorKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted);
const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey;
if (grantorUserKey == null) {
throw new Error("Failed to decrypt grantor key");
}
const masterKey = await this.cryptoService.makeMasterKey(
masterPassword,
email,
takeoverResponse.kdf,
new KdfConfig(
takeoverResponse.kdfIterations,
takeoverResponse.kdfMemory,
takeoverResponse.kdfParallelism
)
);
const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, grantorUserKey);
const request = new EmergencyAccessPasswordRequest();
request.newMasterPasswordHash = masterKeyHash;
request.key = encKey[1].encryptedString;
this.emergencyAccessApiService.postEmergencyAccessPassword(id, request);
}
async rotateEmergencyAccess(newUserKey: UserKey) { async rotateEmergencyAccess(newUserKey: UserKey) {
const emergencyAccess = await this.emergencyAccessApiService.getEmergencyAccessTrusted(); const emergencyAccess = await this.emergencyAccessApiService.getEmergencyAccessTrusted();
// Any Invited or Accepted requests won't have the key yet, so we don't need to update them // Any Invited or Accepted requests won't have the key yet, so we don't need to update them

View File

@ -12,8 +12,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction"; import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { EmergencyAccessStatusType } from "@bitwarden/common/auth/enums/emergency-access-status-type";
import { EmergencyAccessUpdateRequest } from "@bitwarden/common/auth/models/request/emergency-access-update.request";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { UpdateKeyRequest } from "@bitwarden/common/models/request/update-key.request"; import { UpdateKeyRequest } from "@bitwarden/common/models/request/update-key.request";
@ -40,6 +38,8 @@ import { CipherWithIdRequest } from "@bitwarden/common/vault/models/request/ciph
import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request"; import { FolderWithIdRequest } from "@bitwarden/common/vault/models/request/folder-with-id.request";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
import { EmergencyAccessApiService } from "../core/services/emergency-access/emergency-access-api.service"; import { EmergencyAccessApiService } from "../core/services/emergency-access/emergency-access-api.service";
import { EmergencyAccessStatusType } from "../core/enums/emergency-access-status-type";
import { EmergencyAccessUpdateRequest } from "../core/services/emergency-access/request/emergency-access-update.request";
@Component({ @Component({
selector: "app-change-password", selector: "app-change-password",

View File

@ -6,8 +6,6 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { EmergencyAccessPasswordRequest } from "@bitwarden/common/auth/models/request/emergency-access-password.request";
import { KdfType } from "@bitwarden/common/enums"; import { KdfType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -15,13 +13,11 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import {
SymmetricCryptoKey,
UserKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
import { EmergencyAccessApiService } from "../../core/services/emergency-access/emergency-access-api.service"; import { EmergencyAccessApiService } from "../../core/services/emergency-access/emergency-access-api.service";
import { EmergencyAccessService } from "../../core/services/emergency-access/emergency-access.service";
@Component({ @Component({
selector: "emergency-access-takeover", selector: "emergency-access-takeover",
@ -49,6 +45,7 @@ export class EmergencyAccessTakeoverComponent
passwordGenerationService: PasswordGenerationServiceAbstraction, passwordGenerationService: PasswordGenerationServiceAbstraction,
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
policyService: PolicyService, policyService: PolicyService,
private emergencyAccessService: EmergencyAccessService,
private emergencyAccessApiService: EmergencyAccessApiService, private emergencyAccessApiService: EmergencyAccessApiService,
private logService: LogService, private logService: LogService,
dialogService: DialogService dialogService: DialogService
@ -91,46 +88,20 @@ export class EmergencyAccessTakeoverComponent
return; return;
} }
const takeoverResponse = await this.emergencyAccessApiService.postEmergencyAccessTakeover( try {
this.emergencyAccessId await this.emergencyAccessService.takeover(
); this.emergencyAccessId,
this.masterPassword,
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted); this.email
const oldUserKey = new SymmetricCryptoKey(oldKeyBuffer) as UserKey; );
this.onDone.emit();
if (oldUserKey == null) { } catch (e) {
this.logService.error(e);
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),
this.i18nService.t("unexpectedError") this.i18nService.t("unexpectedError")
); );
return;
}
const masterKey = await this.cryptoService.makeMasterKey(
this.masterPassword,
this.email,
takeoverResponse.kdf,
new KdfConfig(
takeoverResponse.kdfIterations,
takeoverResponse.kdfMemory,
takeoverResponse.kdfParallelism
)
);
const masterKeyHash = await this.cryptoService.hashMasterKey(this.masterPassword, masterKey);
const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, oldUserKey);
const request = new EmergencyAccessPasswordRequest();
request.newMasterPasswordHash = masterKeyHash;
request.key = encKey[1].encryptedString;
this.emergencyAccessApiService.postEmergencyAccessPassword(this.emergencyAccessId, request);
try {
this.onDone.emit();
} catch (e) {
this.logService.error(e);
} }
} }
} }

View File

@ -12,7 +12,6 @@ import { DialogService } from "@bitwarden/components";
import { EmergencyAccessStatusType } from "../../core/enums/emergency-access-status-type"; import { EmergencyAccessStatusType } from "../../core/enums/emergency-access-status-type";
import { EmergencyAccessType } from "../../core/enums/emergency-access-type"; import { EmergencyAccessType } from "../../core/enums/emergency-access-type";
import { EmergencyAccessApiService } from "../../core/services/emergency-access/emergency-access-api.service";
import { EmergencyAccessService } from "../../core/services/emergency-access/emergency-access.service"; import { EmergencyAccessService } from "../../core/services/emergency-access/emergency-access.service";
import { import {
EmergencyAccessGranteeView, EmergencyAccessGranteeView,
@ -45,7 +44,6 @@ export class EmergencyAccessComponent implements OnInit {
isOrganizationOwner: boolean; isOrganizationOwner: boolean;
constructor( constructor(
private emergencyAccessApiService: EmergencyAccessApiService,
private emergencyAccessService: EmergencyAccessService, private emergencyAccessService: EmergencyAccessService,
private i18nService: I18nService, private i18nService: I18nService,
private modalService: ModalService, private modalService: ModalService,
@ -108,7 +106,7 @@ export class EmergencyAccessComponent implements OnInit {
if (this.actionPromise != null) { if (this.actionPromise != null) {
return; return;
} }
this.actionPromise = this.emergencyAccessApiService.postEmergencyAccessReinvite(contact.id); this.actionPromise = this.emergencyAccessService.reinvite(contact.id);
await this.actionPromise; await this.actionPromise;
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"success", "success",
@ -179,7 +177,7 @@ export class EmergencyAccessComponent implements OnInit {
} }
try { try {
await this.emergencyAccessApiService.deleteEmergencyAccess(details.id); await this.emergencyAccessService.delete(details.id);
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"success", "success",
null, null,