From 5b6ffb61823358327e777b72dffe3bec5068f635 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 4 Sep 2025 19:05:07 -0300 Subject: [PATCH 1/8] feat: adds get eventId and acl middleware --- .../src/api/_matrix/transactions.ts | 11 +++-- .../federation-matrix/src/api/middlewares.ts | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 ee/packages/federation-matrix/src/api/middlewares.ts diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index 27a58a054b8dc..460e7e7dbd7a7 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -3,6 +3,8 @@ import type { EventID } from '@hs/room'; import { Router } from '@rocket.chat/http-router'; import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv'; +import { aclMiddleware } from '../middlewares'; + const SendTransactionParamsSchema = { type: 'object', properties: { @@ -252,7 +254,7 @@ const GetStateResponseSchema = { const isGetStateResponseProps = ajv.compile(GetStateResponseSchema); export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { - const { event, config } = services; + const { event, federationAuth } = services; // PUT /_matrix/federation/v1/send/{txnId} return ( @@ -373,6 +375,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { tags: ['Federation'], license: ['federation'], }, + aclMiddleware(federationAuth), async (c) => { const eventData = await event.getEventById(c.req.param('eventId') as EventID); if (!eventData) { @@ -387,9 +390,9 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { return { body: { - origin_server_ts: new Date().getTime(), - origin: config.serverName, - pdus: [eventData.event], + origin_server_ts: eventData.origin_server_ts, + origin: eventData.origin, + pdus: [eventData], }, statusCode: 200, }; diff --git a/ee/packages/federation-matrix/src/api/middlewares.ts b/ee/packages/federation-matrix/src/api/middlewares.ts new file mode 100644 index 0000000000000..c9ed1e7402e42 --- /dev/null +++ b/ee/packages/federation-matrix/src/api/middlewares.ts @@ -0,0 +1,43 @@ +import type { EventAuthorizationService } from '@hs/federation-sdk'; +import type { Context, Next } from 'hono'; + +const errCodes = { + M_UNAUTHORIZED: { + errcode: 'M_UNAUTHORIZED', + error: 'Invalid or missing signature', + }, + M_FORBIDDEN: { + errcode: 'M_FORBIDDEN', + error: 'Access denied', + }, + M_UNKNOWN: { + errcode: 'M_UNKNOWN', + error: 'Internal server error while processing request', + }, +}; + +export const aclMiddleware = (federationAuth: EventAuthorizationService) => async (c: Context, next: Next) => { + const { eventId } = c.req.param(); + + try { + const verificationResult = await federationAuth.verifyRequestSignature({ + method: c.req.method, + uri: c.req.path, + headers: Object.fromEntries(c.req.raw.headers.entries()), + body: undefined, // GET requests don't have body + }); + + if (!verificationResult.valid) { + return c.json(errCodes.M_UNAUTHORIZED, 401); + } + + const authResult = await federationAuth.canAccessEvent(eventId, verificationResult.serverName); + if (!authResult.authorized) { + return c.json(errCodes.M_FORBIDDEN, 403); + } + + return next(); + } catch (error) { + return c.json(errCodes.M_UNKNOWN, 500); + } +}; From 13a27939c13f686b808412ca345757ee7bc78479 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 4 Sep 2025 20:01:40 -0300 Subject: [PATCH 2/8] makes return right props on get event route --- .../federation-matrix/src/api/_matrix/transactions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index 460e7e7dbd7a7..b46ab9cef2060 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -254,7 +254,7 @@ const GetStateResponseSchema = { const isGetStateResponseProps = ajv.compile(GetStateResponseSchema); export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { - const { event, federationAuth } = services; + const { event, federationAuth, config } = services; // PUT /_matrix/federation/v1/send/{txnId} return ( @@ -390,9 +390,9 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { return { body: { - origin_server_ts: eventData.origin_server_ts, - origin: eventData.origin, - pdus: [eventData], + origin_server_ts: eventData.event.origin_server_ts, + origin: config.serverName, + pdus: [eventData.event], }, statusCode: 200, }; From 1199851c2351113b23f2609f97d847f50d753713 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Wed, 10 Sep 2025 17:49:46 -0300 Subject: [PATCH 3/8] removes ACL to isolate into a proper PR --- .../src/api/_matrix/transactions.ts | 5 +-- .../federation-matrix/src/api/middlewares.ts | 43 ------------------- 2 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 ee/packages/federation-matrix/src/api/middlewares.ts diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index b46ab9cef2060..262988b690efa 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -3,8 +3,6 @@ import type { EventID } from '@hs/room'; import { Router } from '@rocket.chat/http-router'; import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv'; -import { aclMiddleware } from '../middlewares'; - const SendTransactionParamsSchema = { type: 'object', properties: { @@ -254,7 +252,7 @@ const GetStateResponseSchema = { const isGetStateResponseProps = ajv.compile(GetStateResponseSchema); export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { - const { event, federationAuth, config } = services; + const { event, config } = services; // PUT /_matrix/federation/v1/send/{txnId} return ( @@ -375,7 +373,6 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { tags: ['Federation'], license: ['federation'], }, - aclMiddleware(federationAuth), async (c) => { const eventData = await event.getEventById(c.req.param('eventId') as EventID); if (!eventData) { diff --git a/ee/packages/federation-matrix/src/api/middlewares.ts b/ee/packages/federation-matrix/src/api/middlewares.ts deleted file mode 100644 index c9ed1e7402e42..0000000000000 --- a/ee/packages/federation-matrix/src/api/middlewares.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { EventAuthorizationService } from '@hs/federation-sdk'; -import type { Context, Next } from 'hono'; - -const errCodes = { - M_UNAUTHORIZED: { - errcode: 'M_UNAUTHORIZED', - error: 'Invalid or missing signature', - }, - M_FORBIDDEN: { - errcode: 'M_FORBIDDEN', - error: 'Access denied', - }, - M_UNKNOWN: { - errcode: 'M_UNKNOWN', - error: 'Internal server error while processing request', - }, -}; - -export const aclMiddleware = (federationAuth: EventAuthorizationService) => async (c: Context, next: Next) => { - const { eventId } = c.req.param(); - - try { - const verificationResult = await federationAuth.verifyRequestSignature({ - method: c.req.method, - uri: c.req.path, - headers: Object.fromEntries(c.req.raw.headers.entries()), - body: undefined, // GET requests don't have body - }); - - if (!verificationResult.valid) { - return c.json(errCodes.M_UNAUTHORIZED, 401); - } - - const authResult = await federationAuth.canAccessEvent(eventId, verificationResult.serverName); - if (!authResult.authorized) { - return c.json(errCodes.M_FORBIDDEN, 403); - } - - return next(); - } catch (error) { - return c.json(errCodes.M_UNKNOWN, 500); - } -}; From 4bd1e6d96501edae18b8f636714ee45281515590 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Wed, 10 Sep 2025 17:51:49 -0300 Subject: [PATCH 4/8] makes origin_server_ts as now --- ee/packages/federation-matrix/src/api/_matrix/transactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index 262988b690efa..27a58a054b8dc 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -387,7 +387,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { return { body: { - origin_server_ts: eventData.event.origin_server_ts, + origin_server_ts: new Date().getTime(), origin: config.serverName, pdus: [eventData.event], }, From 7113c568d8e6ff4cd217736b592a3ad76da8aeb8 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 11 Sep 2025 00:05:59 -0300 Subject: [PATCH 5/8] feat: adds event ACL --- .../src/api/_matrix/transactions.ts | 5 +- .../federation-matrix/src/api/middlewares.ts | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 ee/packages/federation-matrix/src/api/middlewares.ts diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index 27a58a054b8dc..e4a6350809ed1 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -3,6 +3,8 @@ import type { EventID } from '@hs/room'; import { Router } from '@rocket.chat/http-router'; import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv'; +import { canAccessEvent } from '../middlewares'; + const SendTransactionParamsSchema = { type: 'object', properties: { @@ -252,7 +254,7 @@ const GetStateResponseSchema = { const isGetStateResponseProps = ajv.compile(GetStateResponseSchema); export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { - const { event, config } = services; + const { event, config, federationAuth } = services; // PUT /_matrix/federation/v1/send/{txnId} return ( @@ -373,6 +375,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { tags: ['Federation'], license: ['federation'], }, + canAccessEvent(federationAuth), async (c) => { const eventData = await event.getEventById(c.req.param('eventId') as EventID); if (!eventData) { diff --git a/ee/packages/federation-matrix/src/api/middlewares.ts b/ee/packages/federation-matrix/src/api/middlewares.ts new file mode 100644 index 0000000000000..6385ed215d7ea --- /dev/null +++ b/ee/packages/federation-matrix/src/api/middlewares.ts @@ -0,0 +1,49 @@ +import type { EventAuthorizationService } from '@hs/federation-sdk'; +import type { Context, Next } from 'hono'; +import type { ContentfulStatusCode } from 'hono/utils/http-status'; + +const errCodes: Record = { + M_UNAUTHORIZED: { + errcode: 'M_UNAUTHORIZED', + error: 'Invalid or missing signature', + status: 401, + }, + M_FORBIDDEN: { + errcode: 'M_FORBIDDEN', + error: 'Access denied', + status: 403, + }, + M_UNKNOWN: { + errcode: 'M_UNKNOWN', + error: 'Internal server error while processing request', + status: 500, + }, +}; + +export const canAccessEvent = (federationAuth: EventAuthorizationService) => async (c: Context, next: Next) => { + const { eventId } = c.req.param(); + + try { + const verificationResult = await federationAuth.canAccessEventFromAuthorizationHeader( + eventId, + c.req.header('Authorization') || '', + c.req.method, + c.req.path, + await c.req.json(), + ); + + if (!verificationResult.authorized) { + return c.json( + { + errcode: errCodes[verificationResult.errorCode].errcode, + error: errCodes[verificationResult.errorCode].error, + }, + errCodes[verificationResult.errorCode].status, + ); + } + + return next(); + } catch (error) { + return c.json(errCodes.M_UNKNOWN, 500); + } +}; From e3df66907fef7cda6bb45e36179b4865c244f7c4 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Mon, 15 Sep 2025 14:07:52 -0300 Subject: [PATCH 6/8] chore: load errCodes from federation-sdk --- .../federation-matrix/src/api/middlewares.ts | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/ee/packages/federation-matrix/src/api/middlewares.ts b/ee/packages/federation-matrix/src/api/middlewares.ts index 6385ed215d7ea..6c7c97b83836f 100644 --- a/ee/packages/federation-matrix/src/api/middlewares.ts +++ b/ee/packages/federation-matrix/src/api/middlewares.ts @@ -1,35 +1,18 @@ import type { EventAuthorizationService } from '@hs/federation-sdk'; +import { errCodes } from '@hs/federation-sdk'; import type { Context, Next } from 'hono'; -import type { ContentfulStatusCode } from 'hono/utils/http-status'; - -const errCodes: Record = { - M_UNAUTHORIZED: { - errcode: 'M_UNAUTHORIZED', - error: 'Invalid or missing signature', - status: 401, - }, - M_FORBIDDEN: { - errcode: 'M_FORBIDDEN', - error: 'Access denied', - status: 403, - }, - M_UNKNOWN: { - errcode: 'M_UNKNOWN', - error: 'Internal server error while processing request', - status: 500, - }, -}; export const canAccessEvent = (federationAuth: EventAuthorizationService) => async (c: Context, next: Next) => { - const { eventId } = c.req.param(); - try { + const url = new URL(c.req.url); + const path = url.search ? `${c.req.path}${url.search}` : c.req.path; + const verificationResult = await federationAuth.canAccessEventFromAuthorizationHeader( - eventId, + c.req.param('eventId'), c.req.header('Authorization') || '', c.req.method, - c.req.path, - await c.req.json(), + path, + undefined, ); if (!verificationResult.authorized) { From aead857952b4db280803f89591af8a897dc35ef8 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Mon, 15 Sep 2025 14:11:59 -0300 Subject: [PATCH 7/8] refactor: makes getEventById route returns event origin and ts --- .../federation-matrix/src/api/_matrix/transactions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index e4a6350809ed1..3aee495153bd0 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -254,7 +254,7 @@ const GetStateResponseSchema = { const isGetStateResponseProps = ajv.compile(GetStateResponseSchema); export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { - const { event, config, federationAuth } = services; + const { event, federationAuth } = services; // PUT /_matrix/federation/v1/send/{txnId} return ( @@ -390,8 +390,8 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { return { body: { - origin_server_ts: new Date().getTime(), - origin: config.serverName, + origin_server_ts: eventData.origin_server_ts, + origin: eventData.origin, pdus: [eventData.event], }, statusCode: 200, From 61368732f1aa0496ec295f130f7496d9c1323b8d Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Wed, 17 Sep 2025 15:01:47 -0300 Subject: [PATCH 8/8] fixes event response and input param type --- ee/packages/federation-matrix/src/api/_matrix/transactions.ts | 2 +- ee/packages/federation-matrix/src/api/middlewares.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts index 3aee495153bd0..31f94e0bd454f 100644 --- a/ee/packages/federation-matrix/src/api/_matrix/transactions.ts +++ b/ee/packages/federation-matrix/src/api/_matrix/transactions.ts @@ -390,7 +390,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => { return { body: { - origin_server_ts: eventData.origin_server_ts, + origin_server_ts: eventData.event.origin_server_ts, origin: eventData.origin, pdus: [eventData.event], }, diff --git a/ee/packages/federation-matrix/src/api/middlewares.ts b/ee/packages/federation-matrix/src/api/middlewares.ts index 6c7c97b83836f..bcfff8dada781 100644 --- a/ee/packages/federation-matrix/src/api/middlewares.ts +++ b/ee/packages/federation-matrix/src/api/middlewares.ts @@ -1,5 +1,6 @@ import type { EventAuthorizationService } from '@hs/federation-sdk'; import { errCodes } from '@hs/federation-sdk'; +import type { EventID } from '@hs/room'; import type { Context, Next } from 'hono'; export const canAccessEvent = (federationAuth: EventAuthorizationService) => async (c: Context, next: Next) => { @@ -8,7 +9,7 @@ export const canAccessEvent = (federationAuth: EventAuthorizationService) => asy const path = url.search ? `${c.req.path}${url.search}` : c.req.path; const verificationResult = await federationAuth.canAccessEventFromAuthorizationHeader( - c.req.param('eventId'), + c.req.param('eventId') as EventID, c.req.header('Authorization') || '', c.req.method, path,