Skip to content

Commit 3fcc566

Browse files
t3chguyrichvdh
andauthored
Use mapped types for account data content (#4590)
* Use mapped types around account data events Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Harden types for reading account data too Signed-off-by: Michael Telatynski <[email protected]> * Correct empty object type Signed-off-by: Michael Telatynski <[email protected]> * Update src/secret-storage.ts Co-authored-by: Richard van der Hoff <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> * Iterate Signed-off-by: Michael Telatynski <[email protected]> --------- Signed-off-by: Michael Telatynski <[email protected]> Co-authored-by: Richard van der Hoff <[email protected]>
1 parent bcf3d56 commit 3fcc566

18 files changed

+235
-120
lines changed

spec/integ/matrix-client-syncing.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ import { THREAD_RELATION_TYPE } from "../../src/models/thread";
5050
import { IActionsObject } from "../../src/pushprocessor";
5151
import { KnownMembership } from "../../src/@types/membership";
5252

53+
declare module "../../src/@types/event" {
54+
interface AccountDataEvents {
55+
a: {};
56+
b: {};
57+
}
58+
}
59+
5360
describe("MatrixClient syncing", () => {
5461
const selfUserId = "@alice:localhost";
5562
const selfAccessToken = "aseukfgwef";

spec/integ/sliding-sync-sdk.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ import { emitPromise } from "../test-utils/test-utils";
4545
import { defer } from "../../src/utils";
4646
import { KnownMembership } from "../../src/@types/membership";
4747

48+
declare module "../../src/@types/event" {
49+
interface AccountDataEvents {
50+
global_test: {};
51+
tester: {};
52+
}
53+
}
54+
4855
describe("SlidingSyncSdk", () => {
4956
let client: MatrixClient | undefined;
5057
let httpBackend: MockHttpBackend | undefined;

spec/unit/crypto/secrets.spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { ICurve25519AuthData } from "../../../src/crypto/keybackup";
3030
import { SecretStorageKeyDescription, SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/secret-storage";
3131
import { decodeBase64 } from "../../../src/base64";
3232
import { CrossSigningKeyInfo } from "../../../src/crypto-api";
33+
import { SecretInfo } from "../../../src/secret-storage.ts";
3334

3435
async function makeTestClient(
3536
userInfo: { userId: string; deviceId: string },
@@ -68,6 +69,12 @@ function sign<T extends IObject | ICurve25519AuthData>(
6869
};
6970
}
7071

72+
declare module "../../../src/@types/event" {
73+
interface SecretStorageAccountDataEvents {
74+
foo: SecretInfo;
75+
}
76+
}
77+
7178
describe("Secrets", function () {
7279
if (!globalThis.Olm) {
7380
logger.warn("Not running megolm backup unit tests: libolm not present");

spec/unit/matrix-client.spec.ts

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ function convertQueryDictToMap(queryDict?: QueryDict): Map<string, string> {
9494
return new Map(Object.entries(queryDict).map(([k, v]) => [k, String(v)]));
9595
}
9696

97+
declare module "../../src/@types/event" {
98+
interface AccountDataEvents {
99+
"im.vector.test": {};
100+
}
101+
}
102+
97103
type HttpLookup = {
98104
method: string;
99105
path: string;

spec/unit/rust-crypto/rust-crypto.spec.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import fetchMock from "fetch-mock-jest";
3030
import { RustCrypto } from "../../../src/rust-crypto/rust-crypto";
3131
import { initRustCrypto } from "../../../src/rust-crypto";
3232
import {
33+
AccountDataEvents,
3334
Device,
3435
DeviceVerification,
3536
encodeBase64,
@@ -1924,11 +1925,13 @@ class DummyAccountDataClient
19241925
super();
19251926
}
19261927

1927-
public async getAccountDataFromServer<T extends Record<string, any>>(eventType: string): Promise<T | null> {
1928+
public async getAccountDataFromServer<K extends keyof AccountDataEvents>(
1929+
eventType: K,
1930+
): Promise<AccountDataEvents[K] | null> {
19281931
const ret = this.storage.get(eventType);
19291932

19301933
if (eventType) {
1931-
return ret as T;
1934+
return ret;
19321935
} else {
19331936
return null;
19341937
}

spec/unit/rust-crypto/secret-storage.spec.ts

+12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ import {
1919
secretStorageContainsCrossSigningKeys,
2020
} from "../../../src/rust-crypto/secret-storage";
2121
import { ServerSideSecretStorage } from "../../../src/secret-storage";
22+
import { SecretInfo } from "../../../src/secret-storage.ts";
23+
24+
declare module "../../../src/@types/event" {
25+
interface SecretStorageAccountDataEvents {
26+
secretA: SecretInfo;
27+
secretB: SecretInfo;
28+
secretC: SecretInfo;
29+
secretD: SecretInfo;
30+
secretE: SecretInfo;
31+
Unknown: SecretInfo;
32+
}
33+
}
2234

2335
describe("secret-storage", () => {
2436
describe("secretStorageContainsCrossSigningKeys", () => {

spec/unit/secret-storage.spec.ts

+21-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ import {
2727
trimTrailingEquals,
2828
} from "../../src/secret-storage";
2929
import { randomString } from "../../src/randomstring";
30+
import { SecretInfo } from "../../src/secret-storage.ts";
31+
import { AccountDataEvents } from "../../src";
32+
33+
declare module "../../src/@types/event" {
34+
interface SecretStorageAccountDataEvents {
35+
mysecret: SecretInfo;
36+
}
37+
}
3038

3139
describe("ServerSideSecretStorageImpl", function () {
3240
describe(".addKey", function () {
@@ -117,9 +125,11 @@ describe("ServerSideSecretStorageImpl", function () {
117125
const secretStorage = new ServerSideSecretStorageImpl(accountDataAdapter, {});
118126

119127
const storedKey = { iv: "iv", mac: "mac" } as SecretStorageKeyDescriptionAesV1;
120-
async function mockGetAccountData<T extends Record<string, any>>(eventType: string): Promise<T | null> {
128+
async function mockGetAccountData<K extends keyof AccountDataEvents>(
129+
eventType: string,
130+
): Promise<AccountDataEvents[K] | null> {
121131
if (eventType === "m.secret_storage.key.my_key") {
122-
return storedKey as unknown as T;
132+
return storedKey as any;
123133
} else {
124134
throw new Error(`unexpected eventType ${eventType}`);
125135
}
@@ -135,11 +145,13 @@ describe("ServerSideSecretStorageImpl", function () {
135145
const secretStorage = new ServerSideSecretStorageImpl(accountDataAdapter, {});
136146

137147
const storedKey = { iv: "iv", mac: "mac" } as SecretStorageKeyDescriptionAesV1;
138-
async function mockGetAccountData<T extends Record<string, any>>(eventType: string): Promise<T | null> {
148+
async function mockGetAccountData<K extends keyof AccountDataEvents>(
149+
eventType: string,
150+
): Promise<AccountDataEvents[K] | null> {
139151
if (eventType === "m.secret_storage.default_key") {
140-
return { key: "default_key_id" } as unknown as T;
152+
return { key: "default_key_id" } as any;
141153
} else if (eventType === "m.secret_storage.key.default_key_id") {
142-
return storedKey as unknown as T;
154+
return storedKey as any;
143155
} else {
144156
throw new Error(`unexpected eventType ${eventType}`);
145157
}
@@ -236,9 +248,11 @@ describe("ServerSideSecretStorageImpl", function () {
236248

237249
// stub out getAccountData to return a key with an unknown algorithm
238250
const storedKey = { algorithm: "badalg" } as SecretStorageKeyDescriptionCommon;
239-
async function mockGetAccountData<T extends Record<string, any>>(eventType: string): Promise<T | null> {
251+
async function mockGetAccountData<K extends keyof AccountDataEvents>(
252+
eventType: string,
253+
): Promise<AccountDataEvents[K] | null> {
240254
if (eventType === "m.secret_storage.key.keyid") {
241-
return storedKey as unknown as T;
255+
return storedKey as any;
242256
} else {
243257
throw new Error(`unexpected eventType ${eventType}`);
244258
}

src/@types/event.ts

+34
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ import {
5858
import { EncryptionKeysEventContent, ICallNotifyContent } from "../matrixrtc/types.ts";
5959
import { M_POLL_END, M_POLL_START, PollEndEventContent, PollStartEventContent } from "./polls.ts";
6060
import { SessionMembershipData } from "../matrixrtc/CallMembership.ts";
61+
import { LocalNotificationSettings } from "./local_notifications.ts";
62+
import { IPushRules } from "./PushRules.ts";
63+
import { SecretInfo, SecretStorageKeyDescription } from "../secret-storage.ts";
64+
import { POLICIES_ACCOUNT_EVENT_TYPE } from "../models/invites-ignorer-types.ts";
6165

6266
export enum EventType {
6367
// Room state events
@@ -368,3 +372,33 @@ export interface StateEvents {
368372
// MSC3672
369373
[M_BEACON_INFO.name]: MBeaconInfoEventContent;
370374
}
375+
376+
/**
377+
* Mapped type from event type to content type for all specified global account_data events.
378+
*/
379+
export interface AccountDataEvents extends SecretStorageAccountDataEvents {
380+
[EventType.PushRules]: IPushRules;
381+
[EventType.Direct]: { [userId: string]: string[] };
382+
[EventType.IgnoredUserList]: { [userId: string]: {} };
383+
"m.secret_storage.default_key": { key: string };
384+
"m.identity_server": { base_url: string | null };
385+
[key: `${typeof LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${string}`]: LocalNotificationSettings;
386+
[key: `m.secret_storage.key.${string}`]: SecretStorageKeyDescription;
387+
388+
// Invites-ignorer events
389+
[POLICIES_ACCOUNT_EVENT_TYPE.name]: { [key: string]: any };
390+
[POLICIES_ACCOUNT_EVENT_TYPE.altName]: { [key: string]: any };
391+
}
392+
393+
/**
394+
* Mapped type from event type to content type for all specified global events encrypted by secret storage.
395+
*
396+
* See https://spec.matrix.org/v1.13/client-server-api/#msecret_storagev1aes-hmac-sha2-1
397+
*/
398+
export interface SecretStorageAccountDataEvents {
399+
"m.megolm_backup.v1": SecretInfo;
400+
"m.cross_signing.master": SecretInfo;
401+
"m.cross_signing.self_signing": SecretInfo;
402+
"m.cross_signing.user_signing": SecretInfo;
403+
"org.matrix.msc3814": SecretInfo;
404+
}

src/client.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ import {
136136
UpdateDelayedEventAction,
137137
} from "./@types/requests.ts";
138138
import {
139+
AccountDataEvents,
139140
EventType,
140141
LOCAL_NOTIFICATION_SETTINGS_PREFIX,
141142
MSC3912_RELATION_BASED_REDACTIONS_PROP,
@@ -232,6 +233,7 @@ import {
232233
import { DeviceInfoMap } from "./crypto/DeviceList.ts";
233234
import {
234235
AddSecretStorageKeyOpts,
236+
SecretStorageKey,
235237
SecretStorageKeyDescription,
236238
ServerSideSecretStorage,
237239
ServerSideSecretStorageImpl,
@@ -3070,7 +3072,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
30703072
*
30713073
* @deprecated Use {@link MatrixClient#secretStorage} and {@link SecretStorage.ServerSideSecretStorage#isStored}.
30723074
*/
3073-
public isSecretStored(name: string): Promise<Record<string, SecretStorageKeyDescription> | null> {
3075+
public isSecretStored(name: SecretStorageKey): Promise<Record<string, SecretStorageKeyDescription> | null> {
30743076
return this.secretStorage.isStored(name);
30753077
}
30763078

@@ -4236,7 +4238,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
42364238
* @returns Promise which resolves: an empty object
42374239
* @returns Rejects: with an error response.
42384240
*/
4239-
public setAccountData(eventType: EventType | string, content: IContent): Promise<{}> {
4241+
public setAccountData<K extends keyof AccountDataEvents>(
4242+
eventType: K,
4243+
content: AccountDataEvents[K] | Record<string, never>,
4244+
): Promise<{}> {
42404245
const path = utils.encodeUri("/user/$userId/account_data/$type", {
42414246
$userId: this.credentials.userId!,
42424247
$type: eventType,
@@ -4251,7 +4256,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
42514256
* @param eventType - The event type
42524257
* @returns The contents of the given account data event
42534258
*/
4254-
public getAccountData(eventType: string): MatrixEvent | undefined {
4259+
public getAccountData<K extends keyof AccountDataEvents>(eventType: K): MatrixEvent | undefined {
42554260
return this.store.getAccountData(eventType);
42564261
}
42574262

@@ -4263,15 +4268,17 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
42634268
* @returns Promise which resolves: The contents of the given account data event.
42644269
* @returns Rejects: with an error response.
42654270
*/
4266-
public async getAccountDataFromServer<T extends { [k: string]: any }>(eventType: string): Promise<T | null> {
4271+
public async getAccountDataFromServer<K extends keyof AccountDataEvents>(
4272+
eventType: K,
4273+
): Promise<AccountDataEvents[K] | null> {
42674274
if (this.isInitialSyncComplete()) {
42684275
const event = this.store.getAccountData(eventType);
42694276
if (!event) {
42704277
return null;
42714278
}
42724279
// The network version below returns just the content, so this branch
42734280
// does the same to match.
4274-
return event.getContent<T>();
4281+
return event.getContent<AccountDataEvents[K]>();
42754282
}
42764283
const path = utils.encodeUri("/user/$userId/account_data/$type", {
42774284
$userId: this.credentials.userId!,
@@ -4287,7 +4294,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
42874294
}
42884295
}
42894296

4290-
public async deleteAccountData(eventType: string): Promise<void> {
4297+
public async deleteAccountData(eventType: keyof AccountDataEvents): Promise<void> {
42914298
const msc3391DeleteAccountDataServerSupport = this.canSupport.get(Feature.AccountDataDeletion);
42924299
// if deletion is not supported overwrite with empty content
42934300
if (msc3391DeleteAccountDataServerSupport === ServerSupport.Unsupported) {
@@ -4310,7 +4317,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
43104317
* @returns The array of users that are ignored (empty if none)
43114318
*/
43124319
public getIgnoredUsers(): string[] {
4313-
const event = this.getAccountData("m.ignored_user_list");
4320+
const event = this.getAccountData(EventType.IgnoredUserList);
43144321
if (!event?.getContent()["ignored_users"]) return [];
43154322
return Object.keys(event.getContent()["ignored_users"]);
43164323
}
@@ -4326,7 +4333,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
43264333
userIds.forEach((u) => {
43274334
content.ignored_users[u] = {};
43284335
});
4329-
return this.setAccountData("m.ignored_user_list", content);
4336+
return this.setAccountData(EventType.IgnoredUserList, content);
43304337
}
43314338

43324339
/**
@@ -9264,7 +9271,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
92649271
deviceId: string,
92659272
notificationSettings: LocalNotificationSettings,
92669273
): Promise<{}> {
9267-
const key = `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}`;
9274+
const key = `${LOCAL_NOTIFICATION_SETTINGS_PREFIX.name}.${deviceId}` as const;
92689275
return this.setAccountData(key, notificationSettings);
92699276
}
92709277

src/crypto/CrossSigning.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export class CrossSigningInfo {
184184
}
185185
}
186186
}
187-
for (const type of ["self_signing", "user_signing"]) {
187+
for (const type of ["self_signing", "user_signing"] as const) {
188188
intersect((await secretStorage.isStored(`m.cross_signing.${type}`)) || {});
189189
}
190190
return Object.keys(stored).length ? stored : null;

0 commit comments

Comments
 (0)