mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-21 21:11:35 +01:00
[PM-3143] Trusted device encryption: Refactor reset enroll service (#5869)
* create new reset enrollment service * refactor: login decryption options according to TODO * feat: add tests * PM-3143 - Add override to overriden methods --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com>
This commit is contained in:
parent
04232fe94a
commit
41ceaddfc5
@ -1,62 +1,20 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { BrowserApi } from "../../../platform/browser/browser-api";
|
||||||
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "browser-login-decryption-options",
|
selector: "browser-login-decryption-options",
|
||||||
templateUrl: "login-decryption-options.component.html",
|
templateUrl: "login-decryption-options.component.html",
|
||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
override async createUser(): Promise<void> {
|
||||||
protected formBuilder: FormBuilder,
|
try {
|
||||||
protected devicesService: DevicesServiceAbstraction,
|
await super.createUser();
|
||||||
protected stateService: StateService,
|
BrowserApi.closeBitwardenExtensionTab();
|
||||||
protected router: Router,
|
} catch (error) {
|
||||||
protected activatedRoute: ActivatedRoute,
|
this.validationService.showError(error);
|
||||||
protected messagingService: MessagingService,
|
}
|
||||||
protected tokenService: TokenService,
|
|
||||||
protected loginService: LoginService,
|
|
||||||
protected organizationApiService: OrganizationApiServiceAbstraction,
|
|
||||||
protected cryptoService: CryptoService,
|
|
||||||
protected organizationUserService: OrganizationUserService,
|
|
||||||
protected apiService: ApiService,
|
|
||||||
protected i18nService: I18nService,
|
|
||||||
protected validationService: ValidationService,
|
|
||||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
|
||||||
protected platformUtilsService: PlatformUtilsService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
formBuilder,
|
|
||||||
devicesService,
|
|
||||||
stateService,
|
|
||||||
router,
|
|
||||||
activatedRoute,
|
|
||||||
messagingService,
|
|
||||||
tokenService,
|
|
||||||
loginService,
|
|
||||||
organizationApiService,
|
|
||||||
cryptoService,
|
|
||||||
organizationUserService,
|
|
||||||
apiService,
|
|
||||||
i18nService,
|
|
||||||
validationService,
|
|
||||||
deviceTrustCryptoService,
|
|
||||||
platformUtilsService
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,18 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
|
||||||
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "desktop-login-decryption-options",
|
selector: "desktop-login-decryption-options",
|
||||||
templateUrl: "login-decryption-options.component.html",
|
templateUrl: "login-decryption-options.component.html",
|
||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
override async createUser(): Promise<void> {
|
||||||
protected formBuilder: FormBuilder,
|
try {
|
||||||
protected devicesService: DevicesServiceAbstraction,
|
await super.createUser();
|
||||||
protected stateService: StateService,
|
await this.router.navigate(["/vault"]);
|
||||||
protected router: Router,
|
} catch (error) {
|
||||||
protected activatedRoute: ActivatedRoute,
|
this.validationService.showError(error);
|
||||||
protected messagingService: MessagingService,
|
}
|
||||||
protected tokenService: TokenService,
|
|
||||||
protected loginService: LoginService,
|
|
||||||
protected organizationApiService: OrganizationApiServiceAbstraction,
|
|
||||||
protected cryptoService: CryptoService,
|
|
||||||
protected organizationUserService: OrganizationUserService,
|
|
||||||
protected apiService: ApiService,
|
|
||||||
protected i18nService: I18nService,
|
|
||||||
protected validationService: ValidationService,
|
|
||||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
|
||||||
protected platformUtilsService: PlatformUtilsService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
formBuilder,
|
|
||||||
devicesService,
|
|
||||||
stateService,
|
|
||||||
router,
|
|
||||||
activatedRoute,
|
|
||||||
messagingService,
|
|
||||||
tokenService,
|
|
||||||
loginService,
|
|
||||||
organizationApiService,
|
|
||||||
cryptoService,
|
|
||||||
organizationUserService,
|
|
||||||
apiService,
|
|
||||||
i18nService,
|
|
||||||
validationService,
|
|
||||||
deviceTrustCryptoService,
|
|
||||||
platformUtilsService
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@
|
|||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
block
|
block
|
||||||
class="tw-mb-6"
|
class="tw-mb-6"
|
||||||
[bitAction]="createUser"
|
[bitAction]="createUserAction"
|
||||||
>
|
>
|
||||||
{{ "continue" | i18n }}
|
{{ "continue" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,61 +1,21 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
|
|
||||||
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
import { BaseLoginDecryptionOptionsComponent } from "@bitwarden/angular/auth/components/base-login-decryption-options.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
|
||||||
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
|
||||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
|
||||||
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "web-login-decryption-options",
|
selector: "web-login-decryption-options",
|
||||||
templateUrl: "login-decryption-options.component.html",
|
templateUrl: "login-decryption-options.component.html",
|
||||||
})
|
})
|
||||||
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent {
|
||||||
constructor(
|
override async createUser(): Promise<void> {
|
||||||
protected formBuilder: FormBuilder,
|
try {
|
||||||
protected devicesService: DevicesServiceAbstraction,
|
await super.createUser();
|
||||||
protected stateService: StateService,
|
await this.router.navigate(["/vault"]);
|
||||||
protected router: Router,
|
} catch (error) {
|
||||||
protected activatedRoute: ActivatedRoute,
|
this.validationService.showError(error);
|
||||||
protected messagingService: MessagingService,
|
}
|
||||||
protected tokenService: TokenService,
|
|
||||||
protected loginService: LoginService,
|
|
||||||
protected organizationApiService: OrganizationApiServiceAbstraction,
|
|
||||||
protected cryptoService: CryptoService,
|
|
||||||
protected organizationUserService: OrganizationUserService,
|
|
||||||
protected apiService: ApiService,
|
|
||||||
protected i18nService: I18nService,
|
|
||||||
protected validationService: ValidationService,
|
|
||||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
|
||||||
protected platformUtilsService: PlatformUtilsService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
formBuilder,
|
|
||||||
devicesService,
|
|
||||||
stateService,
|
|
||||||
router,
|
|
||||||
activatedRoute,
|
|
||||||
messagingService,
|
|
||||||
tokenService,
|
|
||||||
loginService,
|
|
||||||
organizationApiService,
|
|
||||||
cryptoService,
|
|
||||||
organizationUserService,
|
|
||||||
apiService,
|
|
||||||
i18nService,
|
|
||||||
validationService,
|
|
||||||
deviceTrustCryptoService,
|
|
||||||
platformUtilsService
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUserAction = async (): Promise<void> => {
|
||||||
|
return this.createUser();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,10 @@ import {
|
|||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
import { DevicesServiceAbstraction } from "@bitwarden/common/abstractions/devices/devices.service.abstraction";
|
||||||
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service";
|
||||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests";
|
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.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 { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
||||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
@ -28,10 +28,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
|||||||
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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
||||||
import { AccountDecryptionOptions } from "@bitwarden/common/platform/models/domain/account";
|
import { AccountDecryptionOptions } from "@bitwarden/common/platform/models/domain/account";
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
|
||||||
import { UserKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
NewUser,
|
NewUser,
|
||||||
@ -88,7 +85,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected validationService: ValidationService,
|
protected validationService: ValidationService,
|
||||||
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction,
|
||||||
protected platformUtilsService: PlatformUtilsService
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
|
protected passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -240,7 +238,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
this.router.navigate(["/lock"]);
|
this.router.navigate(["/lock"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
createUser = async () => {
|
async createUser() {
|
||||||
if (this.data.state !== State.NewUser) {
|
if (this.data.state !== State.NewUser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -248,11 +246,11 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
// this.loading to support clients without async-actions-support
|
// this.loading to support clients without async-actions-support
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const { userKey, publicKey, privateKey } = await this.cryptoService.initAccount();
|
const { publicKey, privateKey } = await this.cryptoService.initAccount();
|
||||||
const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString);
|
const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString);
|
||||||
await this.apiService.postAccountKeys(keysRequest);
|
await this.apiService.postAccountKeys(keysRequest);
|
||||||
|
|
||||||
await this.passwordResetEnroll(userKey, publicKey, privateKey);
|
await this.passwordResetEnrollmentService.enroll(this.data.organizationId);
|
||||||
|
|
||||||
if (this.rememberDeviceForm.value.rememberDevice) {
|
if (this.rememberDeviceForm.value.rememberDevice) {
|
||||||
await this.deviceTrustCryptoService.trustDevice();
|
await this.deviceTrustCryptoService.trustDevice();
|
||||||
@ -262,46 +260,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
passwordResetEnroll = async (userKey: UserKey, publicKey: string, privateKey: EncString) => {
|
|
||||||
if (this.data.state !== State.NewUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this.loading to support clients without async-actions-support
|
|
||||||
this.loading = true;
|
|
||||||
try {
|
|
||||||
const orgKeyResponse = await this.organizationApiService.getKeys(this.data.organizationId);
|
|
||||||
if (orgKeyResponse == null) {
|
|
||||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const orgPublicKey = Utils.fromB64ToArray(orgKeyResponse.publicKey);
|
|
||||||
|
|
||||||
// RSA Encrypt user's userKey.key with organization public key
|
|
||||||
const userId = await this.stateService.getUserId();
|
|
||||||
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, orgPublicKey.buffer);
|
|
||||||
|
|
||||||
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
|
||||||
resetRequest.resetPasswordKey = encryptedKey.encryptedString;
|
|
||||||
|
|
||||||
await this.organizationUserService.putOrganizationUserResetPasswordEnrollment(
|
|
||||||
this.data.organizationId,
|
|
||||||
userId,
|
|
||||||
resetRequest
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: On browser this should close the window. But since we might extract
|
|
||||||
// this logic into a service I'm gonna leaves this as-is untill that
|
|
||||||
// refactor is done
|
|
||||||
await this.router.navigate(["/vault"]);
|
|
||||||
} catch (error) {
|
|
||||||
this.validationService.showError(error);
|
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
logOut() {
|
logOut() {
|
||||||
this.loading = true; // to avoid an awkward delay in browser extension
|
this.loading = true; // to avoid an awkward delay in browser extension
|
||||||
|
@ -45,6 +45,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst
|
|||||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||||
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
|
||||||
|
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
|
||||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
|
||||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
|
||||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
||||||
@ -56,6 +57,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device
|
|||||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||||
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
import { LoginService } from "@bitwarden/common/auth/services/login.service";
|
||||||
|
import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation";
|
||||||
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
import { TokenService } from "@bitwarden/common/auth/services/token.service";
|
||||||
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
|
||||||
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
|
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
|
||||||
@ -595,6 +597,17 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
|
|||||||
useClass: OrganizationUserServiceImplementation,
|
useClass: OrganizationUserServiceImplementation,
|
||||||
deps: [ApiServiceAbstraction],
|
deps: [ApiServiceAbstraction],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: PasswordResetEnrollmentServiceAbstraction,
|
||||||
|
useClass: PasswordResetEnrollmentServiceImplementation,
|
||||||
|
deps: [
|
||||||
|
OrganizationApiServiceAbstraction,
|
||||||
|
StateServiceAbstraction,
|
||||||
|
CryptoServiceAbstraction,
|
||||||
|
OrganizationUserService,
|
||||||
|
I18nServiceAbstraction,
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: ProviderServiceAbstraction,
|
provide: ProviderServiceAbstraction,
|
||||||
useClass: ProviderService,
|
useClass: ProviderService,
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { UserKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
|
export abstract class PasswordResetEnrollmentServiceAbstraction {
|
||||||
|
/**
|
||||||
|
* Enroll current user in password reset
|
||||||
|
* @param organizationId - Organization in which to enroll the user
|
||||||
|
* @returns Promise that resolves when the user is enrolled
|
||||||
|
* @throws Error if the action fails
|
||||||
|
*/
|
||||||
|
abstract enroll(organizationId: string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enroll user in password reset
|
||||||
|
* @param organizationId - Organization in which to enroll the user
|
||||||
|
* @param userId - User to enroll
|
||||||
|
* @param userKey - User's symmetric key
|
||||||
|
* @returns Promise that resolves when the user is enrolled
|
||||||
|
* @throws Error if the action fails
|
||||||
|
*/
|
||||||
|
abstract enroll(organizationId: string, userId: string, userKey: UserKey): Promise<void>;
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
|
import { OrganizationUserService } from "../../abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
|
import { CryptoService } from "../../platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||||
|
import { StateService } from "../../platform/abstractions/state.service";
|
||||||
|
|
||||||
|
import { PasswordResetEnrollmentServiceImplementation } from "./password-reset-enrollment.service.implementation";
|
||||||
|
|
||||||
|
describe("PasswordResetEnrollmentServiceImplementation", () => {
|
||||||
|
let organizationApiService: MockProxy<OrganizationApiServiceAbstraction>;
|
||||||
|
let stateService: MockProxy<StateService>;
|
||||||
|
let cryptoService: MockProxy<CryptoService>;
|
||||||
|
let organizationUserService: MockProxy<OrganizationUserService>;
|
||||||
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
let service: PasswordResetEnrollmentServiceImplementation;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
organizationApiService = mock<OrganizationApiServiceAbstraction>();
|
||||||
|
stateService = mock<StateService>();
|
||||||
|
cryptoService = mock<CryptoService>();
|
||||||
|
organizationUserService = mock<OrganizationUserService>();
|
||||||
|
i18nService = mock<I18nService>();
|
||||||
|
service = new PasswordResetEnrollmentServiceImplementation(
|
||||||
|
organizationApiService,
|
||||||
|
stateService,
|
||||||
|
cryptoService,
|
||||||
|
organizationUserService,
|
||||||
|
i18nService
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("enroll", () => {
|
||||||
|
it("should throw an error if the organization keys are not found", async () => {
|
||||||
|
organizationApiService.getKeys.mockResolvedValue(null);
|
||||||
|
i18nService.t.mockReturnValue("resetPasswordOrgKeysError");
|
||||||
|
|
||||||
|
const result = () => service.enroll("orgId");
|
||||||
|
|
||||||
|
await expect(result).rejects.toThrowError("resetPasswordOrgKeysError");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enroll the user when no user id or key is provided", async () => {
|
||||||
|
const orgKeyResponse = {
|
||||||
|
publicKey: "publicKey",
|
||||||
|
privateKey: "privateKey",
|
||||||
|
};
|
||||||
|
const encryptedKey = { encryptedString: "encryptedString" };
|
||||||
|
organizationApiService.getKeys.mockResolvedValue(orgKeyResponse as any);
|
||||||
|
stateService.getUserId.mockResolvedValue("userId");
|
||||||
|
cryptoService.getUserKey.mockResolvedValue({ key: "key" } as any);
|
||||||
|
cryptoService.rsaEncrypt.mockResolvedValue(encryptedKey as any);
|
||||||
|
|
||||||
|
await service.enroll("orgId");
|
||||||
|
|
||||||
|
expect(
|
||||||
|
organizationUserService.putOrganizationUserResetPasswordEnrollment
|
||||||
|
).toHaveBeenCalledWith(
|
||||||
|
"orgId",
|
||||||
|
"userId",
|
||||||
|
expect.objectContaining({
|
||||||
|
resetPasswordKey: encryptedKey.encryptedString,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should enroll the user when a user id and key is provided", async () => {
|
||||||
|
const orgKeyResponse = {
|
||||||
|
publicKey: "publicKey",
|
||||||
|
privateKey: "privateKey",
|
||||||
|
};
|
||||||
|
const encryptedKey = { encryptedString: "encryptedString" };
|
||||||
|
organizationApiService.getKeys.mockResolvedValue(orgKeyResponse as any);
|
||||||
|
cryptoService.rsaEncrypt.mockResolvedValue(encryptedKey as any);
|
||||||
|
|
||||||
|
await service.enroll("orgId", "userId", { key: "key" } as any);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
organizationUserService.putOrganizationUserResetPasswordEnrollment
|
||||||
|
).toHaveBeenCalledWith(
|
||||||
|
"orgId",
|
||||||
|
"userId",
|
||||||
|
expect.objectContaining({
|
||||||
|
resetPasswordKey: encryptedKey.encryptedString,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
import { OrganizationUserService } from "../../abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationUserResetPasswordEnrollmentRequest } from "../../abstractions/organization-user/requests";
|
||||||
|
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
|
import { CryptoService } from "../../platform/abstractions/crypto.service";
|
||||||
|
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||||
|
import { StateService } from "../../platform/abstractions/state.service";
|
||||||
|
import { Utils } from "../../platform/misc/utils";
|
||||||
|
import { UserKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { PasswordResetEnrollmentServiceAbstraction } from "../abstractions/password-reset-enrollment.service.abstraction";
|
||||||
|
|
||||||
|
export class PasswordResetEnrollmentServiceImplementation
|
||||||
|
implements PasswordResetEnrollmentServiceAbstraction
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
protected organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
|
protected stateService: StateService,
|
||||||
|
protected cryptoService: CryptoService,
|
||||||
|
protected organizationUserService: OrganizationUserService,
|
||||||
|
protected i18nService: I18nService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async enroll(organizationId: string): Promise<void>;
|
||||||
|
async enroll(organizationId: string, userId: string, userKey: UserKey): Promise<void>;
|
||||||
|
async enroll(organizationId: string, userId?: string, userKey?: UserKey): Promise<void> {
|
||||||
|
const orgKeyResponse = await this.organizationApiService.getKeys(organizationId);
|
||||||
|
if (orgKeyResponse == null) {
|
||||||
|
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgPublicKey = Utils.fromB64ToArray(orgKeyResponse.publicKey);
|
||||||
|
|
||||||
|
userId = userId ?? (await this.stateService.getUserId());
|
||||||
|
userKey = userKey ?? (await this.cryptoService.getUserKey(userId));
|
||||||
|
// RSA Encrypt user's userKey.key with organization public key
|
||||||
|
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, orgPublicKey.buffer);
|
||||||
|
|
||||||
|
const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
|
||||||
|
resetRequest.resetPasswordKey = encryptedKey.encryptedString;
|
||||||
|
|
||||||
|
await this.organizationUserService.putOrganizationUserResetPasswordEnrollment(
|
||||||
|
organizationId,
|
||||||
|
userId,
|
||||||
|
resetRequest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user