1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00

[PM-5533] Migrate Asymmetric User Keys to State Providers (#7665)

This commit is contained in:
Matt Gibson
2024-02-14 15:04:08 -05:00
committed by GitHub
parent 7a6d7b3a68
commit d8b74b78da
13 changed files with 554 additions and 127 deletions

View File

@@ -0,0 +1,42 @@
import { DeriveDefinition } from "./derive-definition";
import { KeyDefinition } from "./key-definition";
import { StateDefinition } from "./state-definition";
const derive: () => any = () => null;
const deserializer: any = (obj: any) => obj;
const STATE_DEFINITION = new StateDefinition("test", "disk");
const TEST_KEY = new KeyDefinition(STATE_DEFINITION, "test", {
deserializer,
});
const TEST_DERIVE = new DeriveDefinition(STATE_DEFINITION, "test", {
derive,
deserializer,
});
describe("DeriveDefinition", () => {
describe("from", () => {
it("should create a new DeriveDefinition from a KeyDefinition", () => {
const result = DeriveDefinition.from(TEST_KEY, {
derive,
deserializer,
});
expect(result).toEqual(TEST_DERIVE);
});
it("should create a new DeriveDefinition from a DeriveDefinition", () => {
const result = DeriveDefinition.from([TEST_DERIVE, "newDerive"], {
derive,
deserializer,
});
expect(result).toEqual(
new DeriveDefinition(STATE_DEFINITION, "newDerive", {
derive,
deserializer,
}),
);
});
});
});

View File

@@ -1,5 +1,6 @@
import { Jsonify } from "type-fest";
import { UserId } from "../../types/guid";
import { DerivedStateDependencies, StorageKey } from "../../types/state";
import { KeyDefinition } from "./key-definition";
@@ -95,18 +96,60 @@ export class DeriveDefinition<TFrom, TTo, TDeps extends DerivedStateDependencies
) {}
/**
* Factory that produces a {@link DeriveDefinition} from a {@link KeyDefinition} and a set of options. The returned
* definition will have the same key as the given key definition, but will not collide with it in storage, even if
* they both reside in memory.
* @param keyDefinition
* Factory that produces a {@link DeriveDefinition} from a {@link KeyDefinition} or {@link DeriveDefinition} and new name.
*
* If a `KeyDefinition` is passed in, the returned definition will have the same key as the given key definition, but
* will not collide with it in storage, even if they both reside in memory.
*
* If a `DeriveDefinition` is passed in, the returned definition will instead use the name given in the second position
* of the tuple. It is up to you to ensure this is unique within the domain of derived state.
*
* @param options A set of options to customize the behavior of {@link DeriveDefinition}.
* @param options.derive A function to use to convert values from TFrom to TTo. This is called on each emit of the parent state observable
* and the resulting value will be emitted from the derived state observable.
* @param options.cleanupDelayMs The number of milliseconds to wait before cleaning up the state after the last subscriber has unsubscribed.
* Defaults to 1000ms.
* @param options.dependencyShape An object defining the dependencies of the derive function. The keys of the object are the names of the dependencies
* and the values are the types of the dependencies.
* for example:
* ```
* {
* myService: MyService,
* myOtherService: MyOtherService,
* }
* ```
*
* @param options.deserializer A function to use to safely convert your type from json to your expected type.
* Your data may be serialized/deserialized at any time and this needs callback needs to be able to faithfully re-initialize
* from the JSON object representation of your type.
* @param definition
* @param options
* @returns
*/
static from<TFrom, TTo, TDeps extends DerivedStateDependencies = never>(
keyDefinition: KeyDefinition<TFrom>,
definition:
| KeyDefinition<TFrom>
| [DeriveDefinition<unknown, TFrom, DerivedStateDependencies>, string],
options: DeriveDefinitionOptions<TFrom, TTo, TDeps>,
) {
return new DeriveDefinition(keyDefinition.stateDefinition, keyDefinition.key, options);
if (isKeyDefinition(definition)) {
return new DeriveDefinition(definition.stateDefinition, definition.key, options);
} else {
return new DeriveDefinition(definition[0].stateDefinition, definition[1], options);
}
}
static fromWithUserId<TKeyDef, TTo, TDeps extends DerivedStateDependencies = never>(
definition:
| KeyDefinition<TKeyDef>
| [DeriveDefinition<unknown, TKeyDef, DerivedStateDependencies>, string],
options: DeriveDefinitionOptions<[UserId, TKeyDef], TTo, TDeps>,
) {
if (isKeyDefinition(definition)) {
return new DeriveDefinition(definition.stateDefinition, definition.key, options);
} else {
return new DeriveDefinition(definition[0].stateDefinition, definition[1], options);
}
}
get derive() {
@@ -137,3 +180,11 @@ export class DeriveDefinition<TFrom, TTo, TDeps extends DerivedStateDependencies
return `derived_${this.stateDefinition.name}_${this.uniqueDerivationName}` as StorageKey;
}
}
function isKeyDefinition(
definition:
| KeyDefinition<unknown>
| [DeriveDefinition<unknown, unknown, DerivedStateDependencies>, string],
): definition is KeyDefinition<unknown> {
return Object.prototype.hasOwnProperty.call(definition, "key");
}

View File

@@ -49,7 +49,7 @@ export abstract class StateProvider {
getGlobal: <T>(keyDefinition: KeyDefinition<T>) => GlobalState<T>;
getDerived: <TFrom, TTo, TDeps extends DerivedStateDependencies>(
parentState$: Observable<TFrom>,
deriveDefinition: DeriveDefinition<unknown, TTo, TDeps>,
deriveDefinition: DeriveDefinition<TFrom, TTo, TDeps>,
dependencies: TDeps,
) => DerivedState<TTo>;
}