1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 01:03:35 +00:00
Files
browser/libs/common/src/tools/state/secret-key-definition.ts
Matt Gibson 9c1e2ebd67 Typescript-strict-plugin (#12235)
* Use typescript-strict-plugin to iteratively turn on strict

* Add strict testing to pipeline

Can be executed locally through either `npm run test:types` for full type checking including spec files, or `npx tsc-strict` for only tsconfig.json included files.

* turn on strict for scripts directory

* Use plugin for all tsconfigs in monorepo

vscode is capable of executing tsc with plugins, but uses the most relevant tsconfig to do so. If the plugin is not a part of that config, it is skipped and developers get no feedback of strict compile time issues. These updates remedy that at the cost of slightly more complex removal of the plugin when the time comes.

* remove plugin from configs that extend one that already has it

* Update workspace settings to honor strict plugin

* Apply strict-plugin to native message test runner

* Update vscode workspace to use root tsc version

* `./node_modules/.bin/update-strict-comments` 🤖

This is a one-time operation. All future files should adhere to strict type checking.

* Add fixme to `ts-strict-ignore` comments

* `update-strict-comments` 🤖

repeated for new merge files
2024-12-09 20:58:50 +01:00

112 lines
4.3 KiB
TypeScript

// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { UserKeyDefinitionOptions, UserKeyDefinition } from "../../platform/state";
// eslint-disable-next-line -- `StateDefinition` used as an argument
import { StateDefinition } from "../../platform/state/state-definition";
import { ClassifiedFormat } from "./classified-format";
import { Classifier } from "./classifier";
/** Encryption and storage settings for data stored by a `SecretState`.
*/
export class SecretKeyDefinition<Outer, Id, Inner extends object, Disclosed, Secret> {
private constructor(
readonly stateDefinition: StateDefinition,
readonly key: string,
readonly classifier: Classifier<Inner, Disclosed, Secret>,
readonly options: UserKeyDefinitionOptions<Inner>,
// type erasure is necessary here because typescript doesn't support
// higher kinded types that generalize over collections. The invariants
// needed to make this typesafe are maintained by the static factories.
readonly deconstruct: (value: any) => [Id, any][],
readonly reconstruct: ([inners, ids]: (readonly [Id, any])[]) => Outer,
) {}
/** Converts the secret key to the `KeyDefinition` used for secret storage. */
toEncryptedStateKey() {
const secretKey = new UserKeyDefinition<ClassifiedFormat<Id, Disclosed>[]>(
this.stateDefinition,
this.key,
{
cleanupDelayMs: this.options.cleanupDelayMs,
deserializer: (jsonValue) => jsonValue as ClassifiedFormat<Id, Disclosed>[],
// Clear encrypted state on logout
clearOn: this.options.clearOn,
},
);
return secretKey;
}
/**
* Define a secret state for a single value
* @param stateDefinition The domain of the secret's durable state.
* @param key Domain key that identifies the stored value. This key must not be reused
* in any capacity.
* @param classifier Partitions the value into encrypted, discarded, and public data.
* @param options Configures the operation of the secret state.
*/
static value<Value extends object, Disclosed, Secret>(
stateDefinition: StateDefinition,
key: string,
classifier: Classifier<Value, Disclosed, Secret>,
options: UserKeyDefinitionOptions<Value>,
) {
return new SecretKeyDefinition<Value, void, Value, Disclosed, Secret>(
stateDefinition,
key,
classifier,
options,
(value) => [[null, value]],
([[, inner]]) => inner,
);
}
/**
* Define a secret state for an array of values. Each item is encrypted separately.
* @param stateDefinition The domain of the secret's durable state.
* @param key Domain key that identifies the stored items. This key must not be reused
* in any capacity.
* @param classifier Partitions each item into encrypted, discarded, and public data.
* @param options Configures the operation of the secret state.
*/
static array<Item extends object, Disclosed, Secret>(
stateDefinition: StateDefinition,
key: string,
classifier: Classifier<Item, Disclosed, Secret>,
options: UserKeyDefinitionOptions<Item>,
) {
return new SecretKeyDefinition<Item[], number, Item, Disclosed, Secret>(
stateDefinition,
key,
classifier,
options,
(value) => value.map((v: any, id: number) => [id, v]),
(values) => values.map(([, v]) => v),
);
}
/**
* Define a secret state for a record. Each property is encrypted separately.
* @param stateDefinition The domain of the secret's durable state.
* @param key Domain key that identifies the stored properties. This key must not be reused
* in any capacity.
* @param classifier Partitions each property into encrypted, discarded, and public data.
* @param options Configures the operation of the secret state.
*/
static record<Item extends object, Disclosed, Secret, Id extends string | number>(
stateDefinition: StateDefinition,
key: string,
classifier: Classifier<Item, Disclosed, Secret>,
options: UserKeyDefinitionOptions<Item>,
) {
return new SecretKeyDefinition<Record<Id, Item>, Id, Item, Disclosed, Secret>(
stateDefinition,
key,
classifier,
options,
(value) => Object.entries(value) as [Id, Item][],
(values) => Object.fromEntries(values) as Record<Id, Item>,
);
}
}