Skip to content

Commit 3580f62

Browse files
committed
refactor canAccessResource middleware
1 parent 3ef9d03 commit 3580f62

File tree

7 files changed

+117
-112
lines changed

7 files changed

+117
-112
lines changed

ee/packages/federation-matrix/src/api/_matrix/invite.ts

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { Rooms, Users } from '@rocket.chat/models';
1414
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
1515

1616
import { createOrUpdateFederatedUser, getUsernameServername } from '../../FederationMatrix';
17-
import { isAuthenticatedMiddleware, canAccessResourceMiddleware } from '../middlewares';
17+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
1818

1919
const EventBaseSchema = {
2020
type: 'object',
@@ -319,60 +319,57 @@ export const acceptInvite = async (
319319
export const getMatrixInviteRoutes = (services: HomeserverServices) => {
320320
const { invite, state, room, federationAuth } = services;
321321

322-
return new Router('/federation')
323-
.use(isAuthenticatedMiddleware(federationAuth))
324-
.use(canAccessResourceMiddleware(federationAuth))
325-
.put(
326-
'/v2/invite/:roomId/:eventId',
327-
{
328-
body: ajv.compile({ type: 'object' }), // TODO: add schema from room package.
329-
params: isProcessInviteParamsProps,
330-
response: {
331-
200: isProcessInviteResponseProps,
332-
},
333-
tags: ['Federation'],
334-
license: ['federation'],
322+
return new Router('/federation').use(canAccessResourceMiddleware(federationAuth, 'room')).put(
323+
'/v2/invite/:roomId/:eventId',
324+
{
325+
body: ajv.compile({ type: 'object' }), // TODO: add schema from room package.
326+
params: isProcessInviteParamsProps,
327+
response: {
328+
200: isProcessInviteResponseProps,
335329
},
336-
async (c) => {
337-
const { roomId, eventId } = c.req.param();
338-
const { event, room_version: roomVersion } = await c.req.json();
330+
tags: ['Federation'],
331+
license: ['federation'],
332+
},
333+
async (c) => {
334+
const { roomId, eventId } = c.req.param();
335+
const { event, room_version: roomVersion } = await c.req.json();
339336

340-
const userToCheck = event.state_key as string;
337+
const userToCheck = event.state_key as string;
341338

342-
if (!userToCheck) {
343-
throw new Error('join event has missing state key, unable to determine user to join');
344-
}
339+
if (!userToCheck) {
340+
throw new Error('join event has missing state key, unable to determine user to join');
341+
}
345342

346-
const [username /* domain */] = userToCheck.split(':');
343+
const [username /* domain */] = userToCheck.split(':');
347344

348-
// TODO: check domain
345+
// TODO: check domain
349346

350-
const ourUser = await Users.findOneByUsername(username.slice(1));
347+
const ourUser = await Users.findOneByUsername(username.slice(1));
351348

352-
if (!ourUser) {
353-
throw new Error('user not found not processing invite');
354-
}
349+
if (!ourUser) {
350+
throw new Error('user not found not processing invite');
351+
}
355352

356353
const inviteEvent = await invite.processInvite(event, roomIdSchema.parse(roomId), eventIdSchema.parse(eventId), roomVersion);
357354

358-
setTimeout(
359-
() => {
360-
void startJoiningRoom({
361-
inviteEvent,
362-
user: ourUser,
363-
room,
364-
state,
365-
});
366-
},
367-
inviteEvent.event.content.is_direct ? 2000 : 0,
368-
);
369-
370-
return {
371-
body: {
372-
event: inviteEvent.event,
373-
},
374-
statusCode: 200,
375-
};
376-
},
377-
);
355+
setTimeout(
356+
() => {
357+
void startJoiningRoom({
358+
inviteEvent,
359+
user: ourUser,
360+
room,
361+
state,
362+
});
363+
},
364+
inviteEvent.event.content.is_direct ? 2000 : 0,
365+
);
366+
367+
return {
368+
body: {
369+
event: inviteEvent.event,
370+
},
371+
statusCode: 200,
372+
};
373+
},
374+
);
378375
};

ee/packages/federation-matrix/src/api/_matrix/media.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Router } from '@rocket.chat/http-router';
66
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
77

88
import { MatrixMediaService } from '../../services/MatrixMediaService';
9-
import { isAuthenticatedMiddleware, canAccessResourceMiddleware } from '../middlewares';
9+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
1010

1111
const MediaDownloadParamsSchema = {
1212
type: 'object',
@@ -76,8 +76,7 @@ async function getMediaFile(mediaId: string, serverName: string): Promise<{ file
7676
export const getMatrixMediaRoutes = (homeserverServices: HomeserverServices) => {
7777
const { config, federationAuth } = homeserverServices;
7878
return new Router('/federation')
79-
.use(isAuthenticatedMiddleware(federationAuth))
80-
.use(canAccessResourceMiddleware(federationAuth))
79+
.use(canAccessResourceMiddleware(federationAuth, 'media'))
8180
.get(
8281
'/v1/media/download/:mediaId',
8382
{

ee/packages/federation-matrix/src/api/_matrix/profiles.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { eventIdSchema, roomIdSchema, userIdSchema, type HomeserverServices, typ
22
import { Router } from '@rocket.chat/http-router';
33
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
44

5-
import { canAccessResourceMiddleware, isAuthenticatedMiddleware } from '../middlewares';
5+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
6+
import { isAuthenticatedMiddleware } from '../middlewares/isAuthenticated';
67

78
const UsernameSchema = {
89
type: 'string',
@@ -356,7 +357,6 @@ export const getMatrixProfilesRoutes = (services: HomeserverServices) => {
356357

357358
return new Router('/federation')
358359
.use(isAuthenticatedMiddleware(federationAuth))
359-
.use(canAccessResourceMiddleware(federationAuth))
360360
.get(
361361
'/v1/query/profile',
362362
{
@@ -418,17 +418,17 @@ export const getMatrixProfilesRoutes = (services: HomeserverServices) => {
418418
tags: ['Federation'],
419419
license: ['federation'],
420420
},
421-
async (c) => {
422-
const { userId } = c.req.param();
423-
424-
const response = await profile.getDevices(userId);
425-
421+
async (_c) => {
426422
return {
427-
body: response,
428-
statusCode: 200,
423+
body: {
424+
errcode: 'M_UNRECOGNIZED',
425+
error: 'This endpoint is not implemented on the homeserver side',
426+
},
427+
statusCode: 501,
429428
};
430429
},
431430
)
431+
.use(canAccessResourceMiddleware(federationAuth, 'room'))
432432
.get(
433433
'/v1/make_join/:roomId/:userId',
434434
{

ee/packages/federation-matrix/src/api/_matrix/send-join.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { HomeserverServices, EventID } from '@rocket.chat/federation-sdk';
22
import { Router } from '@rocket.chat/http-router';
33
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
44

5-
import { isAuthenticatedMiddleware, canAccessResourceMiddleware } from '../middlewares';
5+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
66

77
const UsernameSchema = {
88
type: 'string',
@@ -226,30 +226,27 @@ const isSendJoinResponseProps = ajv.compile(SendJoinResponseSchema);
226226
export const getMatrixSendJoinRoutes = (services: HomeserverServices) => {
227227
const { sendJoin, federationAuth } = services;
228228

229-
return new Router('/federation')
230-
.use(isAuthenticatedMiddleware(federationAuth))
231-
.use(canAccessResourceMiddleware(federationAuth))
232-
.put(
233-
'/v2/send_join/:roomId/:stateKey',
234-
{
235-
params: isSendJoinParamsProps,
236-
body: isSendJoinEventProps,
237-
response: {
238-
200: isSendJoinResponseProps,
239-
},
240-
tags: ['Federation'],
241-
license: ['federation'],
229+
return new Router('/federation').use(canAccessResourceMiddleware(federationAuth, 'room')).put(
230+
'/v2/send_join/:roomId/:stateKey',
231+
{
232+
params: isSendJoinParamsProps,
233+
body: isSendJoinEventProps,
234+
response: {
235+
200: isSendJoinResponseProps,
242236
},
243-
async (c) => {
244-
const { roomId, stateKey } = c.req.param();
245-
const body = await c.req.json();
237+
tags: ['Federation'],
238+
license: ['federation'],
239+
},
240+
async (c) => {
241+
const { roomId, stateKey } = c.req.param();
242+
const body = await c.req.json();
246243

247-
const response = await sendJoin.sendJoin(roomId, stateKey as EventID, body);
244+
const response = await sendJoin.sendJoin(roomId, stateKey as EventID, body);
248245

249-
return {
250-
body: response,
251-
statusCode: 200,
252-
};
253-
},
254-
);
246+
return {
247+
body: response,
248+
statusCode: 200,
249+
};
250+
},
251+
);
255252
};

ee/packages/federation-matrix/src/api/_matrix/transactions.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import type { HomeserverServices, EventID } from '@rocket.chat/federation-sdk';
22
import { Router } from '@rocket.chat/http-router';
33
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
44

5-
import { isAuthenticatedMiddleware, canAccessResourceMiddleware } from '../middlewares';
5+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
6+
import { isAuthenticatedMiddleware } from '../middlewares/isAuthenticated';
67

78
const SendTransactionParamsSchema = {
89
type: 'object',
@@ -320,7 +321,6 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
320321
return (
321322
new Router('/federation')
322323
.use(isAuthenticatedMiddleware(federationAuth))
323-
.use(canAccessResourceMiddleware(federationAuth))
324324
.put(
325325
'/v1/send/:txnId',
326326
{
@@ -367,7 +367,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
367367
)
368368

369369
// GET /_matrix/federation/v1/state_ids/{roomId}
370-
370+
.use(canAccessResourceMiddleware(federationAuth, 'room'))
371371
.get(
372372
'/v1/state_ids/:roomId',
373373
{
@@ -427,6 +427,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
427427
},
428428
)
429429
// GET /_matrix/federation/v1/event/{eventId}
430+
.use(canAccessResourceMiddleware(federationAuth, 'event'))
430431
.get(
431432
'/v1/event/:eventId',
432433
{
Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,55 @@
11
import { errCodes } from '@rocket.chat/federation-sdk';
22
import type { EventAuthorizationService } from '@rocket.chat/federation-sdk';
3+
import { every } from 'hono/combine';
34
import { createMiddleware } from 'hono/factory';
45

5-
function extractEntityId(params: {
6-
eventId?: string;
7-
mediaId?: string;
8-
roomId?: string;
9-
}): { type: 'event' | 'media' | 'room'; id: string } | undefined {
10-
if (params.eventId) {
11-
return { type: 'event', id: params.eventId };
6+
import { isAuthenticatedMiddleware } from './isAuthenticated';
7+
8+
function extractEntityId(params: { eventId?: string; mediaId?: string; roomId?: string }, entityType: 'event' | 'media' | 'room'): string {
9+
if (entityType === 'room') {
10+
const { roomId } = params;
11+
if (!roomId) {
12+
throw new Error('Room ID is required');
13+
}
14+
15+
return roomId;
1216
}
1317

14-
if (params.mediaId) {
15-
return { type: 'media', id: params.mediaId };
18+
if (entityType === 'media') {
19+
const { mediaId } = params;
20+
if (!mediaId) {
21+
throw new Error('Media ID is required');
22+
}
23+
24+
return mediaId;
1625
}
1726

18-
if (params.roomId) {
19-
return { type: 'room', id: params.roomId };
27+
if (entityType === 'event') {
28+
const { eventId } = params;
29+
if (!eventId) {
30+
throw new Error('Event ID is required');
31+
}
32+
33+
return eventId;
2034
}
35+
36+
throw new Error('Invalid entity type');
2137
}
2238

23-
export const canAccessResourceMiddleware = (federationAuth: EventAuthorizationService) =>
39+
const canAccessResource = (federationAuth: EventAuthorizationService, entityType: 'event' | 'media' | 'room') =>
2440
createMiddleware(async (c, next) => {
2541
try {
2642
const authenticatedServer = c.get('authenticatedServer');
2743
if (!authenticatedServer) {
2844
return c.json({ errcode: 'M_UNAUTHORIZED', error: 'Missing Authorization header' }, 401);
2945
}
3046

31-
const entity = extractEntityId(c.req.param());
32-
if (!entity) {
33-
// we're assuming there's no entity ID to get from route path params, so we're skipping the middleware
34-
// seems secure cause in case you don't have entity ID on the path the router will end up with 404 or routing to a different resource
35-
return next();
36-
}
37-
38-
const verificationResult = await federationAuth.canAccessResource(entity.type, entity.id, authenticatedServer);
39-
if (!verificationResult) {
47+
const resourceAccess = await federationAuth.canAccessResource(
48+
entityType,
49+
extractEntityId(c.req.param(), entityType),
50+
authenticatedServer,
51+
);
52+
if (!resourceAccess) {
4053
return c.json(
4154
{
4255
errcode: 'M_FORBIDDEN',
@@ -51,3 +64,6 @@ export const canAccessResourceMiddleware = (federationAuth: EventAuthorizationSe
5164
return c.json(errCodes.M_UNKNOWN, 500);
5265
}
5366
});
67+
68+
export const canAccessResourceMiddleware = (federationAuth: EventAuthorizationService, entityType: 'event' | 'media' | 'room') =>
69+
every(isAuthenticatedMiddleware(federationAuth), canAccessResource(federationAuth, entityType));

ee/packages/federation-matrix/src/api/middlewares/index.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)