mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 15:23:33 +00:00
Migrate autoConfirmFingerPrints to StateProvider (#8337)
* Fix a typo in the `StateDefinition` description * Introduce `OrganizationManagementPreferencesService` * Declare `OrganizationManagementPreferencesService` in DI * Update `autoConfirmFingerPrints` logic in emergency access files * Update `autoConfirmFingerPrints` logic in `people` files * Remove `autoConfirmFingerPrints` from `StateService` and `Account` * Migrate existing client data for `autoConfirmFingerPrints` * Update apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts Co-authored-by: Matt Gibson <mgibson@bitwarden.com> * Update apps/web/src/app/admin-console/organizations/manage/user-confirm.component.ts Co-authored-by: Matt Gibson <mgibson@bitwarden.com> * Use `set` instead of `update` for function names --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
/**
|
||||
* Manages the state of a single organization management preference.
|
||||
* Can be used to subscribe to or update a given property.
|
||||
*/
|
||||
export class OrganizationManagementPreference<T> {
|
||||
state$: Observable<T>;
|
||||
set: (value: T) => Promise<void>;
|
||||
|
||||
constructor(state$: Observable<T>, setFn: (value: T) => Promise<void>) {
|
||||
this.state$ = state$;
|
||||
this.set = setFn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes state of a given user's personal settings relating to the user experience of managing an organization.
|
||||
*/
|
||||
export abstract class OrganizationManagementPreferencesService {
|
||||
autoConfirmFingerPrints: OrganizationManagementPreference<boolean>;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { MockProxy } from "jest-mock-extended";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
|
||||
import { UserId } from "../../../types/guid";
|
||||
|
||||
import { DefaultOrganizationManagementPreferencesService } from "./default-organization-management-preferences.service";
|
||||
|
||||
describe("OrganizationManagementPreferencesService", () => {
|
||||
let stateProvider: FakeStateProvider;
|
||||
let organizationManagementPreferencesService: MockProxy<DefaultOrganizationManagementPreferencesService>;
|
||||
|
||||
beforeEach(() => {
|
||||
const accountService = mockAccountServiceWith("userId" as UserId);
|
||||
stateProvider = new FakeStateProvider(accountService);
|
||||
organizationManagementPreferencesService = new DefaultOrganizationManagementPreferencesService(
|
||||
stateProvider,
|
||||
);
|
||||
});
|
||||
|
||||
describe("autoConfirmFingerPrints", () => {
|
||||
it("returns false by default", async () => {
|
||||
const value = await firstValueFrom(
|
||||
organizationManagementPreferencesService.autoConfirmFingerPrints.state$,
|
||||
);
|
||||
expect(value).toEqual(false);
|
||||
});
|
||||
it("returns true if set", async () => {
|
||||
await organizationManagementPreferencesService.autoConfirmFingerPrints.set(true);
|
||||
const value = await firstValueFrom(
|
||||
organizationManagementPreferencesService.autoConfirmFingerPrints.state$,
|
||||
);
|
||||
expect(value).toEqual(true);
|
||||
});
|
||||
it("can be unset", async () => {
|
||||
await organizationManagementPreferencesService.autoConfirmFingerPrints.set(true);
|
||||
await organizationManagementPreferencesService.autoConfirmFingerPrints.set(false);
|
||||
const value = await firstValueFrom(
|
||||
organizationManagementPreferencesService.autoConfirmFingerPrints.state$,
|
||||
);
|
||||
expect(value).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { map } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import {
|
||||
ORGANIZATION_MANAGEMENT_PREFERENCES_DISK,
|
||||
StateProvider,
|
||||
UserKeyDefinition,
|
||||
} from "../../../platform/state";
|
||||
import {
|
||||
OrganizationManagementPreference,
|
||||
OrganizationManagementPreferencesService,
|
||||
} from "../../abstractions/organization-management-preferences/organization-management-preferences.service";
|
||||
|
||||
/**
|
||||
* This helper function can be used to quickly create `KeyDefinitions` that
|
||||
* target the `ORGANIZATION_MANAGEMENT_PREFERENCES_DISK` `StateDefinition`
|
||||
* and that have the default deserializer and `clearOn` options. Any
|
||||
* contenders for options to add to this service will likely use these same
|
||||
* options.
|
||||
*/
|
||||
function buildKeyDefinition<T>(key: string): UserKeyDefinition<T> {
|
||||
return new UserKeyDefinition<T>(ORGANIZATION_MANAGEMENT_PREFERENCES_DISK, key, {
|
||||
deserializer: (obj: Jsonify<T>) => obj as T,
|
||||
clearOn: ["logout"],
|
||||
});
|
||||
}
|
||||
|
||||
export const AUTO_CONFIRM_FINGERPRINTS = buildKeyDefinition<boolean>("autoConfirmFingerPrints");
|
||||
|
||||
export class DefaultOrganizationManagementPreferencesService
|
||||
implements OrganizationManagementPreferencesService
|
||||
{
|
||||
constructor(private stateProvider: StateProvider) {}
|
||||
|
||||
autoConfirmFingerPrints = this.buildOrganizationManagementPreference(
|
||||
AUTO_CONFIRM_FINGERPRINTS,
|
||||
false,
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns an `OrganizationManagementPreference` object for the provided
|
||||
* `KeyDefinition`. This object can then be used by callers to subscribe to
|
||||
* a given key, or set its value in state.
|
||||
*/
|
||||
private buildOrganizationManagementPreference<T>(
|
||||
keyDefinition: UserKeyDefinition<T>,
|
||||
defaultValue: T,
|
||||
) {
|
||||
return new OrganizationManagementPreference<T>(
|
||||
this.getKeyFromState(keyDefinition).state$.pipe(map((x) => x ?? defaultValue)),
|
||||
this.setKeyInStateFn(keyDefinition),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full `ActiveUserState` value for a given `keyDefinition`
|
||||
* The returned value can then be called for subscription || set operations
|
||||
*/
|
||||
private getKeyFromState<T>(keyDefinition: UserKeyDefinition<T>) {
|
||||
return this.stateProvider.getActive(keyDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that can be called to set the given `keyDefinition` in state
|
||||
*/
|
||||
private setKeyInStateFn<T>(keyDefinition: UserKeyDefinition<T>) {
|
||||
return async (value: T) => {
|
||||
await this.getKeyFromState(keyDefinition).update(() => value);
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user