mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
[PM-23628] Require userId for fetching provider keys (#16993)
* remove getProviderKey and expose providerKeys$ * update consumers
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
||||
import { WrappedSigningKey } from "@bitwarden/common/key-management/types";
|
||||
import { KeySuffixOptions, HashPurpose } from "@bitwarden/common/platform/enums";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
UserKey,
|
||||
MasterKey,
|
||||
@@ -248,17 +248,19 @@ export abstract class KeyService {
|
||||
|
||||
/**
|
||||
* Stores the provider keys for a given user.
|
||||
* @param orgs The provider orgs for which to save the keys from.
|
||||
* @param providers The provider orgs for which to save the keys from.
|
||||
* @param userId The user id of the user for which to store the keys for.
|
||||
*/
|
||||
abstract setProviderKeys(orgs: ProfileProviderResponse[], userId: UserId): Promise<void>;
|
||||
abstract setProviderKeys(providers: ProfileProviderResponse[], userId: UserId): Promise<void>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @throws Error when providerId is null or no active user
|
||||
* @param providerId The desired provider
|
||||
* @returns The provider's symmetric key
|
||||
* Gets an observable of provider keys for the given user.
|
||||
* @param userId The user to get provider keys for.
|
||||
* @return An observable stream of the users providers keys if they are unlocked, or null if the user is not unlocked.
|
||||
* @throws If an invalid user id is passed in.
|
||||
*/
|
||||
abstract getProviderKey(providerId: string): Promise<ProviderKey | null>;
|
||||
abstract providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
FakeSingleUserState,
|
||||
} from "@bitwarden/common/spec";
|
||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
UserKey,
|
||||
MasterKey,
|
||||
@@ -1314,6 +1314,49 @@ describe("keyService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("providerKeys$", () => {
|
||||
let mockUserPrivateKey: Uint8Array;
|
||||
let mockProviderKeys: Record<ProviderId, ProviderKey>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockUserPrivateKey = makeStaticByteArray(64, 1);
|
||||
mockProviderKeys = {
|
||||
["provider1" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64),
|
||||
["provider2" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64),
|
||||
};
|
||||
});
|
||||
|
||||
it("returns null when userPrivateKey is null", async () => {
|
||||
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(null));
|
||||
|
||||
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("returns provider keys when userPrivateKey is available", async () => {
|
||||
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any));
|
||||
jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(mockProviderKeys));
|
||||
|
||||
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
|
||||
|
||||
expect(result).toEqual(mockProviderKeys);
|
||||
expect((keyService as any).providerKeysHelper$).toHaveBeenCalledWith(
|
||||
mockUserId,
|
||||
mockUserPrivateKey,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns null when providerKeysHelper$ returns null", async () => {
|
||||
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any));
|
||||
jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(null));
|
||||
|
||||
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("makeKeyPair", () => {
|
||||
test.each([null as unknown as SymmetricCryptoKey, undefined as unknown as SymmetricCryptoKey])(
|
||||
"throws when the provided key is %s",
|
||||
|
||||
@@ -426,20 +426,16 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Deprecate in favor of observable
|
||||
async getProviderKey(providerId: ProviderId): Promise<ProviderKey | null> {
|
||||
if (providerId == null) {
|
||||
return null;
|
||||
}
|
||||
providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null> {
|
||||
return this.userPrivateKey$(userId).pipe(
|
||||
switchMap((userPrivateKey) => {
|
||||
if (userPrivateKey == null) {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$);
|
||||
if (activeUserId == null) {
|
||||
throw new Error("No active user found.");
|
||||
}
|
||||
|
||||
const providerKeys = await firstValueFrom(this.providerKeys$(activeUserId));
|
||||
|
||||
return providerKeys?.[providerId] ?? null;
|
||||
return this.providerKeysHelper$(userId, userPrivateKey);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private async clearProviderKeys(userId: UserId): Promise<void> {
|
||||
@@ -829,18 +825,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
)) as UserPrivateKey;
|
||||
}
|
||||
|
||||
providerKeys$(userId: UserId) {
|
||||
return this.userPrivateKey$(userId).pipe(
|
||||
switchMap((userPrivateKey) => {
|
||||
if (userPrivateKey == null) {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
return this.providerKeysHelper$(userId, userPrivateKey);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper for decrypting provider keys that requires a user id and that users decrypted private key
|
||||
* this is helpful for when you may have already grabbed the user private key and don't want to redo
|
||||
|
||||
Reference in New Issue
Block a user