Skip to content

Commit 05ee026

Browse files
committed
refactor canAccessResource middleware
1 parent 05982a0 commit 05ee026

File tree

7 files changed

+126
-121
lines changed

7 files changed

+126
-121
lines changed

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

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Router } from '@rocket.chat/http-router';
1212
import { Rooms, Users } from '@rocket.chat/models';
1313
import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
1414

15-
import { isAuthenticatedMiddleware, canAccessResourceMiddleware } from '../middlewares';
15+
import { canAccessResourceMiddleware } from '../middlewares/canAccessResource';
1616

1717
const EventBaseSchema = {
1818
type: 'object',
@@ -297,60 +297,57 @@ async function startJoiningRoom(...opts: Parameters<typeof joinRoom>) {
297297
export const getMatrixInviteRoutes = (services: HomeserverServices) => {
298298
const { invite, state, room, federationAuth } = services;
299299

300-
return new Router('/federation')
301-
.use(isAuthenticatedMiddleware(federationAuth))
302-
.use(canAccessResourceMiddleware(federationAuth))
303-
.put(
304-
'/v2/invite/:roomId/:eventId',
305-
{
306-
body: ajv.compile({ type: 'object' }), // TODO: add schema from room package.
307-
params: isProcessInviteParamsProps,
308-
response: {
309-
200: isProcessInviteResponseProps,
310-
},
311-
tags: ['Federation'],
312-
license: ['federation'],
313-
},
314-
async (c) => {
315-
const { roomId, eventId } = c.req.param();
316-
const { event, room_version: roomVersion } = await c.req.json();
317-
318-
const userToCheck = event.state_key as string;
319-
320-
if (!userToCheck) {
321-
throw new Error('join event has missing state key, unable to determine user to join');
322-
}
323-
324-
const [username /* domain */] = userToCheck.split(':');
325-
326-
// TODO: check domain
327-
328-
const ourUser = await Users.findOneByUsername(username.slice(1));
329-
330-
if (!ourUser) {
331-
throw new Error('user not found not processing invite');
332-
}
333-
334-
const inviteEvent = await invite.processInvite(event, roomId, eventId, roomVersion);
335-
336-
setTimeout(
337-
() => {
338-
void startJoiningRoom({
339-
inviteEvent,
340-
user: ourUser,
341-
room,
342-
state,
343-
});
344-
},
345-
inviteEvent.event.content.is_direct ? 2000 : 0,
346-
);
347-
348-
return {
349-
body: {
350-
event: inviteEvent.event,
351-
},
352-
statusCode: 200,
353-
};
300+
return new Router('/federation').use(canAccessResourceMiddleware(federationAuth, 'room')).put(
301+
'/v2/invite/:roomId/:eventId',
302+
{
303+
body: ajv.compile({ type: 'object' }), // TODO: add schema from room package.
304+
params: isProcessInviteParamsProps,
305+
response: {
306+
200: isProcessInviteResponseProps,
354307
},
355-
);
308+
tags: ['Federation'],
309+
license: ['federation'],
310+
},
311+
async (c) => {
312+
const { roomId, eventId } = c.req.param();
313+
const { event, room_version: roomVersion } = await c.req.json();
314+
315+
const userToCheck = event.state_key as string;
316+
317+
if (!userToCheck) {
318+
throw new Error('join event has missing state key, unable to determine user to join');
319+
}
320+
321+
const [username /* domain */] = userToCheck.split(':');
322+
323+
// TODO: check domain
324+
325+
const ourUser = await Users.findOneByUsername(username.slice(1));
326+
327+
if (!ourUser) {
328+
throw new Error('user not found not processing invite');
329+
}
330+
331+
const inviteEvent = await invite.processInvite(event, roomId, eventId, roomVersion);
332+
333+
setTimeout(
334+
() => {
335+
void startJoiningRoom({
336+
inviteEvent,
337+
user: ourUser,
338+
room,
339+
state,
340+
});
341+
},
342+
inviteEvent.event.content.is_direct ? 2000 : 0,
343+
);
344+
345+
return {
346+
body: {
347+
event: inviteEvent.event,
348+
},
349+
statusCode: 200,
350+
};
351+
},
352+
);
356353
};

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 type { HomeserverServices, RoomVersion } from '@rocket.chat/federation-sd
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',
@@ -348,7 +349,6 @@ export const getMatrixProfilesRoutes = (services: HomeserverServices) => {
348349

349350
return new Router('/federation')
350351
.use(isAuthenticatedMiddleware(federationAuth))
351-
.use(canAccessResourceMiddleware(federationAuth))
352352
.get(
353353
'/v1/query/profile',
354354
{
@@ -410,17 +410,17 @@ export const getMatrixProfilesRoutes = (services: HomeserverServices) => {
410410
tags: ['Federation'],
411411
license: ['federation'],
412412
},
413-
async (c) => {
414-
const { userId } = c.req.param();
415-
416-
const response = await profile.getDevices(userId);
417-
413+
async (_c) => {
418414
return {
419-
body: response,
420-
statusCode: 200,
415+
body: {
416+
errcode: 'M_UNRECOGNIZED',
417+
error: 'This endpoint is not implemented on the homeserver side',
418+
},
419+
statusCode: 501,
421420
};
422421
},
423422
)
423+
.use(canAccessResourceMiddleware(federationAuth, 'room'))
424424
.get(
425425
'/v1/make_join/:roomId/:userId',
426426
{

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',
@@ -259,7 +260,6 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
259260
return (
260261
new Router('/federation')
261262
.use(isAuthenticatedMiddleware(federationAuth))
262-
.use(canAccessResourceMiddleware(federationAuth))
263263
.put(
264264
'/v1/send/:txnId',
265265
{
@@ -306,7 +306,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
306306
)
307307

308308
// GET /_matrix/federation/v1/state_ids/{roomId}
309-
309+
.use(canAccessResourceMiddleware(federationAuth, 'room'))
310310
.get(
311311
'/v1/state_ids/:roomId',
312312
{
@@ -366,6 +366,7 @@ export const getMatrixTransactionsRoutes = (services: HomeserverServices) => {
366366
},
367367
)
368368
// GET /_matrix/federation/v1/event/{eventId}
369+
.use(canAccessResourceMiddleware(federationAuth, 'event'))
369370
.get(
370371
'/v1/event/:eventId',
371372
{
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)