From 1308b326fde194615d8e2fb41faa032598e762a1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Mon, 8 Apr 2024 07:26:22 -0500 Subject: [PATCH] Tools/specify-clearon-conditions (#8596) * Specify user clear events for event upload * Specify generator clear events * Specify clear events for user send data * Specify generic clear on logout for encrypted secret state * Allow `clearOn`event to be passed into secret state * Match current data persistence rules * Clear ui memory on lock + logout --- libs/common/src/platform/state/index.ts | 2 +- .../src/platform/state/user-key-definition.ts | 2 +- .../src/services/event/key-definitions.ts | 5 ++- .../src/tools/generator/key-definitions.ts | 39 ++++++++++++------- .../state/secret-key-definition.spec.ts | 8 ++-- .../generator/state/secret-key-definition.ts | 14 ++++--- .../username/forwarder-generator-strategy.ts | 5 ++- .../tools/send/services/key-definitions.ts | 24 ++++++++---- 8 files changed, 64 insertions(+), 35 deletions(-) diff --git a/libs/common/src/platform/state/index.ts b/libs/common/src/platform/state/index.ts index dd14aaf329d..367beefb495 100644 --- a/libs/common/src/platform/state/index.ts +++ b/libs/common/src/platform/state/index.ts @@ -8,7 +8,7 @@ export { ActiveUserState, SingleUserState, CombinedState } from "./user-state"; export { ActiveUserStateProvider, SingleUserStateProvider } from "./user-state.provider"; export { KeyDefinition, KeyDefinitionOptions } from "./key-definition"; export { StateUpdateOptions } from "./state-update-options"; -export { UserKeyDefinition } from "./user-key-definition"; +export { UserKeyDefinitionOptions, UserKeyDefinition } from "./user-key-definition"; export { StateEventRunnerService } from "./state-event-runner.service"; export * from "./state-definitions"; diff --git a/libs/common/src/platform/state/user-key-definition.ts b/libs/common/src/platform/state/user-key-definition.ts index 3405b388375..3eb9369080c 100644 --- a/libs/common/src/platform/state/user-key-definition.ts +++ b/libs/common/src/platform/state/user-key-definition.ts @@ -8,7 +8,7 @@ import { StateDefinition } from "./state-definition"; export type ClearEvent = "lock" | "logout"; -type UserKeyDefinitionOptions = KeyDefinitionOptions & { +export type UserKeyDefinitionOptions = KeyDefinitionOptions & { clearOn: ClearEvent[]; }; diff --git a/libs/common/src/services/event/key-definitions.ts b/libs/common/src/services/event/key-definitions.ts index 1059d24b724..56820996886 100644 --- a/libs/common/src/services/event/key-definitions.ts +++ b/libs/common/src/services/event/key-definitions.ts @@ -1,10 +1,11 @@ import { EventData } from "../../models/data/event.data"; -import { KeyDefinition, EVENT_COLLECTION_DISK } from "../../platform/state"; +import { EVENT_COLLECTION_DISK, UserKeyDefinition } from "../../platform/state"; -export const EVENT_COLLECTION: KeyDefinition = KeyDefinition.array( +export const EVENT_COLLECTION = UserKeyDefinition.array( EVENT_COLLECTION_DISK, "events", { deserializer: (s) => EventData.fromJSON(s), + clearOn: ["logout"], }, ); diff --git a/libs/common/src/tools/generator/key-definitions.ts b/libs/common/src/tools/generator/key-definitions.ts index 2f351696122..074df484682 100644 --- a/libs/common/src/tools/generator/key-definitions.ts +++ b/libs/common/src/tools/generator/key-definitions.ts @@ -1,4 +1,4 @@ -import { GENERATOR_DISK, GENERATOR_MEMORY, KeyDefinition } from "../../platform/state"; +import { GENERATOR_DISK, GENERATOR_MEMORY, UserKeyDefinition } from "../../platform/state"; import { GeneratedCredential } from "./history/generated-credential"; import { GeneratorNavigation } from "./navigation/generator-navigation"; @@ -17,110 +17,122 @@ import { import { SubaddressGenerationOptions } from "./username/subaddress-generator-options"; /** plaintext password generation options */ -export const GENERATOR_SETTINGS = new KeyDefinition( +export const GENERATOR_SETTINGS = new UserKeyDefinition( GENERATOR_MEMORY, "generatorSettings", { deserializer: (value) => value, + clearOn: ["lock", "logout"], }, ); /** plaintext password generation options */ -export const PASSWORD_SETTINGS = new KeyDefinition( +export const PASSWORD_SETTINGS = new UserKeyDefinition( GENERATOR_DISK, "passwordGeneratorSettings", { deserializer: (value) => value, + clearOn: [], }, ); /** plaintext passphrase generation options */ -export const PASSPHRASE_SETTINGS = new KeyDefinition( +export const PASSPHRASE_SETTINGS = new UserKeyDefinition( GENERATOR_DISK, "passphraseGeneratorSettings", { deserializer: (value) => value, + clearOn: [], }, ); /** plaintext username generation options */ -export const EFF_USERNAME_SETTINGS = new KeyDefinition( +export const EFF_USERNAME_SETTINGS = new UserKeyDefinition( GENERATOR_DISK, "effUsernameGeneratorSettings", { deserializer: (value) => value, + clearOn: [], }, ); /** plaintext configuration for a domain catch-all address. */ -export const CATCHALL_SETTINGS = new KeyDefinition( +export const CATCHALL_SETTINGS = new UserKeyDefinition( GENERATOR_DISK, "catchallGeneratorSettings", { deserializer: (value) => value, + clearOn: [], }, ); /** plaintext configuration for an email subaddress. */ -export const SUBADDRESS_SETTINGS = new KeyDefinition( +export const SUBADDRESS_SETTINGS = new UserKeyDefinition( GENERATOR_DISK, "subaddressGeneratorSettings", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link Forwarders.AddyIo} */ -export const ADDY_IO_FORWARDER = new KeyDefinition( +export const ADDY_IO_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "addyIoForwarder", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link Forwarders.DuckDuckGo} */ -export const DUCK_DUCK_GO_FORWARDER = new KeyDefinition( +export const DUCK_DUCK_GO_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "duckDuckGoForwarder", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link Forwarders.FastMail} */ -export const FASTMAIL_FORWARDER = new KeyDefinition( +export const FASTMAIL_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "fastmailForwarder", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link Forwarders.FireFoxRelay} */ -export const FIREFOX_RELAY_FORWARDER = new KeyDefinition( +export const FIREFOX_RELAY_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "firefoxRelayForwarder", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link Forwarders.ForwardEmail} */ -export const FORWARD_EMAIL_FORWARDER = new KeyDefinition( +export const FORWARD_EMAIL_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "forwardEmailForwarder", { deserializer: (value) => value, + clearOn: [], }, ); /** backing store configuration for {@link forwarders.SimpleLogin} */ -export const SIMPLE_LOGIN_FORWARDER = new KeyDefinition( +export const SIMPLE_LOGIN_FORWARDER = new UserKeyDefinition( GENERATOR_DISK, "simpleLoginForwarder", { deserializer: (value) => value, + clearOn: [], }, ); @@ -131,5 +143,6 @@ export const GENERATOR_HISTORY = SecretKeyDefinition.array( SecretClassifier.allSecret(), { deserializer: GeneratedCredential.fromJSON, + clearOn: ["logout"], }, ); diff --git a/libs/common/src/tools/generator/state/secret-key-definition.spec.ts b/libs/common/src/tools/generator/state/secret-key-definition.spec.ts index 7352631ff6c..a347268b0b4 100644 --- a/libs/common/src/tools/generator/state/secret-key-definition.spec.ts +++ b/libs/common/src/tools/generator/state/secret-key-definition.spec.ts @@ -1,16 +1,17 @@ -import { GENERATOR_DISK } from "../../../platform/state"; +import { GENERATOR_DISK, UserKeyDefinitionOptions } from "../../../platform/state"; import { SecretClassifier } from "./secret-classifier"; import { SecretKeyDefinition } from "./secret-key-definition"; describe("SecretKeyDefinition", () => { const classifier = SecretClassifier.allSecret<{ foo: boolean }>(); - const options = { deserializer: (v: any) => v }; + const options: UserKeyDefinitionOptions = { deserializer: (v: any) => v, clearOn: [] }; it("toEncryptedStateKey returns a key", () => { - const expectedOptions = { + const expectedOptions: UserKeyDefinitionOptions = { deserializer: (v: any) => v, cleanupDelayMs: 100, + clearOn: ["logout", "lock"], }; const definition = SecretKeyDefinition.value( GENERATOR_DISK, @@ -26,6 +27,7 @@ describe("SecretKeyDefinition", () => { expect(result.stateDefinition).toEqual(GENERATOR_DISK); expect(result.key).toBe("key"); expect(result.cleanupDelayMs).toBe(expectedOptions.cleanupDelayMs); + expect(result.clearOn).toEqual(expectedOptions.clearOn); expect(deserializerResult).toBe(expectedDeserializerResult); }); diff --git a/libs/common/src/tools/generator/state/secret-key-definition.ts b/libs/common/src/tools/generator/state/secret-key-definition.ts index 0de59be6244..22496d878e6 100644 --- a/libs/common/src/tools/generator/state/secret-key-definition.ts +++ b/libs/common/src/tools/generator/state/secret-key-definition.ts @@ -1,4 +1,4 @@ -import { KeyDefinition, KeyDefinitionOptions } from "../../../platform/state"; +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"; @@ -11,7 +11,7 @@ export class SecretKeyDefinition, - readonly options: KeyDefinitionOptions, + readonly options: UserKeyDefinitionOptions, // 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. @@ -21,12 +21,14 @@ export class SecretKeyDefinition[]>( + const secretKey = new UserKeyDefinition[]>( this.stateDefinition, this.key, { cleanupDelayMs: this.options.cleanupDelayMs, deserializer: (jsonValue) => jsonValue as ClassifiedFormat[], + // Clear encrypted state on logout + clearOn: this.options.clearOn, }, ); @@ -45,7 +47,7 @@ export class SecretKeyDefinition, - options: KeyDefinitionOptions, + options: UserKeyDefinitionOptions, ) { return new SecretKeyDefinition( stateDefinition, @@ -69,7 +71,7 @@ export class SecretKeyDefinition, - options: KeyDefinitionOptions, + options: UserKeyDefinitionOptions, ) { return new SecretKeyDefinition( stateDefinition, @@ -93,7 +95,7 @@ export class SecretKeyDefinition, - options: KeyDefinitionOptions, + options: UserKeyDefinitionOptions, ) { return new SecretKeyDefinition, Id, Item, Disclosed, Secret>( stateDefinition, diff --git a/libs/common/src/tools/generator/username/forwarder-generator-strategy.ts b/libs/common/src/tools/generator/username/forwarder-generator-strategy.ts index 086e3476698..b4205b9fc98 100644 --- a/libs/common/src/tools/generator/username/forwarder-generator-strategy.ts +++ b/libs/common/src/tools/generator/username/forwarder-generator-strategy.ts @@ -3,7 +3,7 @@ import { Observable, map, pipe } from "rxjs"; import { PolicyType } from "../../../admin-console/enums"; import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; -import { KeyDefinition, SingleUserState, StateProvider } from "../../../platform/state"; +import { SingleUserState, StateProvider, UserKeyDefinition } from "../../../platform/state"; import { UserId } from "../../../types/guid"; import { GeneratorStrategy } from "../abstractions"; import { DefaultPolicyEvaluator } from "../default-policy-evaluator"; @@ -56,6 +56,7 @@ export abstract class ForwarderGeneratorStrategy< const key = SecretKeyDefinition.value(this.key.stateDefinition, this.key.key, classifier, { deserializer: (d) => this.key.deserializer(d), cleanupDelayMs: this.key.cleanupDelayMs, + clearOn: this.key.clearOn, }); // the type parameter is explicit because type inference fails for `Omit` @@ -83,7 +84,7 @@ export abstract class ForwarderGeneratorStrategy< abstract defaults$: (userId: UserId) => Observable; /** Determine where forwarder configuration is stored */ - protected abstract readonly key: KeyDefinition; + protected abstract readonly key: UserKeyDefinition; /** {@link GeneratorStrategy.toEvaluator} */ toEvaluator = () => { diff --git a/libs/common/src/tools/send/services/key-definitions.ts b/libs/common/src/tools/send/services/key-definitions.ts index b117c522686..f1a6b3d6c6a 100644 --- a/libs/common/src/tools/send/services/key-definitions.ts +++ b/libs/common/src/tools/send/services/key-definitions.ts @@ -1,13 +1,23 @@ -import { KeyDefinition, SEND_DISK, SEND_MEMORY } from "../../../platform/state"; +import { SEND_DISK, SEND_MEMORY, UserKeyDefinition } from "../../../platform/state"; import { SendData } from "../models/data/send.data"; import { SendView } from "../models/view/send.view"; /** Encrypted send state stored on disk */ -export const SEND_USER_ENCRYPTED = KeyDefinition.record(SEND_DISK, "sendUserEncrypted", { - deserializer: (obj: SendData) => obj, -}); +export const SEND_USER_ENCRYPTED = UserKeyDefinition.record( + SEND_DISK, + "sendUserEncrypted", + { + deserializer: (obj: SendData) => obj, + clearOn: ["logout"], + }, +); /** Decrypted send state stored in memory */ -export const SEND_USER_DECRYPTED = new KeyDefinition(SEND_MEMORY, "sendUserDecrypted", { - deserializer: (obj) => obj, -}); +export const SEND_USER_DECRYPTED = new UserKeyDefinition( + SEND_MEMORY, + "sendUserDecrypted", + { + deserializer: (obj) => obj, + clearOn: ["lock"], + }, +);