mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 05:13:29 +00:00
[PM-23626] Require userId for makeOrgKey on the key service (#15864)
* Update key service * Update consumers * Add unit test coverage for consumer services * Add unit test coverage for organization-billing service
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { OrganizationResponse } from "../../admin-console/models/response/organization.response";
|
||||
import { InitiationPath } from "../../models/request/reference-event.request";
|
||||
import { PaymentMethodType, PlanType } from "../enums";
|
||||
@@ -47,16 +49,22 @@ export abstract class OrganizationBillingServiceAbstraction {
|
||||
|
||||
abstract purchaseSubscription(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse>;
|
||||
|
||||
abstract purchaseSubscriptionNoPaymentMethod(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse>;
|
||||
|
||||
abstract startFree(subscription: SubscriptionInformation): Promise<OrganizationResponse>;
|
||||
abstract startFree(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse>;
|
||||
|
||||
abstract restartSubscription(
|
||||
organizationId: string,
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction as OrganizationApiService } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import {
|
||||
BillingApiServiceAbstraction,
|
||||
PaymentInformation,
|
||||
SubscriptionInformation,
|
||||
} from "@bitwarden/common/billing/abstractions";
|
||||
import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums";
|
||||
@@ -11,12 +12,16 @@ import { OrganizationBillingService } from "@bitwarden/common/billing/services/o
|
||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { newGuid } from "@bitwarden/guid";
|
||||
// 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
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { OrganizationKeysRequest } from "../../admin-console/models/request/organization-keys.request";
|
||||
import { OrganizationResponse } from "../../admin-console/models/response/organization.response";
|
||||
import { EncString } from "../../key-management/crypto/models/enc-string";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { OrgKey } from "../../types/key";
|
||||
import { PaymentMethodResponse } from "../models/response/payment-method.response";
|
||||
|
||||
@@ -31,6 +36,8 @@ describe("OrganizationBillingService", () => {
|
||||
|
||||
let sut: OrganizationBillingService;
|
||||
|
||||
const mockUserId = newGuid() as UserId;
|
||||
|
||||
beforeEach(() => {
|
||||
apiService = mock<ApiService>();
|
||||
billingApiService = mock<BillingApiServiceAbstraction>();
|
||||
@@ -115,12 +122,12 @@ describe("OrganizationBillingService", () => {
|
||||
} as OrganizationResponse;
|
||||
|
||||
organizationApiService.create.mockResolvedValue(organizationResponse);
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypyted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypyted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypyted"));
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
|
||||
//Act
|
||||
const response = await sut.purchaseSubscription(subscriptionInformation);
|
||||
const response = await sut.purchaseSubscription(subscriptionInformation, mockUserId);
|
||||
|
||||
//Assert
|
||||
expect(organizationApiService.create).toHaveBeenCalledTimes(1);
|
||||
@@ -141,10 +148,10 @@ describe("OrganizationBillingService", () => {
|
||||
organizationApiService.create.mockRejectedValue(new Error("Failed to create organization"));
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypyted"));
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
|
||||
// Act & Assert
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation)).rejects.toThrow(
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation, mockUserId)).rejects.toThrow(
|
||||
"Failed to create organization",
|
||||
);
|
||||
});
|
||||
@@ -163,7 +170,7 @@ describe("OrganizationBillingService", () => {
|
||||
keyService.makeOrgKey.mockRejectedValue(new Error("Key generation failed"));
|
||||
|
||||
// Act & Assert
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation)).rejects.toThrow(
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation, mockUserId)).rejects.toThrow(
|
||||
"Key generation failed",
|
||||
);
|
||||
});
|
||||
@@ -180,7 +187,7 @@ describe("OrganizationBillingService", () => {
|
||||
} as SubscriptionInformation;
|
||||
|
||||
// Act & Assert
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation)).rejects.toThrow();
|
||||
await expect(sut.purchaseSubscription(subscriptionInformation, mockUserId)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -204,7 +211,10 @@ describe("OrganizationBillingService", () => {
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
|
||||
//Act
|
||||
const response = await sut.purchaseSubscriptionNoPaymentMethod(subscriptionInformation);
|
||||
const response = await sut.purchaseSubscriptionNoPaymentMethod(
|
||||
subscriptionInformation,
|
||||
mockUserId,
|
||||
);
|
||||
|
||||
//Assert
|
||||
expect(organizationApiService.createWithoutPayment).toHaveBeenCalledTimes(1);
|
||||
@@ -223,7 +233,7 @@ describe("OrganizationBillingService", () => {
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
|
||||
await expect(
|
||||
sut.purchaseSubscriptionNoPaymentMethod(subscriptionInformation),
|
||||
sut.purchaseSubscriptionNoPaymentMethod(subscriptionInformation, mockUserId),
|
||||
).rejects.toThrow("Creation failed");
|
||||
});
|
||||
|
||||
@@ -237,7 +247,7 @@ describe("OrganizationBillingService", () => {
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
|
||||
await expect(
|
||||
sut.purchaseSubscriptionNoPaymentMethod(subscriptionInformation),
|
||||
sut.purchaseSubscriptionNoPaymentMethod(subscriptionInformation, mockUserId),
|
||||
).rejects.toThrow("Key generation failed");
|
||||
});
|
||||
});
|
||||
@@ -256,12 +266,12 @@ describe("OrganizationBillingService", () => {
|
||||
} as OrganizationResponse;
|
||||
|
||||
organizationApiService.create.mockResolvedValue(organizationResponse);
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypyted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypyted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypyted"));
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
|
||||
//Act
|
||||
const response = await sut.startFree(subscriptionInformation);
|
||||
const response = await sut.startFree(subscriptionInformation, mockUserId);
|
||||
|
||||
//Assert
|
||||
expect(organizationApiService.create).toHaveBeenCalledTimes(1);
|
||||
@@ -277,7 +287,9 @@ describe("OrganizationBillingService", () => {
|
||||
keyService.makeOrgKey.mockRejectedValue(new Error("Key generation failed"));
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
|
||||
await expect(sut.startFree(subscriptionInformation)).rejects.toThrow("Key generation failed");
|
||||
await expect(sut.startFree(subscriptionInformation, mockUserId)).rejects.toThrow(
|
||||
"Key generation failed",
|
||||
);
|
||||
});
|
||||
|
||||
it("given organization creation fails, then it throws an error", async () => {
|
||||
@@ -290,11 +302,162 @@ describe("OrganizationBillingService", () => {
|
||||
organizationApiService.create.mockRejectedValue(new Error("Failed to create organization"));
|
||||
keyService.makeOrgKey.mockResolvedValue([new EncString("encrypted-key"), {} as OrgKey]);
|
||||
keyService.makeKeyPair.mockResolvedValue(["key", new EncString("encrypted-key")]);
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypyted"));
|
||||
encryptService.encryptString.mockResolvedValue(new EncString("collection-encrypted"));
|
||||
// Act & Assert
|
||||
await expect(sut.startFree(subscriptionInformation)).rejects.toThrow(
|
||||
await expect(sut.startFree(subscriptionInformation, mockUserId)).rejects.toThrow(
|
||||
"Failed to create organization",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("organization key creation methods", () => {
|
||||
const organizationKeys = {
|
||||
orgKey: new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey,
|
||||
publicKeyEncapsulatedOrgKey: new EncString("encryptedOrgKey"),
|
||||
publicKey: "public-key",
|
||||
encryptedPrivateKey: new EncString("encryptedPrivateKey"),
|
||||
};
|
||||
const encryptedCollectionName = new EncString("encryptedCollectionName");
|
||||
const mockSubscription = {
|
||||
organization: {
|
||||
name: "Test Org",
|
||||
businessName: "Test Business",
|
||||
billingEmail: "test@example.com",
|
||||
initiationPath: "Registration form",
|
||||
},
|
||||
plan: {
|
||||
type: 0, // Free plan
|
||||
passwordManagerSeats: 0,
|
||||
subscribeToSecretsManager: false,
|
||||
isFromSecretsManagerTrial: false,
|
||||
},
|
||||
} as SubscriptionInformation;
|
||||
const mockResponse = { id: "org-id" } as OrganizationResponse;
|
||||
|
||||
const expectedRequestObject = {
|
||||
name: "Test Org",
|
||||
businessName: "Test Business",
|
||||
billingEmail: "test@example.com",
|
||||
initiationPath: "Registration form",
|
||||
planType: 0,
|
||||
key: organizationKeys.publicKeyEncapsulatedOrgKey.encryptedString,
|
||||
keys: new OrganizationKeysRequest(
|
||||
organizationKeys.publicKey,
|
||||
organizationKeys.encryptedPrivateKey.encryptedString!,
|
||||
),
|
||||
collectionName: encryptedCollectionName.encryptedString,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
keyService.makeOrgKey.mockResolvedValue([
|
||||
organizationKeys.publicKeyEncapsulatedOrgKey,
|
||||
organizationKeys.orgKey,
|
||||
]);
|
||||
keyService.makeKeyPair.mockResolvedValue([
|
||||
organizationKeys.publicKey,
|
||||
organizationKeys.encryptedPrivateKey,
|
||||
]);
|
||||
encryptService.encryptString.mockResolvedValueOnce(encryptedCollectionName);
|
||||
i18nService.t.mockReturnValue("Default Collection");
|
||||
|
||||
organizationApiService.create.mockResolvedValue(mockResponse);
|
||||
});
|
||||
|
||||
describe("purchaseSubscription", () => {
|
||||
it("sets the correct organization keys on the organization creation request", async () => {
|
||||
const subscriptionWithPayment = {
|
||||
...mockSubscription,
|
||||
payment: {
|
||||
paymentMethod: ["test-token", PaymentMethodType.Card],
|
||||
billing: {
|
||||
postalCode: "12345",
|
||||
country: "US",
|
||||
},
|
||||
} as PaymentInformation,
|
||||
} as SubscriptionInformation;
|
||||
const result = await sut.purchaseSubscription(subscriptionWithPayment, mockUserId);
|
||||
|
||||
expect(keyService.makeOrgKey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(organizationKeys.orgKey);
|
||||
expect(encryptService.encryptString).toHaveBeenCalledWith(
|
||||
"Default Collection",
|
||||
organizationKeys.orgKey,
|
||||
);
|
||||
expect(organizationApiService.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining(expectedRequestObject),
|
||||
);
|
||||
expect(apiService.refreshIdentityToken).toHaveBeenCalled();
|
||||
expect(syncService.fullSync).toHaveBeenCalledWith(true);
|
||||
expect(result).toBe(mockResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe("purchaseSubscriptionNoPaymentMethod", () => {
|
||||
it("sets the correct organization keys on the organization creation request", async () => {
|
||||
organizationApiService.createWithoutPayment.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await sut.purchaseSubscriptionNoPaymentMethod(mockSubscription, mockUserId);
|
||||
|
||||
expect(keyService.makeOrgKey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(organizationKeys.orgKey);
|
||||
expect(encryptService.encryptString).toHaveBeenCalledWith(
|
||||
"Default Collection",
|
||||
organizationKeys.orgKey,
|
||||
);
|
||||
expect(organizationApiService.createWithoutPayment).toHaveBeenCalledWith(
|
||||
expect.objectContaining(expectedRequestObject),
|
||||
);
|
||||
expect(apiService.refreshIdentityToken).toHaveBeenCalled();
|
||||
expect(syncService.fullSync).toHaveBeenCalledWith(true);
|
||||
expect(result).toBe(mockResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe("startFree", () => {
|
||||
it("sets the correct organization keys on the organization creation request", async () => {
|
||||
const result = await sut.startFree(mockSubscription, mockUserId);
|
||||
|
||||
expect(keyService.makeOrgKey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(organizationKeys.orgKey);
|
||||
expect(encryptService.encryptString).toHaveBeenCalledWith(
|
||||
"Default Collection",
|
||||
organizationKeys.orgKey,
|
||||
);
|
||||
expect(organizationApiService.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining(expectedRequestObject),
|
||||
);
|
||||
expect(apiService.refreshIdentityToken).toHaveBeenCalled();
|
||||
expect(syncService.fullSync).toHaveBeenCalledWith(true);
|
||||
expect(result).toBe(mockResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe("restartSubscription", () => {
|
||||
it("sets the correct organization keys on the organization creation request", async () => {
|
||||
const subscriptionWithPayment = {
|
||||
...mockSubscription,
|
||||
payment: {
|
||||
paymentMethod: ["test-token", PaymentMethodType.Card],
|
||||
billing: {
|
||||
postalCode: "12345",
|
||||
country: "US",
|
||||
},
|
||||
} as PaymentInformation,
|
||||
} as SubscriptionInformation;
|
||||
|
||||
await sut.restartSubscription("org-id", subscriptionWithPayment, mockUserId);
|
||||
|
||||
expect(keyService.makeOrgKey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyService.makeKeyPair).toHaveBeenCalledWith(organizationKeys.orgKey);
|
||||
expect(encryptService.encryptString).toHaveBeenCalledWith(
|
||||
"Default Collection",
|
||||
organizationKeys.orgKey,
|
||||
);
|
||||
expect(billingApiService.restartSubscription).toHaveBeenCalledWith(
|
||||
"org-id",
|
||||
expect.objectContaining(expectedRequestObject),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// 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
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction as OrganizationApiService } from "../../admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
@@ -49,10 +50,13 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
return paymentMethod?.paymentSource;
|
||||
}
|
||||
|
||||
async purchaseSubscription(subscription: SubscriptionInformation): Promise<OrganizationResponse> {
|
||||
async purchaseSubscription(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse> {
|
||||
const request = new OrganizationCreateRequest();
|
||||
|
||||
const organizationKeys = await this.makeOrganizationKeys();
|
||||
const organizationKeys = await this.makeOrganizationKeys(activeUserId);
|
||||
|
||||
this.setOrganizationKeys(request, organizationKeys);
|
||||
|
||||
@@ -73,10 +77,11 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
|
||||
async purchaseSubscriptionNoPaymentMethod(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse> {
|
||||
const request = new OrganizationNoPaymentMethodCreateRequest();
|
||||
|
||||
const organizationKeys = await this.makeOrganizationKeys();
|
||||
const organizationKeys = await this.makeOrganizationKeys(activeUserId);
|
||||
|
||||
this.setOrganizationKeys(request, organizationKeys);
|
||||
|
||||
@@ -93,10 +98,13 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
return response;
|
||||
}
|
||||
|
||||
async startFree(subscription: SubscriptionInformation): Promise<OrganizationResponse> {
|
||||
async startFree(
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<OrganizationResponse> {
|
||||
const request = new OrganizationCreateRequest();
|
||||
|
||||
const organizationKeys = await this.makeOrganizationKeys();
|
||||
const organizationKeys = await this.makeOrganizationKeys(activeUserId);
|
||||
|
||||
this.setOrganizationKeys(request, organizationKeys);
|
||||
|
||||
@@ -113,8 +121,8 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
return response;
|
||||
}
|
||||
|
||||
private async makeOrganizationKeys(): Promise<OrganizationKeys> {
|
||||
const [encryptedKey, key] = await this.keyService.makeOrgKey<OrgKey>();
|
||||
private async makeOrganizationKeys(activeUserId: UserId): Promise<OrganizationKeys> {
|
||||
const [encryptedKey, key] = await this.keyService.makeOrgKey<OrgKey>(activeUserId);
|
||||
const [publicKey, encryptedPrivateKey] = await this.keyService.makeKeyPair(key);
|
||||
const encryptedCollectionName = await this.encryptService.encryptString(
|
||||
this.i18nService.t("defaultCollection"),
|
||||
@@ -214,9 +222,10 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
|
||||
async restartSubscription(
|
||||
organizationId: string,
|
||||
subscription: SubscriptionInformation,
|
||||
activeUserId: UserId,
|
||||
): Promise<void> {
|
||||
const request = new OrganizationCreateRequest();
|
||||
const organizationKeys = await this.makeOrganizationKeys();
|
||||
const organizationKeys = await this.makeOrganizationKeys(activeUserId);
|
||||
this.setOrganizationKeys(request, organizationKeys);
|
||||
this.setOrganizationInformation(request, subscription.organization);
|
||||
this.setPlanInformation(request, subscription.plan);
|
||||
|
||||
@@ -259,11 +259,12 @@ export abstract class KeyService {
|
||||
/**
|
||||
* Creates a new organization key and encrypts it with the user's public key.
|
||||
* This method can also return Provider keys for creating new Provider users.
|
||||
*
|
||||
* @throws Error when no active user or user have no public key
|
||||
* @returns The new encrypted org key and the decrypted key itself
|
||||
* @param userId The user id of the target user's public key to use.
|
||||
* @throws Error when userId is null or undefined.
|
||||
* @throws Error when no public key is found for the target user.
|
||||
* @returns The new encrypted OrgKey | ProviderKey and the decrypted key itself
|
||||
*/
|
||||
abstract makeOrgKey<T extends OrgKey | ProviderKey>(): Promise<[EncString, T]>;
|
||||
abstract makeOrgKey<T extends OrgKey | ProviderKey>(userId: UserId): Promise<[EncString, T]>;
|
||||
/**
|
||||
* Sets the user's encrypted private key in storage and
|
||||
* clears the decrypted private key from memory
|
||||
|
||||
@@ -39,7 +39,13 @@ import {
|
||||
} from "@bitwarden/common/spec";
|
||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
||||
import {
|
||||
UserKey,
|
||||
MasterKey,
|
||||
UserPublicKey,
|
||||
OrgKey,
|
||||
ProviderKey,
|
||||
} from "@bitwarden/common/types/key";
|
||||
|
||||
import { KdfConfigService } from "./abstractions/kdf-config.service";
|
||||
import { UserPrivateKeyDecryptionFailedError } from "./abstractions/key.service";
|
||||
@@ -1029,6 +1035,66 @@ describe("keyService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("makeOrgKey", () => {
|
||||
const mockUserPublicKey = new Uint8Array(64) as UserPublicKey;
|
||||
const shareKey = new SymmetricCryptoKey(new Uint8Array(64));
|
||||
const mockEncapsulatedKey = new EncString("mockEncapsulatedKey");
|
||||
|
||||
beforeEach(() => {
|
||||
keyService.userPublicKey$ = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(new BehaviorSubject(mockUserPublicKey));
|
||||
keyGenerationService.createKey.mockResolvedValue(shareKey);
|
||||
encryptService.encapsulateKeyUnsigned.mockResolvedValue(mockEncapsulatedKey);
|
||||
});
|
||||
|
||||
it("creates a new OrgKey and encapsulates it with the user's public key", async () => {
|
||||
const result = await keyService.makeOrgKey<OrgKey>(mockUserId);
|
||||
|
||||
expect(result).toEqual([mockEncapsulatedKey, shareKey as OrgKey]);
|
||||
expect(keyService.userPublicKey$).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyGenerationService.createKey).toHaveBeenCalledWith(512);
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
shareKey,
|
||||
mockUserPublicKey,
|
||||
);
|
||||
});
|
||||
|
||||
it("creates a new ProviderKey and encapsulates it with the user's public key", async () => {
|
||||
const result = await keyService.makeOrgKey<ProviderKey>(mockUserId);
|
||||
|
||||
expect(result).toEqual([mockEncapsulatedKey, shareKey as ProviderKey]);
|
||||
expect(keyService.userPublicKey$).toHaveBeenCalledWith(mockUserId);
|
||||
expect(keyGenerationService.createKey).toHaveBeenCalledWith(512);
|
||||
expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(
|
||||
shareKey,
|
||||
mockUserPublicKey,
|
||||
);
|
||||
});
|
||||
|
||||
test.each([null as unknown as UserId, undefined as unknown as UserId])(
|
||||
"throws when the provided userId is %s",
|
||||
async (userId) => {
|
||||
await expect(keyService.makeOrgKey(userId)).rejects.toThrow("UserId is required");
|
||||
|
||||
expect(keyService.userPublicKey$).not.toHaveBeenCalled();
|
||||
expect(keyGenerationService.createKey).not.toHaveBeenCalled();
|
||||
expect(encryptService.encapsulateKeyUnsigned).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
it("throws if the user's public key is not found", async () => {
|
||||
keyService.userPublicKey$ = jest.fn().mockReturnValueOnce(new BehaviorSubject(null));
|
||||
|
||||
await expect(keyService.makeOrgKey(mockUserId)).rejects.toThrow(
|
||||
"No public key found for user " + mockUserId,
|
||||
);
|
||||
|
||||
expect(keyGenerationService.createKey).not.toHaveBeenCalled();
|
||||
expect(encryptService.encapsulateKeyUnsigned).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("userEncryptionKeyPair$", () => {
|
||||
type SetupKeysParams = {
|
||||
makeMasterKey: boolean;
|
||||
|
||||
@@ -446,19 +446,17 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
await this.stateProvider.setUserState(USER_ENCRYPTED_PROVIDER_KEYS, null, userId);
|
||||
}
|
||||
|
||||
// TODO: Make userId required
|
||||
async makeOrgKey<T extends OrgKey | ProviderKey>(userId?: UserId): Promise<[EncString, T]> {
|
||||
const shareKey = await this.keyGenerationService.createKey(512);
|
||||
userId ??= await firstValueFrom(this.stateProvider.activeUserId$);
|
||||
async makeOrgKey<T extends OrgKey | ProviderKey>(userId: UserId): Promise<[EncString, T]> {
|
||||
if (userId == null) {
|
||||
throw new Error("No active user found.");
|
||||
throw new Error("UserId is required");
|
||||
}
|
||||
|
||||
const publicKey = await firstValueFrom(this.userPublicKey$(userId));
|
||||
if (publicKey == null) {
|
||||
throw new Error("No public key found.");
|
||||
throw new Error("No public key found for user " + userId);
|
||||
}
|
||||
|
||||
const shareKey = await this.keyGenerationService.createKey(512);
|
||||
const encShareKey = await this.encryptService.encapsulateKeyUnsigned(shareKey, publicKey);
|
||||
return [encShareKey, shareKey as T];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user