diff --git a/registration.sample.yml b/registration.sample.yml index 4f6cf6a33..4ebcbc864 100644 --- a/registration.sample.yml +++ b/registration.sample.yml @@ -21,4 +21,5 @@ url: "http://localhost:9993" # This should match the bridge.port in your config rate_limited: false # If enabling encryption -de.sorunome.msc2409.push_ephemeral: true, \ No newline at end of file +de.sorunome.msc2409.push_ephemeral: true +org.matrix.msc3202: true \ No newline at end of file diff --git a/src/Bridge.ts b/src/Bridge.ts index ce7399ade..8b8cc206f 100644 --- a/src/Bridge.ts +++ b/src/Bridge.ts @@ -171,6 +171,11 @@ export class Bridge { return this.onRoomJoin(roomId, event); }); + this.as.on("room.failed_decryption", (roomId, event, err) => { + log.warn(`Failed to decrypt event ${event.event_id} from ${roomId}: ${err.message}`); + Metrics.matrixAppserviceDecryptionFailed.inc(); + }); + this.queue.subscribe("response.matrix.message"); this.queue.subscribe("notifications.user.events"); this.queue.subscribe("github.*"); diff --git a/src/Config/Config.ts b/src/Config/Config.ts index dfe7ca4a0..fb1ad3912 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -479,6 +479,10 @@ export class BridgeConfig { if (this.encryption && !this.queue.monolithic) { throw new ConfigError("queue.monolithic", "Encryption is not supported in worker mode yet."); } + + if (this.encryption && !this.queue.port) { + throw new ConfigError("queue.port", "You must enable redis support for encryption to work."); + } } public async prefillMembershipCache(client: MatrixClient) { diff --git a/src/Metrics.ts b/src/Metrics.ts index 63e6308f8..2c82f2608 100644 --- a/src/Metrics.ts +++ b/src/Metrics.ts @@ -20,6 +20,7 @@ export class Metrics { private readonly matrixApiCallsFailed = new Counter({ name: "matrix_api_calls_failed", help: "The number of Matrix client API calls which failed", labelNames: ["method"], registers: [this.registry]}); public readonly matrixAppserviceEvents = new Counter({ name: "matrix_appservice_events", help: "The number of events sent over the AS API", labelNames: [], registers: [this.registry]}); + public readonly matrixAppserviceDecryptionFailed = new Counter({ name: "matrix_appservice_decryption_failed", help: "The number of events sent over the AS API that failed to decrypt", registers: [this.registry]}); constructor(private registry: Registry = register) { this.expressRouter.get('/metrics', this.metricsFunc.bind(this)); diff --git a/src/Stores/RedisStorageProvider.ts b/src/Stores/RedisStorageProvider.ts index bb982d5c4..b0f5e5948 100644 --- a/src/Stores/RedisStorageProvider.ts +++ b/src/Stores/RedisStorageProvider.ts @@ -3,7 +3,7 @@ import { Redis, default as redis } from "ioredis"; import LogWrapper from "../LogWrapper"; import { IBridgeStorageProvider } from "./StorageProvider"; -import { IFilterInfo } from "matrix-bot-sdk"; +import { IFilterInfo, IStorageProvider } from "matrix-bot-sdk"; import { ProvisionSession } from "matrix-appservice-bridge"; const BOT_SYNC_TOKEN_KEY = "bot.sync_token."; @@ -25,15 +25,8 @@ const WIDGET_USER_TOKENS = "widgets.user-tokens."; const log = new LogWrapper("RedisASProvider"); -export class RedisStorageProvider implements IBridgeStorageProvider { - private redis: Redis; - - constructor(host: string, port: number, private contextSuffix = '') { - this.redis = new redis(port, host); - this.redis.expire(COMPLETED_TRANSACTIONS_KEY, COMPLETED_TRANSACTIONS_EXPIRE_AFTER).catch((ex) => { - log.warn("Failed to set expiry time on as.completed_transactions", ex); - }); - } +export class RedisStorageContextualProvider implements IStorageProvider { + constructor(protected readonly redis: Redis, protected readonly contextSuffix = "") { } public setSyncToken(token: string|null){ if (token === null) { @@ -64,6 +57,16 @@ export class RedisStorageProvider implements IBridgeStorageProvider { return this.redis.get(`${BOT_VALUE_KEY}${this.contextSuffix}.${key}`); } +} + +export class RedisStorageProvider extends RedisStorageContextualProvider implements IBridgeStorageProvider { + constructor(host: string, port: number, contextSuffix = '') { + super(new redis(port, host), contextSuffix); + this.redis.expire(COMPLETED_TRANSACTIONS_KEY, COMPLETED_TRANSACTIONS_EXPIRE_AFTER).catch((ex) => { + log.warn("Failed to set expiry time on as.completed_transactions", ex); + }); + } + public async addRegisteredUser(userId: string) { this.redis.sadd(REGISTERED_USERS_KEY, [userId]); } @@ -155,4 +158,12 @@ export class RedisStorageProvider implements IBridgeStorageProvider { token = await this.redis.spop(`${WIDGET_USER_TOKENS}${userId}`); } } + + storageForUser(userId: string) { + const newContext = [userId]; + if (this.contextSuffix) { + newContext.push(this.contextSuffix); + } + return new RedisStorageContextualProvider(this.redis, newContext.join(".")); + } }