Skip to content

Commit 620670b

Browse files
authored
feat: adds Federation canAccessMedia middleware (#36926)
1 parent c04b7b3 commit 620670b

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

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

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

99
import { MatrixMediaService } from '../../services/MatrixMediaService';
10+
import { canAccessMedia } from '../middlewares';
1011

1112
const logger = new Logger('federation-matrix:media');
1213

@@ -76,7 +77,7 @@ async function getMediaFile(mediaId: string, serverName: string): Promise<{ file
7677
}
7778

7879
export const getMatrixMediaRoutes = (homeserverServices: HomeserverServices) => {
79-
const { config } = homeserverServices;
80+
const { config, federationAuth } = homeserverServices;
8081
const router = new Router('/federation');
8182

8283
router.get(
@@ -86,12 +87,14 @@ export const getMatrixMediaRoutes = (homeserverServices: HomeserverServices) =>
8687
response: {
8788
200: isBufferResponseProps,
8889
401: isErrorResponseProps,
90+
403: isErrorResponseProps,
8991
404: isErrorResponseProps,
9092
429: isErrorResponseProps,
9193
500: isErrorResponseProps,
9294
},
9395
tags: ['Federation', 'Media'],
9496
},
97+
canAccessMedia(federationAuth),
9598
async (c) => {
9699
try {
97100
const { mediaId } = c.req.param();
@@ -122,7 +125,6 @@ export const getMatrixMediaRoutes = (homeserverServices: HomeserverServices) =>
122125
body: multipartResponse.body,
123126
};
124127
} catch (error) {
125-
logger.error('Federation media download error:', error);
126128
return {
127129
statusCode: 500,
128130
body: { errcode: 'M_UNKNOWN', error: 'Internal server error' },
@@ -138,10 +140,12 @@ export const getMatrixMediaRoutes = (homeserverServices: HomeserverServices) =>
138140
response: {
139141
200: isBufferResponseProps,
140142
401: isErrorResponseProps,
143+
403: isErrorResponseProps,
141144
404: isErrorResponseProps,
142145
},
143146
tags: ['Federation', 'Media'],
144147
},
148+
canAccessMedia(federationAuth),
145149
async (context: any) => {
146150
try {
147151
const { mediaId } = context.req.param();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { EventAuthorizationService } from '@hs/federation-sdk';
2+
import type { Context, Next } from 'hono';
3+
import type { ContentfulStatusCode } from 'hono/utils/http-status';
4+
5+
const errCodes: Record<string, { errcode: string; error: string; status: ContentfulStatusCode }> = {
6+
M_UNAUTHORIZED: {
7+
errcode: 'M_UNAUTHORIZED',
8+
error: 'Invalid or missing signature',
9+
status: 401,
10+
},
11+
M_FORBIDDEN: {
12+
errcode: 'M_FORBIDDEN',
13+
error: 'Access denied',
14+
status: 403,
15+
},
16+
M_UNKNOWN: {
17+
errcode: 'M_UNKNOWN',
18+
error: 'Internal server error while processing request',
19+
status: 500,
20+
},
21+
};
22+
23+
export const canAccessMedia = (federationAuth: EventAuthorizationService) => {
24+
return async (c: Context, next: Next) => {
25+
const mediaId = c.req.param('mediaId');
26+
const authHeader = c.req.header('Authorization') || '';
27+
const { method } = c.req;
28+
const { path } = c.req;
29+
const query = c.req.query();
30+
31+
let uriForSignature = path;
32+
const queryString = new URLSearchParams(query).toString();
33+
if (queryString) {
34+
uriForSignature = `${path}?${queryString}`;
35+
}
36+
37+
try {
38+
const body = method === 'GET' ? undefined : await c.req.json();
39+
40+
const verificationResult = await federationAuth.canAccessMediaFromAuthorizationHeader(
41+
mediaId,
42+
authHeader,
43+
method,
44+
uriForSignature, // use URI with query params for signature verification
45+
body,
46+
);
47+
48+
if (!verificationResult.authorized) {
49+
const errorResponse = errCodes[verificationResult.errorCode];
50+
return c.json(
51+
{
52+
errcode: errorResponse.errcode,
53+
error: errorResponse.error,
54+
},
55+
errorResponse.status,
56+
);
57+
}
58+
59+
return next();
60+
} catch (error) {
61+
return c.json(errCodes.M_UNKNOWN, 500);
62+
}
63+
};
64+
};

ee/packages/federation-matrix/src/services/MatrixMediaService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ export class MatrixMediaService {
112112
}
113113

114114
const buffer = await this.homeserverServices.media.downloadFromRemoteServer(parts.serverName, parts.mediaId);
115+
if (!buffer) {
116+
throw new Error('Download from remote server returned null content.');
117+
}
115118

116119
const uploadedFile = await Upload.uploadFile({
117120
userId: metadata.userId || 'federation',

0 commit comments

Comments
 (0)