mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
feat(auth-tech-debt): [PM-24103] Remove Get User Key to UserKey$ (#16589)
* fix(auth-tech-debt): [PM-24103] Remove Get User Key to UserKey$ - Fixed and updated tests. * fix(auth-tech-debt): [PM-24103] Remove Get User Key to UserKey$ - Fixed test variable being made more vague.
This commit is contained in:
committed by
GitHub
parent
9b2fbdba1c
commit
94cb1fe07b
@@ -886,6 +886,7 @@ export default class MainBackground {
|
|||||||
this.apiService,
|
this.apiService,
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
this.authRequestApiService,
|
this.authRequestApiService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||||
|
|||||||
@@ -621,7 +621,7 @@ export class LoginCommand {
|
|||||||
const newPasswordHash = await this.keyService.hashMasterKey(masterPassword, newMasterKey);
|
const newPasswordHash = await this.keyService.hashMasterKey(masterPassword, newMasterKey);
|
||||||
|
|
||||||
// Grab user key
|
// Grab user key
|
||||||
const userKey = await this.keyService.getUserKey();
|
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
if (!userKey) {
|
if (!userKey) {
|
||||||
throw new Error("User key not found.");
|
throw new Error("User key not found.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -657,6 +657,7 @@ export class ServiceContainer {
|
|||||||
this.apiService,
|
this.apiService,
|
||||||
this.stateProvider,
|
this.stateProvider,
|
||||||
this.authRequestApiService,
|
this.authRequestApiService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { CsprngArray } from "@bitwarden/common/types/csprng";
|
|||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { UserKey, MasterKey, UserPrivateKey } from "@bitwarden/common/types/key";
|
import { UserKey, MasterKey, UserPrivateKey } from "@bitwarden/common/types/key";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
|
import { newGuid } from "@bitwarden/guid";
|
||||||
import { Argon2KdfConfig, KdfType, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management";
|
import { Argon2KdfConfig, KdfType, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
|
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
|
||||||
@@ -44,6 +45,7 @@ describe("EmergencyAccessService", () => {
|
|||||||
|
|
||||||
const mockNewUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
|
const mockNewUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
|
||||||
const mockTrustedPublicKeys = [Utils.fromUtf8ToArray("trustedPublicKey")];
|
const mockTrustedPublicKeys = [Utils.fromUtf8ToArray("trustedPublicKey")];
|
||||||
|
const mockUserId = newGuid() as UserId;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
emergencyAccessApiService = mock<EmergencyAccessApiService>();
|
emergencyAccessApiService = mock<EmergencyAccessApiService>();
|
||||||
@@ -125,7 +127,7 @@ describe("EmergencyAccessService", () => {
|
|||||||
"mockUserPublicKeyEncryptedUserKey",
|
"mockUserPublicKeyEncryptedUserKey",
|
||||||
);
|
);
|
||||||
|
|
||||||
keyService.getUserKey.mockResolvedValueOnce(mockUserKey);
|
keyService.userKey$.mockReturnValue(of(mockUserKey));
|
||||||
|
|
||||||
encryptService.encapsulateKeyUnsigned.mockResolvedValueOnce(
|
encryptService.encapsulateKeyUnsigned.mockResolvedValueOnce(
|
||||||
mockUserPublicKeyEncryptedUserKey,
|
mockUserPublicKeyEncryptedUserKey,
|
||||||
@@ -134,7 +136,7 @@ describe("EmergencyAccessService", () => {
|
|||||||
emergencyAccessApiService.postEmergencyAccessConfirm.mockResolvedValueOnce();
|
emergencyAccessApiService.postEmergencyAccessConfirm.mockResolvedValueOnce();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await emergencyAccessService.confirm(id, granteeId, publicKey);
|
await emergencyAccessService.confirm(id, granteeId, publicKey, mockUserId);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(emergencyAccessApiService.postEmergencyAccessConfirm).toHaveBeenCalledWith(id, {
|
expect(emergencyAccessApiService.postEmergencyAccessConfirm).toHaveBeenCalledWith(id, {
|
||||||
|
|||||||
@@ -175,11 +175,17 @@ export class EmergencyAccessService
|
|||||||
* 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 granteeId id of the grantee
|
||||||
* @param publicKey public key of grantee
|
* @param publicKey public key of grantee
|
||||||
|
* @param activeUserId the active user's id
|
||||||
*/
|
*/
|
||||||
async confirm(id: string, granteeId: string, publicKey: Uint8Array): Promise<void> {
|
async confirm(
|
||||||
const userKey = await this.keyService.getUserKey();
|
id: string,
|
||||||
|
granteeId: string,
|
||||||
|
publicKey: Uint8Array,
|
||||||
|
activeUserId: UserId,
|
||||||
|
): Promise<void> {
|
||||||
|
const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId));
|
||||||
if (!userKey) {
|
if (!userKey) {
|
||||||
throw new Error("No user key found");
|
throw new Error("No user key found");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { lastValueFrom, Observable, firstValueFrom, switchMap } from "rxjs";
|
import { lastValueFrom, Observable, firstValueFrom, switchMap, map } from "rxjs";
|
||||||
|
|
||||||
import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge";
|
import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge";
|
||||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||||
@@ -165,7 +165,15 @@ export class EmergencyAccessComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
const result = await lastValueFrom(dialogRef.closed);
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
if (result === EmergencyAccessConfirmDialogResult.Confirmed) {
|
if (result === EmergencyAccessConfirmDialogResult.Confirmed) {
|
||||||
await this.emergencyAccessService.confirm(contact.id, contact.granteeId, publicKey);
|
const activeUserId = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(getUserId),
|
||||||
|
);
|
||||||
|
await this.emergencyAccessService.confirm(
|
||||||
|
contact.id,
|
||||||
|
contact.granteeId,
|
||||||
|
publicKey,
|
||||||
|
activeUserId,
|
||||||
|
);
|
||||||
updateUser();
|
updateUser();
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
@@ -176,10 +184,14 @@ export class EmergencyAccessComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeUserId = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||||
|
);
|
||||||
this.actionPromise = this.emergencyAccessService.confirm(
|
this.actionPromise = this.emergencyAccessService.confirm(
|
||||||
contact.id,
|
contact.id,
|
||||||
contact.granteeId,
|
contact.granteeId,
|
||||||
publicKey,
|
publicKey,
|
||||||
|
activeUserId,
|
||||||
);
|
);
|
||||||
await this.actionPromise;
|
await this.actionPromise;
|
||||||
updateUser();
|
updateUser();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class SetPinComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||||
const userKey = await this.keyService.getUserKey();
|
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
|
|
||||||
const userKeyEncryptedPin = await this.pinService.createUserKeyEncryptedPin(
|
const userKeyEncryptedPin = await this.pinService.createUserKeyEncryptedPin(
|
||||||
pinFormControl.value,
|
pinFormControl.value,
|
||||||
|
|||||||
@@ -1291,6 +1291,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
StateProvider,
|
StateProvider,
|
||||||
AuthRequestApiServiceAbstraction,
|
AuthRequestApiServiceAbstraction,
|
||||||
|
AccountServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export abstract class AuthRequestServiceAbstraction {
|
|||||||
* Approve or deny an auth request.
|
* Approve or deny an auth request.
|
||||||
* @param approve True to approve, false to deny.
|
* @param approve True to approve, false to deny.
|
||||||
* @param authRequest The auth request to approve or deny, must have an id and key.
|
* @param authRequest The auth request to approve or deny, must have an id and key.
|
||||||
|
* @param activeUserId the active user id
|
||||||
* @returns The updated auth request, the `requestApproved` field will be true if
|
* @returns The updated auth request, the `requestApproved` field will be true if
|
||||||
* approval was successful.
|
* approval was successful.
|
||||||
* @throws If the auth request is missing an id or key.
|
* @throws If the auth request is missing an id or key.
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ describe("LoginStrategy", () => {
|
|||||||
const tokenResponse = identityTokenResponseFactory();
|
const tokenResponse = identityTokenResponseFactory();
|
||||||
tokenResponse.privateKey = null;
|
tokenResponse.privateKey = null;
|
||||||
keyService.makeKeyPair.mockResolvedValue(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]);
|
keyService.makeKeyPair.mockResolvedValue(["PUBLIC_KEY", new EncString("PRIVATE_KEY")]);
|
||||||
keyService.getUserKey.mockResolvedValue(userKey);
|
keyService.userKey$.mockReturnValue(new BehaviorSubject<UserKey>(userKey).asObservable());
|
||||||
|
|
||||||
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
|
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
|
||||||
masterPasswordService.masterKeySubject.next(masterKey);
|
masterPasswordService.masterKeySubject.next(masterKey);
|
||||||
@@ -356,9 +356,11 @@ describe("LoginStrategy", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("throws if userKey is CoseEncrypt0 (V2 encryption) in createKeyPairForOldAccount", async () => {
|
it("throws if userKey is CoseEncrypt0 (V2 encryption) in createKeyPairForOldAccount", async () => {
|
||||||
keyService.getUserKey.mockResolvedValue({
|
keyService.userKey$.mockReturnValue(
|
||||||
inner: () => ({ type: 7 }),
|
new BehaviorSubject<UserKey>({
|
||||||
} as UserKey);
|
inner: () => ({ type: 7 }),
|
||||||
|
} as unknown as UserKey).asObservable(),
|
||||||
|
);
|
||||||
await expect(passwordLoginStrategy["createKeyPairForOldAccount"](userId)).resolves.toBe(
|
await expect(passwordLoginStrategy["createKeyPairForOldAccount"](userId)).resolves.toBe(
|
||||||
undefined,
|
undefined,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -306,7 +306,11 @@ export abstract class LoginStrategy {
|
|||||||
|
|
||||||
protected async createKeyPairForOldAccount(userId: UserId) {
|
protected async createKeyPairForOldAccount(userId: UserId) {
|
||||||
try {
|
try {
|
||||||
const userKey = await this.keyService.getUserKey(userId);
|
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
|
if (userKey === null) {
|
||||||
|
throw new Error("User key is null when creating key pair for old account");
|
||||||
|
}
|
||||||
|
|
||||||
if (userKey.inner().type == EncryptionType.CoseEncrypt0) {
|
if (userKey.inner().type == EncryptionType.CoseEncrypt0) {
|
||||||
throw new Error("Cannot create key pair for account on V2 encryption");
|
throw new Error("Cannot create key pair for account on V2 encryption");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, of } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
@@ -9,11 +10,11 @@ import { FakeMasterPasswordService } from "@bitwarden/common/key-management/mast
|
|||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
|
import { AuthRequestPushNotification } from "@bitwarden/common/models/response/notification.response";
|
||||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
|
import { newGuid } from "@bitwarden/guid";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { DefaultAuthRequestApiService } from "./auth-request-api.service";
|
import { DefaultAuthRequestApiService } from "./auth-request-api.service";
|
||||||
@@ -29,10 +30,11 @@ describe("AuthRequestService", () => {
|
|||||||
const encryptService = mock<EncryptService>();
|
const encryptService = mock<EncryptService>();
|
||||||
const apiService = mock<ApiService>();
|
const apiService = mock<ApiService>();
|
||||||
const authRequestApiService = mock<DefaultAuthRequestApiService>();
|
const authRequestApiService = mock<DefaultAuthRequestApiService>();
|
||||||
|
const accountService = mock<AccountService>();
|
||||||
|
|
||||||
let mockPrivateKey: Uint8Array;
|
let mockPrivateKey: Uint8Array;
|
||||||
let mockPublicKey: Uint8Array;
|
let mockPublicKey: Uint8Array;
|
||||||
const mockUserId = Utils.newGuid() as UserId;
|
const mockUserId = newGuid() as UserId;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -46,6 +48,7 @@ describe("AuthRequestService", () => {
|
|||||||
apiService,
|
apiService,
|
||||||
stateProvider,
|
stateProvider,
|
||||||
authRequestApiService,
|
authRequestApiService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
mockPrivateKey = new Uint8Array(64);
|
mockPrivateKey = new Uint8Array(64);
|
||||||
@@ -95,6 +98,8 @@ describe("AuthRequestService", () => {
|
|||||||
const authRequestNoId = new AuthRequestResponse({ id: "", key: "KEY" });
|
const authRequestNoId = new AuthRequestResponse({ id: "", key: "KEY" });
|
||||||
const authRequestNoPublicKey = new AuthRequestResponse({ id: "123", publicKey: "" });
|
const authRequestNoPublicKey = new AuthRequestResponse({ id: "123", publicKey: "" });
|
||||||
|
|
||||||
|
accountService.activeAccount$ = of({ id: mockUserId } as any);
|
||||||
|
|
||||||
await expect(sut.approveOrDenyAuthRequest(true, authRequestNoId)).rejects.toThrow(
|
await expect(sut.approveOrDenyAuthRequest(true, authRequestNoId)).rejects.toThrow(
|
||||||
"Auth request has no id",
|
"Auth request has no id",
|
||||||
);
|
);
|
||||||
@@ -104,8 +109,9 @@ describe("AuthRequestService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should use the user key if the master key and hash do not exist", async () => {
|
it("should use the user key if the master key and hash do not exist", async () => {
|
||||||
keyService.getUserKey.mockResolvedValueOnce(
|
accountService.activeAccount$ = of({ id: mockUserId } as any);
|
||||||
new SymmetricCryptoKey(new Uint8Array(64)) as UserKey,
|
keyService.userKey$.mockReturnValue(
|
||||||
|
of(new SymmetricCryptoKey(new Uint8Array(64)) as UserKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
await sut.approveOrDenyAuthRequest(
|
await sut.approveOrDenyAuthRequest(
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import { Observable, Subject, defer, firstValueFrom, map } from "rxjs";
|
|||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
|
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
|
||||||
import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request";
|
import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request";
|
||||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||||
@@ -56,6 +58,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private authRequestApiService: AuthRequestApiServiceAbstraction,
|
private authRequestApiService: AuthRequestApiServiceAbstraction,
|
||||||
|
private accountService: AccountService,
|
||||||
) {
|
) {
|
||||||
this.authRequestPushNotification$ = this.authRequestPushNotificationSubject.asObservable();
|
this.authRequestPushNotification$ = this.authRequestPushNotificationSubject.asObservable();
|
||||||
this.adminLoginApproved$ = this.adminLoginApprovedSubject.asObservable();
|
this.adminLoginApproved$ = this.adminLoginApprovedSubject.asObservable();
|
||||||
@@ -124,15 +127,19 @@ export class AuthRequestService implements AuthRequestServiceAbstraction {
|
|||||||
approve: boolean,
|
approve: boolean,
|
||||||
authRequest: AuthRequestResponse,
|
authRequest: AuthRequestResponse,
|
||||||
): Promise<AuthRequestResponse> {
|
): Promise<AuthRequestResponse> {
|
||||||
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
|
||||||
if (!authRequest.id) {
|
if (!authRequest.id) {
|
||||||
throw new Error("Auth request has no id");
|
throw new Error("Auth request has no id");
|
||||||
}
|
}
|
||||||
if (!authRequest.publicKey) {
|
if (!authRequest.publicKey) {
|
||||||
throw new Error("Auth request has no public key");
|
throw new Error("Auth request has no public key");
|
||||||
}
|
}
|
||||||
|
if (activeUserId == null) {
|
||||||
|
throw new Error("User ID is required");
|
||||||
|
}
|
||||||
const pubKey = Utils.fromB64ToArray(authRequest.publicKey);
|
const pubKey = Utils.fromB64ToArray(authRequest.publicKey);
|
||||||
|
const keyToEncrypt = await firstValueFrom(this.keyService.userKey$(activeUserId));
|
||||||
const keyToEncrypt = await this.keyService.getUserKey();
|
|
||||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(keyToEncrypt, pubKey);
|
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(keyToEncrypt, pubKey);
|
||||||
|
|
||||||
const response = new PasswordlessAuthRequest(
|
const response = new PasswordlessAuthRequest(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject, of } from "rxjs";
|
||||||
|
|
||||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
@@ -103,7 +103,7 @@ describe("PasswordResetEnrollmentServiceImplementation", () => {
|
|||||||
};
|
};
|
||||||
activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId }));
|
activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId }));
|
||||||
|
|
||||||
keyService.getUserKey.mockResolvedValue({ key: "key" } as any);
|
keyService.userKey$.mockReturnValue(of({ key: "key" } as any));
|
||||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedKey as any);
|
encryptService.encapsulateKeyUnsigned.mockResolvedValue(encryptedKey as any);
|
||||||
|
|
||||||
await service.enroll("orgId");
|
await service.enroll("orgId");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
@@ -8,9 +8,11 @@ import {
|
|||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
OrganizationUserResetPasswordEnrollmentRequest,
|
OrganizationUserResetPasswordEnrollmentRequest,
|
||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
import { UserId } from "@bitwarden/user-core";
|
||||||
|
|
||||||
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
||||||
@@ -43,7 +45,7 @@ export class PasswordResetEnrollmentServiceImplementation
|
|||||||
|
|
||||||
async enroll(organizationId: string): Promise<void>;
|
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>;
|
||||||
async enroll(organizationId: string, userId?: string, userKey?: UserKey): Promise<void> {
|
async enroll(organizationId: string, activeUserId?: string, userKey?: UserKey): Promise<void> {
|
||||||
const orgKeyResponse = await this.organizationApiService.getKeys(organizationId);
|
const orgKeyResponse = await this.organizationApiService.getKeys(organizationId);
|
||||||
if (orgKeyResponse == null) {
|
if (orgKeyResponse == null) {
|
||||||
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
throw new Error(this.i18nService.t("resetPasswordOrgKeysError"));
|
||||||
@@ -51,9 +53,12 @@ export class PasswordResetEnrollmentServiceImplementation
|
|||||||
|
|
||||||
const orgPublicKey = Utils.fromB64ToArray(orgKeyResponse.publicKey);
|
const orgPublicKey = Utils.fromB64ToArray(orgKeyResponse.publicKey);
|
||||||
|
|
||||||
userId =
|
activeUserId =
|
||||||
userId ?? (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))));
|
activeUserId ?? (await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)));
|
||||||
userKey = userKey ?? (await this.keyService.getUserKey(userId));
|
if (activeUserId == null) {
|
||||||
|
throw new Error("User ID is required");
|
||||||
|
}
|
||||||
|
userKey = userKey ?? (await firstValueFrom(this.keyService.userKey$(activeUserId as UserId)));
|
||||||
// RSA Encrypt user's userKey.key with organization public key
|
// RSA Encrypt user's userKey.key with organization public key
|
||||||
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, orgPublicKey);
|
const encryptedKey = await this.encryptService.encapsulateKeyUnsigned(userKey, orgPublicKey);
|
||||||
|
|
||||||
@@ -62,7 +67,7 @@ export class PasswordResetEnrollmentServiceImplementation
|
|||||||
|
|
||||||
await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment(
|
await this.organizationUserApiService.putOrganizationUserResetPasswordEnrollment(
|
||||||
organizationId,
|
organizationId,
|
||||||
userId,
|
activeUserId,
|
||||||
resetRequest,
|
resetRequest,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user