mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-05 18:47:52 +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 { 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<void> {
|
||||
try {
|
||||
await super.createUser();
|
||||
BrowserApi.closeBitwardenExtensionTab();
|
||||
} catch (error) {
|
||||
this.validationService.showError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<void> {
|
||||
try {
|
||||
await super.createUser();
|
||||
await this.router.navigate(["/vault"]);
|
||||
} catch (error) {
|
||||
this.validationService.showError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@
|
||||
buttonType="primary"
|
||||
block
|
||||
class="tw-mb-6"
|
||||
[bitAction]="createUser"
|
||||
[bitAction]="createUserAction"
|
||||
>
|
||||
{{ "continue" | i18n }}
|
||||
</button>
|
||||
|
@ -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<void> {
|
||||
try {
|
||||
await super.createUser();
|
||||
await this.router.navigate(["/vault"]);
|
||||
} catch (error) {
|
||||
this.validationService.showError(error);
|
||||
}
|
||||
}
|
||||
|
||||
createUserAction = async (): Promise<void> => {
|
||||
return this.createUser();
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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