From 41ceaddfc5cba4baa874b55d8a2a377cb8c51b70 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 21 Jul 2023 18:52:06 +0200 Subject: [PATCH] [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 --- .../login-decryption-options.component.ts | 60 ++----------- .../login-decryption-options.component.ts | 58 ++---------- .../login-decryption-options.component.html | 2 +- .../login-decryption-options.component.ts | 62 +++---------- ...base-login-decryption-options.component.ts | 55 ++---------- .../src/services/jslib-services.module.ts | 13 +++ ...rd-reset-enrollment.service.abstraction.ts | 21 +++++ ...-enrollment.service.implementation.spec.ts | 90 +++++++++++++++++++ ...reset-enrollment.service.implementation.ts | 46 ++++++++++ 9 files changed, 205 insertions(+), 202 deletions(-) create mode 100644 libs/common/src/auth/abstractions/password-reset-enrollment.service.abstraction.ts create mode 100644 libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts create mode 100644 libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts diff --git a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts index 1533645197..93ffb62077 100644 --- a/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts +++ b/apps/browser/src/auth/popup/login-decryption-options/login-decryption-options.component.ts @@ -1,62 +1,20 @@ 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 { 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"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; @Component({ selector: "browser-login-decryption-options", templateUrl: "login-decryption-options.component.html", }) export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { - constructor( - protected formBuilder: FormBuilder, - protected devicesService: DevicesServiceAbstraction, - protected stateService: StateService, - protected router: Router, - protected activatedRoute: ActivatedRoute, - 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 - ); + override async createUser(): Promise { + try { + await super.createUser(); + BrowserApi.closeBitwardenExtensionTab(); + } catch (error) { + this.validationService.showError(error); + } } } diff --git a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts index 3dc9b416f9..f331db3986 100644 --- a/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts +++ b/apps/desktop/src/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -1,62 +1,18 @@ 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 { 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({ selector: "desktop-login-decryption-options", templateUrl: "login-decryption-options.component.html", }) export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { - constructor( - protected formBuilder: FormBuilder, - protected devicesService: DevicesServiceAbstraction, - protected stateService: StateService, - protected router: Router, - protected activatedRoute: ActivatedRoute, - 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 - ); + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } } } diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html index 72105340e0..187bab065f 100644 --- a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.html @@ -88,7 +88,7 @@ buttonType="primary" block class="tw-mb-6" - [bitAction]="createUser" + [bitAction]="createUserAction" > {{ "continue" | i18n }} diff --git a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts index 076bef38f7..2c97bd227f 100644 --- a/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts +++ b/apps/web/src/app/auth/login/login-decryption-options/login-decryption-options.component.ts @@ -1,61 +1,21 @@ 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 { 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({ selector: "web-login-decryption-options", templateUrl: "login-decryption-options.component.html", }) export class LoginDecryptionOptionsComponent extends BaseLoginDecryptionOptionsComponent { - constructor( - protected formBuilder: FormBuilder, - protected devicesService: DevicesServiceAbstraction, - protected stateService: StateService, - protected router: Router, - protected activatedRoute: ActivatedRoute, - 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 - ); + override async createUser(): Promise { + try { + await super.createUser(); + await this.router.navigate(["/vault"]); + } catch (error) { + this.validationService.showError(error); + } } + + createUserAction = async (): Promise => { + return this.createUser(); + }; } diff --git a/libs/angular/src/auth/components/base-login-decryption-options.component.ts b/libs/angular/src/auth/components/base-login-decryption-options.component.ts index 05dbcc2cfd..d04157b776 100644 --- a/libs/angular/src/auth/components/base-login-decryption-options.component.ts +++ b/libs/angular/src/auth/components/base-login-decryption-options.component.ts @@ -16,10 +16,10 @@ import { 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 { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests"; 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 { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; 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 { StateService } from "@bitwarden/common/platform/abstractions/state.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 { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { UserKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; enum State { NewUser, @@ -88,7 +85,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { protected i18nService: I18nService, protected validationService: ValidationService, protected deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction, - protected platformUtilsService: PlatformUtilsService + protected platformUtilsService: PlatformUtilsService, + protected passwordResetEnrollmentService: PasswordResetEnrollmentServiceAbstraction ) {} async ngOnInit() { @@ -240,7 +238,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { this.router.navigate(["/lock"]); } - createUser = async () => { + async createUser() { if (this.data.state !== State.NewUser) { return; } @@ -248,11 +246,11 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { // this.loading to support clients without async-actions-support this.loading = true; try { - const { userKey, publicKey, privateKey } = await this.cryptoService.initAccount(); + const { publicKey, privateKey } = await this.cryptoService.initAccount(); const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString); await this.apiService.postAccountKeys(keysRequest); - await this.passwordResetEnroll(userKey, publicKey, privateKey); + await this.passwordResetEnrollmentService.enroll(this.data.organizationId); if (this.rememberDeviceForm.value.rememberDevice) { await this.deviceTrustCryptoService.trustDevice(); @@ -262,46 +260,7 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { } finally { 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() { this.loading = true; // to avoid an awkward delay in browser extension diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 751b84897f..382ae7343c 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -45,6 +45,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abst import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.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 { 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"; @@ -56,6 +57,7 @@ import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.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 { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.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, deps: [ApiServiceAbstraction], }, + { + provide: PasswordResetEnrollmentServiceAbstraction, + useClass: PasswordResetEnrollmentServiceImplementation, + deps: [ + OrganizationApiServiceAbstraction, + StateServiceAbstraction, + CryptoServiceAbstraction, + OrganizationUserService, + I18nServiceAbstraction, + ], + }, { provide: ProviderServiceAbstraction, useClass: ProviderService, diff --git a/libs/common/src/auth/abstractions/password-reset-enrollment.service.abstraction.ts b/libs/common/src/auth/abstractions/password-reset-enrollment.service.abstraction.ts new file mode 100644 index 0000000000..c9c2af14b8 --- /dev/null +++ b/libs/common/src/auth/abstractions/password-reset-enrollment.service.abstraction.ts @@ -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; + + /** + * 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; +} diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts new file mode 100644 index 0000000000..b4c32cc43c --- /dev/null +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts @@ -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; + let stateService: MockProxy; + let cryptoService: MockProxy; + let organizationUserService: MockProxy; + let i18nService: MockProxy; + let service: PasswordResetEnrollmentServiceImplementation; + + beforeEach(() => { + organizationApiService = mock(); + stateService = mock(); + cryptoService = mock(); + organizationUserService = mock(); + i18nService = mock(); + 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, + }) + ); + }); + }); +}); diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts new file mode 100644 index 0000000000..ef700855d5 --- /dev/null +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts @@ -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; + async enroll(organizationId: string, userId: string, userKey: UserKey): Promise; + async enroll(organizationId: string, userId?: string, userKey?: UserKey): Promise { + 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 + ); + } +}