mirror of
https://github.com/bitwarden/browser
synced 2026-02-08 04:33:38 +00:00
[PM-19479] Client-Managed SDK state definition
This commit is contained in:
@@ -775,6 +775,7 @@ export default class MainBackground {
|
||||
this.accountService,
|
||||
this.kdfConfigService,
|
||||
this.keyService,
|
||||
this.stateProvider,
|
||||
);
|
||||
|
||||
this.passwordStrengthService = new PasswordStrengthService();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { NavigationEnd, Router, RouterOutlet } from "@angular/router";
|
||||
import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs";
|
||||
import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap, map } from "rxjs";
|
||||
|
||||
import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction";
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
@@ -13,6 +13,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { MessageListener } from "@bitwarden/common/platform/messaging";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
@@ -74,10 +75,27 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private biometricsService: BiometricsService,
|
||||
private deviceTrustToastService: DeviceTrustToastService,
|
||||
private popupSizeService: PopupSizeService,
|
||||
private sdkService: SdkService,
|
||||
) {
|
||||
this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe();
|
||||
}
|
||||
|
||||
/* eslint-disable no-console */
|
||||
async testSdkState() {
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||
await firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
map(async (clientRc) => {
|
||||
using clientRef = clientRc.take();
|
||||
const client = clientRef.value;
|
||||
|
||||
const ciphers = await client.vault().print_the_ciphers();
|
||||
console.log("Ciphers2: ", ciphers);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
initPopupClosedListener();
|
||||
|
||||
@@ -200,6 +218,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
.subscribe((state) => {
|
||||
this.routerAnimations = state;
|
||||
});
|
||||
|
||||
this.testSdkState().catch((e) => {
|
||||
console.error("Error in testSdkState()", e);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@@ -599,6 +599,7 @@ export class ServiceContainer {
|
||||
this.accountService,
|
||||
this.kdfConfigService,
|
||||
this.keyService,
|
||||
this.stateProvider,
|
||||
customUserAgent,
|
||||
);
|
||||
|
||||
|
||||
@@ -1446,6 +1446,7 @@ const safeProviders: SafeProvider[] = [
|
||||
AccountServiceAbstraction,
|
||||
KdfConfigService,
|
||||
KeyService,
|
||||
StateProvider,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -12,13 +12,19 @@ import {
|
||||
of,
|
||||
takeWhile,
|
||||
throwIfEmpty,
|
||||
firstValueFrom,
|
||||
} from "rxjs";
|
||||
|
||||
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { ENCRYPTED_CIPHERS } from "@bitwarden/common/vault/services/key-state/ciphers.state";
|
||||
import { KeyService, KdfConfigService, KdfConfig, KdfType } from "@bitwarden/key-management";
|
||||
import {
|
||||
BitwardenClient,
|
||||
ClientSettings,
|
||||
Cipher as SdkCipher,
|
||||
DeviceType as SdkDeviceType,
|
||||
Repository as SdkRepository,
|
||||
} from "@bitwarden/sdk-internal";
|
||||
|
||||
import { EncryptedOrganizationKeyData } from "../../../admin-console/models/data/encrypted-organization-key.data";
|
||||
@@ -34,6 +40,7 @@ import { SdkService, UserNotLoggedInError } from "../../abstractions/sdk/sdk.ser
|
||||
import { compareValues } from "../../misc/compare-values";
|
||||
import { Rc } from "../../misc/reference-counting/rc";
|
||||
import { EncryptedString } from "../../models/domain/enc-string";
|
||||
import { StateProvider, UserKeyDefinition } from "../../state";
|
||||
|
||||
// A symbol that represents an overriden client that is explicitly set to undefined,
|
||||
// blocking the creation of an internal client for that user.
|
||||
@@ -66,6 +73,7 @@ export class DefaultSdkService implements SdkService {
|
||||
private accountService: AccountService,
|
||||
private kdfConfigService: KdfConfigService,
|
||||
private keyService: KeyService,
|
||||
private stateProvider?: StateProvider,
|
||||
private userAgent: string | null = null,
|
||||
) {}
|
||||
|
||||
@@ -150,7 +158,15 @@ export class DefaultSdkService implements SdkService {
|
||||
const settings = this.toSettings(env);
|
||||
const client = await this.sdkClientFactory.createSdkClient(settings);
|
||||
|
||||
await this.initializeClient(client, account, kdfParams, privateKey, userKey, orgKeys);
|
||||
await this.initializeClient(
|
||||
userId,
|
||||
client,
|
||||
account,
|
||||
kdfParams,
|
||||
privateKey,
|
||||
userKey,
|
||||
orgKeys,
|
||||
);
|
||||
|
||||
return client;
|
||||
};
|
||||
@@ -180,6 +196,7 @@ export class DefaultSdkService implements SdkService {
|
||||
}
|
||||
|
||||
private async initializeClient(
|
||||
userId: UserId,
|
||||
client: BitwardenClient,
|
||||
account: AccountInfo,
|
||||
kdfParams: KdfConfig,
|
||||
@@ -214,6 +231,30 @@ export class DefaultSdkService implements SdkService {
|
||||
.map(([k, v]) => [k, v.key]),
|
||||
),
|
||||
});
|
||||
|
||||
if (this.stateProvider) {
|
||||
// Initialize the cipher store
|
||||
client
|
||||
.platform()
|
||||
.repository()
|
||||
.register_cipher_repository(
|
||||
new RepositoryRecordImpl(
|
||||
userId,
|
||||
this.stateProvider,
|
||||
ENCRYPTED_CIPHERS,
|
||||
new CipherMapper(),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await client.vault().print_the_ciphers();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Ciphers: " + result);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error printing ciphers: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private toSettings(env: Environment): ClientSettings {
|
||||
@@ -282,3 +323,60 @@ export class DefaultSdkService implements SdkService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SdkMapper<ClientType, SdkType> {
|
||||
toSdk(value: ClientType): SdkType;
|
||||
fromSdk(value: SdkType): ClientType;
|
||||
}
|
||||
|
||||
class RepositoryRecordImpl<ClientType, SdkType> implements SdkRepository<SdkType> {
|
||||
constructor(
|
||||
private userId: UserId,
|
||||
private stateProvider: StateProvider,
|
||||
private userKeyDefinition: UserKeyDefinition<Record<string, ClientType>>,
|
||||
private mapper: SdkMapper<ClientType, SdkType>,
|
||||
) {}
|
||||
|
||||
async get(id: string): Promise<SdkType | null> {
|
||||
const prov = this.stateProvider.getUser(this.userId, this.userKeyDefinition);
|
||||
const data = await firstValueFrom(prov.state$.pipe(map((data) => data ?? {})));
|
||||
|
||||
const cipher = data[id];
|
||||
if (!cipher) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.mapper.toSdk(cipher);
|
||||
}
|
||||
async list(): Promise<SdkType[]> {
|
||||
const prov = this.stateProvider.getUser(this.userId, this.userKeyDefinition);
|
||||
const ciphers = await firstValueFrom(prov.state$.pipe(map((data) => data ?? {})));
|
||||
|
||||
return Object.values(ciphers).map((cipher) => this.mapper.toSdk(cipher));
|
||||
}
|
||||
async set(id: string, value: SdkType): Promise<void> {
|
||||
const prov = this.stateProvider.getUser(this.userId, this.userKeyDefinition);
|
||||
const ciphers = await firstValueFrom(prov.state$.pipe(map((data) => data ?? {})));
|
||||
ciphers[id] = this.mapper.fromSdk(value);
|
||||
await prov.update(() => ciphers);
|
||||
}
|
||||
async remove(id: string): Promise<void> {
|
||||
const prov = this.stateProvider.getUser(this.userId, this.userKeyDefinition);
|
||||
const ciphers = await firstValueFrom(prov.state$.pipe(map((data) => data ?? {})));
|
||||
if (!ciphers[id]) {
|
||||
return;
|
||||
}
|
||||
delete ciphers[id];
|
||||
await prov.update(() => ciphers);
|
||||
}
|
||||
}
|
||||
|
||||
class CipherMapper implements SdkMapper<CipherData, SdkCipher> {
|
||||
toSdk(value: CipherData): SdkCipher {
|
||||
return new Cipher(value).toSdkCipher();
|
||||
}
|
||||
|
||||
fromSdk(value: SdkCipher): CipherData {
|
||||
throw new Error("Cipher.fromSdk is not implemented yet");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user