Skip to content

Commit

Permalink
Merge pull request #1 from verji/taj/cryptosetupext
Browse files Browse the repository at this point in the history
Changed call sites from customisations/security to ModuleRunner.extensions
  • Loading branch information
thoraj authored Feb 13, 2024
2 parents 203c15f + a96ad46 commit 7897f6a
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 21 deletions.
5 changes: 3 additions & 2 deletions src/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { MINIMUM_MATRIX_VERSION, SUPPORTED_MATRIX_VERSIONS } from "matrix-js-sdk/src/version-support";

import { IMatrixClientCreds, MatrixClientPeg } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security";
import { ModuleRunner } from "./modules/ModuleRunner";
import EventIndexPeg from "./indexing/EventIndexPeg";
import createMatrixClient from "./utils/createMatrixClient";
import Notifier from "./Notifier";
Expand Down Expand Up @@ -921,7 +921,8 @@ async function persistCredentials(credentials: IMatrixClientCreds): Promise<void
localStorage.setItem("mx_device_id", credentials.deviceId);
}

SecurityCustomisations.persistCredentials?.(credentials);
//SecurityCustomisations.persistCredentials?.(credentials);
ModuleRunner.instance.extensions?.cryptoSetup?.persistCredentials(credentials);

logger.log(`Session persisted for ${credentials.userId}`);
}
Expand Down
5 changes: 3 additions & 2 deletions src/Login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import { logger } from "matrix-js-sdk/src/logger";

import { IMatrixClientCreds } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security";
import { ModuleRunner } from "./modules/ModuleRunner";
import { getOidcClientId } from "./utils/oidc/registerClient";
import { IConfigOptions } from "./IConfigOptions";
import SdkConfig from "./SdkConfig";
Expand Down Expand Up @@ -294,7 +294,8 @@ export async function sendLoginRequest(
accessToken: data.access_token,
};

SecurityCustomisations.examineLoginResponse?.(data, creds);
// SecurityCustomisations.examineLoginResponse?.(data, creds);
ModuleRunner.instance.extensions.cryptoSetup?.examineLoginResponse(data, creds);

return creds;
}
13 changes: 10 additions & 3 deletions src/MatrixClientPeg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientB
import * as StorageManager from "./utils/StorageManager";
import IdentityAuthClient from "./IdentityAuthClient";
import { crossSigningCallbacks, tryToUnlockSecretStorageWithDehydrationKey } from "./SecurityManager";
import SecurityCustomisations from "./customisations/Security";
import { ModuleRunner } from "./modules/ModuleRunner";
import { SlidingSyncManager } from "./SlidingSyncManager";
import CryptoStoreTooNewDialog from "./components/views/dialogs/CryptoStoreTooNewDialog";
import { _t, UserFriendlyError } from "./languageHandler";
Expand Down Expand Up @@ -464,8 +464,15 @@ class MatrixClientPegClass implements IMatrixClientPeg {
},
};

if (SecurityCustomisations.getDehydrationKey) {
opts.cryptoCallbacks!.getDehydrationKey = SecurityCustomisations.getDehydrationKey;
// if (SecurityCustomisations.getDehydrationKey) {
// opts.cryptoCallbacks!.getDehydrationKey = SecurityCustomisations.getDehydrationKey;
// }

console.log("CryptoSetupExtensions: Executing getDehydrationKeyCallback...")
const dehydrationKeyCallback = ModuleRunner.instance.extensions.cryptoSetup?.getDehydrationKeyCallback();
console.log("CryptoSetupExtensions: Executing getDehydrationKeyCallback...Done")
if (dehydrationKeyCallback) {
opts.cryptoCallbacks!.getDehydrationKey = dehydrationKeyCallback;
}

this.matrixClient = createMatrixClient(opts);
Expand Down
15 changes: 9 additions & 6 deletions src/SecurityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { isSecureBackupRequired } from "./utils/WellKnownUtils";
import AccessSecretStorageDialog, { KeyParams } from "./components/views/dialogs/security/AccessSecretStorageDialog";
import RestoreKeyBackupDialog from "./components/views/dialogs/security/RestoreKeyBackupDialog";
import SettingsStore from "./settings/SettingsStore";
import SecurityCustomisations from "./customisations/Security";
import { ModuleRunner } from "./modules/ModuleRunner";
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";

Expand Down Expand Up @@ -130,9 +130,10 @@ async function getSecretStorageKey({
}
}

const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
// const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup?.getSecretStorageKey();
if (keyFromCustomisations) {
logger.log("Using key from security customisations (secret storage)");
logger.log("CryptoSetupExtension: Using key from extension (secret storage)");
cacheSecretStorageKey(keyId, keyInfo, keyFromCustomisations);
return [keyId, keyFromCustomisations];
}
Expand Down Expand Up @@ -180,9 +181,10 @@ export async function getDehydrationKey(
keyInfo: ISecretStorageKeyInfo,
checkFunc: (data: Uint8Array) => void,
): Promise<Uint8Array> {
const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
// const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup?.getSecretStorageKey();
if (keyFromCustomisations) {
logger.log("Using key from security customisations (dehydration)");
logger.log("CryptoSetupExtension: Using key from extension (dehydration)");
return keyFromCustomisations;
}

Expand Down Expand Up @@ -419,7 +421,8 @@ async function doAccessSecretStorage(func: () => Promise<void>, forceReset: bool
// inner operation completes.
return await func();
} catch (e) {
SecurityCustomisations.catchAccessSecretStorageError?.(e);
// SecurityCustomisations.catchAccessSecretStorageError?.(e as Error);
ModuleRunner.instance.extensions.cryptoSetup?.catchAccessSecretStorageError(e as Error);
logger.error(e);
// Re-throw so that higher level logic can abort as needed
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
isSecureBackupRequired,
SecureBackupSetupMethod,
} from "../../../../utils/WellKnownUtils";
import SecurityCustomisations from "../../../../customisations/Security";
import { ModuleRunner } from "../../../../modules/ModuleRunner";
import Field from "../../../../components/views/elements/Field";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import Spinner from "../../../../components/views/elements/Spinner";
Expand Down Expand Up @@ -181,9 +181,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
}

private getInitialPhase(): void {
const keyFromCustomisations = SecurityCustomisations.createSecretStorageKey?.();
//const keyFromCustomisations = SecurityCustomisations.createSecretStorageKey?.();
const keyFromCustomisations = ModuleRunner.instance.extensions.cryptoSetup?.createSecretStorageKey();
if (keyFromCustomisations) {
logger.log("Created key via customisations, jumping to bootstrap step");
logger.log("CryptoSetupExtension: Created key via extension, jumping to bootstrap step");
this.recoveryKey = {
privateKey: keyFromCustomisations,
};
Expand Down
10 changes: 7 additions & 3 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import { showToast as showMobileGuideToast } from "../../toasts/MobileGuideToast
import { shouldUseLoginForWelcome } from "../../utils/pages";
import RoomListStore from "../../stores/room-list/RoomListStore";
import { RoomUpdateCause } from "../../stores/room-list/models";
import SecurityCustomisations from "../../customisations/Security";
import { ModuleRunner } from "../../modules/ModuleRunner";
import Spinner from "../views/elements/Spinner";
import QuestionDialog from "../views/dialogs/QuestionDialog";
import UserSettingsDialog from "../views/dialogs/UserSettingsDialog";
Expand Down Expand Up @@ -443,8 +443,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (crossSigningIsSetUp) {
// if the user has previously set up cross-signing, verify this device so we can fetch the
// private keys.
if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
this.onLoggedIn();


// if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
const cryptoExtension = ModuleRunner.instance.extensions.cryptoSetup;
if (cryptoExtension !== undefined && cryptoExtension.SHOW_ENCRYPTION_SETUP_UI == false) {
this.onLoggedIn();
} else {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
}
Expand Down
71 changes: 71 additions & 0 deletions src/modules/ModuleRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ limitations under the License.
import { safeSet } from "matrix-js-sdk/src/utils";
import { TranslationStringsObject } from "@matrix-org/react-sdk-module-api/lib/types/translations";
import { AnyLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/types";
import { AllExtensions } from "@matrix-org/react-sdk-module-api/lib/types/extensions";
import { DefaultCryptoSetupExtensions } from "@matrix-org/react-sdk-module-api/lib/lifecycles/CryptoSetupExtensions";
import { DefaultExperimentalExtensions } from "@matrix-org/react-sdk-module-api/lib/lifecycles/ExperimentalExtensions";
import { RuntimeModule } from "@matrix-org/react-sdk-module-api";

import { AppModule } from "./AppModule";
import { ModuleFactory } from "./ModuleFactory";
Expand All @@ -29,6 +33,13 @@ import "./ModuleComponents";
export class ModuleRunner {
public static readonly instance = new ModuleRunner();

public className: string = ModuleRunner.name;

public extensions: AllExtensions = {
cryptoSetup: new DefaultCryptoSetupExtensions(),
experimental: new DefaultExperimentalExtensions(),
};

private modules: AppModule[] = [];

private constructor() {
Expand All @@ -42,6 +53,11 @@ export class ModuleRunner {
*/
public reset(): void {
this.modules = [];

this.extensions = {
cryptoSetup: new DefaultCryptoSetupExtensions(),
experimental: new DefaultExperimentalExtensions(),
};
}

/**
Expand All @@ -66,13 +82,68 @@ export class ModuleRunner {
return merged;
}


/**
* Ensure we register extensions provided by the modules
*/
private updateExtensions(): void {
const cryptoSetupExtensions: Array<RuntimeModule> = [];
const experimentalExtensions: Array<RuntimeModule> = [];

this.modules.forEach((m) => {
/* Record the cryptoSetup extensions if any */
if (m.module.extensions?.cryptoSetup) {
cryptoSetupExtensions.push(m.module);
}

/* Record the experimantal extensions if any */
if (m.module.extensions?.experimental) {
experimentalExtensions.push(m.module);
}
});

/* Enforce rule that only a single module may provide a given extension */
if (cryptoSetupExtensions.length > 1) {
throw new Error(
`cryptoSetup extension is provided by modules ${cryptoSetupExtensions
.map((m) => m.moduleName)
.join(", ")}, but can only be provided by a single module`,
);
}
if (experimentalExtensions.length > 1) {
throw new Error(
`experimental extension is provided by modules ${experimentalExtensions
.map((m) => m.moduleName)
.join(", ")}, but can only be provided by a single module`,
);
}

/* Override the default extension if extension was provided by a module */
if (cryptoSetupExtensions.length == 1) {
this.extensions.cryptoSetup = cryptoSetupExtensions[0].extensions?.cryptoSetup;
}

if (experimentalExtensions.length == 1) {
this.extensions.experimental = cryptoSetupExtensions[0].extensions?.experimental;
}
}

/**
* Registers a factory which creates a module for later loading. The factory
* will be called immediately.
* @param factory The module factory.
*/
public registerModule(factory: ModuleFactory): void {
this.modules.push(new AppModule(factory));

/**
* Check if the new module provides any extensions, and also ensure a given extension is only provided by a single runtime module
* Slightly inefficient to do this on each registration, but avoids changes to element-web installer code
* Also note that this require that the statement in the comment above, about immediately calling the factory, is in fact true
* (otherwise wrapped RuntimeModules will not be available)
*/

this.updateExtensions();
}

/**
Expand Down
12 changes: 10 additions & 2 deletions src/toasts/SetupEncryptionToast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import SetupEncryptionDialog from "../components/views/dialogs/security/SetupEnc
import { accessSecretStorage } from "../SecurityManager";
import ToastStore from "../stores/ToastStore";
import GenericToast from "../components/views/toasts/GenericToast";
import SecurityCustomisations from "../customisations/Security";
import { ModuleRunner } from "../modules/ModuleRunner";
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
import Spinner from "../components/views/elements/Spinner";

const TOAST_KEY = "setupencryption";
Expand Down Expand Up @@ -79,7 +80,14 @@ const onReject = (): void => {
};

export const showToast = (kind: Kind): void => {
if (SecurityCustomisations.setupEncryptionNeeded?.(kind)) {
// if (SecurityCustomisations.setupEncryptionNeeded?.(kind)) {
// return;
// }

if (ModuleRunner.instance.extensions.cryptoSetup?.setupEncryptionNeeded({
kind: kind as any,
storeProvider: { getInstance: () => SetupEncryptionStore.sharedInstance() },
})) {
return;
}

Expand Down

0 comments on commit 7897f6a

Please sign in to comment.