Skip to content

Commit 1ba65cb

Browse files
committed
fix(federation): enhance user authorization checks and update end-to-end tests for federated room creation
1 parent 00605ec commit 1ba65cb

File tree

2 files changed

+162
-85
lines changed

2 files changed

+162
-85
lines changed

apps/meteor/app/lib/server/functions/createRoom.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { AppEvents, Apps } from '@rocket.chat/apps';
22
import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions';
33
import { FederationMatrix, Message, Room, Team } from '@rocket.chat/core-services';
44
import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services';
5-
import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typings';
5+
import { type ICreatedRoom, type IUser, type IRoom, type RoomType, isUserNativeFederated } from '@rocket.chat/core-typings';
66
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
77
import { Meteor } from 'meteor/meteor';
88

@@ -184,7 +184,12 @@ export const createRoom = async <T extends RoomType>(
184184

185185
const shouldBeHandledByFederation = extraData.federated === true;
186186

187-
if (shouldBeHandledByFederation && owner && !(await hasPermissionAsync(owner._id, 'access-federation'))) {
187+
if (
188+
shouldBeHandledByFederation &&
189+
owner &&
190+
!isUserNativeFederated(owner) &&
191+
!(await hasPermissionAsync(owner._id, 'access-federation'))
192+
) {
188193
throw new Meteor.Error('error-not-authorized-federation', 'Not authorized to access federation', {
189194
method: 'createRoom',
190195
});

ee/packages/federation-matrix/tests/end-to-end/permissions.spec.ts

Lines changed: 155 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import type { IUser } from '@rocket.chat/core-typings';
1+
import type { ISubscription, IUser } from '@rocket.chat/core-typings';
22

33
import type {} from '../../../../../apps/meteor/app/api/server/v1/permissions.ts';
44
import { api } from '../../../../../apps/meteor/tests/data/api-data';
5-
import { addUserToRoom, createRoom } from '../../../../../apps/meteor/tests/data/rooms.helper';
5+
import { addUserToRoom, createRoom, getSubscriptions } from '../../../../../apps/meteor/tests/data/rooms.helper';
66
import { createUser, deleteUser, getRequestConfig } from '../../../../../apps/meteor/tests/data/users.helper';
77
import type { IRequestConfig, TestUser } from '../../../../../apps/meteor/tests/data/users.helper';
88
import { IS_EE } from '../../../../../apps/meteor/tests/e2e/config/constants';
9+
import { retry } from '../../../../../apps/meteor/tests/end-to-end/api/helpers/retry.ts';
910
import { federationConfig } from '../helper/config';
1011
import { SynapseClient } from '../helper/synapse-client';
1112

@@ -40,88 +41,135 @@ import { SynapseClient } from '../helper/synapse-client';
4041
.expect(200);
4142
});
4243

43-
afterAll(async () => {
44+
afterAll(async () =>
4445
// Add permissions for access-federation to any user but admin
45-
await rc1AdminRequestConfig.request
46+
rc1AdminRequestConfig.request
4647
.post(api('permissions.update'))
4748
.set(rc1AdminRequestConfig.credentials)
4849
.send({ permissions: [{ _id: 'access-federation', roles: ['admin', 'user'] }] })
4950
.expect('Content-Type', 'application/json')
50-
.expect(200);
51-
});
51+
.expect(200),
52+
);
5253

53-
afterAll(async () => {
54-
await hs1AdminApp.close();
55-
});
54+
afterAll(async () => hs1AdminApp.close());
5655

5756
describe('Access Federation Permission', () => {
58-
it('should throw an error if a user without access-federation permission tries to create a federated room', async () => {
59-
const channelName = `federated-room-${Date.now()}`;
60-
const createResponse = await createRoom({
61-
type: 'p',
62-
name: channelName,
63-
members: [],
64-
extraData: {
65-
federated: true,
66-
},
67-
config: rc1User1RequestConfig,
57+
describe('Users without access-federation permission', () => {
58+
beforeAll(async () => {
59+
await rc1AdminRequestConfig.request
60+
.post(api('permissions.update'))
61+
.set(rc1AdminRequestConfig.credentials)
62+
.send({ permissions: [{ _id: 'access-federation', roles: ['admin'] }] })
63+
.expect('Content-Type', 'application/json')
64+
.expect(200);
6865
});
6966

70-
expect(createResponse.status).toBe(400);
71-
expect(createResponse.body).toHaveProperty('success', false);
72-
expect(createResponse.body).toHaveProperty('errorType', 'error-not-authorized-federation');
73-
});
74-
75-
it('should be able to create a federated room if the user has access-federation permission', async () => {
76-
const channelName = `federated-room-${Date.now()}`;
77-
const createResponse = await createRoom({
78-
type: 'p',
79-
name: channelName,
80-
members: [],
81-
extraData: {
82-
federated: true,
83-
},
84-
config: rc1AdminRequestConfig,
67+
afterAll(async () => {
68+
// Add permissions for access-federation to any user but admin
69+
await rc1AdminRequestConfig.request
70+
.post(api('permissions.update'))
71+
.set(rc1AdminRequestConfig.credentials)
72+
.send({ permissions: [{ _id: 'access-federation', roles: ['admin', 'user'] }] })
73+
.expect('Content-Type', 'application/json')
74+
.expect(200);
8575
});
8676

87-
expect(createResponse.status).toBe(200);
88-
expect(createResponse.body).toHaveProperty('success', true);
89-
expect(createResponse.body).toHaveProperty('group');
90-
expect(createResponse.body.group).toHaveProperty('_id');
91-
expect(createResponse.body.group).toHaveProperty('t', 'p');
92-
expect(createResponse.body.group).toHaveProperty('federated', true);
93-
});
77+
describe('Inviting from a remote server', () => {
78+
let user: TestUser<IUser>;
9479

95-
it('should not be able to add a user without access-federation permission to a room', async () => {
96-
const createResponse = await createRoom({
97-
type: 'p',
98-
name: `federated-room-${Date.now()}`,
99-
members: [],
100-
extraData: {
101-
federated: true,
102-
},
103-
config: rc1AdminRequestConfig,
80+
let matrixRoomId: string;
81+
82+
beforeAll(async () => {
83+
user = await createUser(
84+
{
85+
username: `g3-${Date.now()}`,
86+
password: '1',
87+
roles: ['user'],
88+
},
89+
rc1AdminRequestConfig,
90+
);
91+
});
92+
93+
afterAll(async () => {
94+
await deleteUser(user, {}, rc1AdminRequestConfig);
95+
});
96+
97+
let channelName: string;
98+
99+
beforeAll(async () => {
100+
channelName = `federated-room-${Date.now()}`;
101+
matrixRoomId = await hs1AdminApp.createRoom(channelName);
102+
});
103+
104+
it('should throw an error if a user without access-federation permission tries to invite a user to a room', async () => {
105+
await expect(hs1AdminApp.matrixClient.invite(matrixRoomId, `@${user.username}:${federationConfig.rc1.url}`)).rejects.toThrow();
106+
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
107+
const invitedSub = subscriptions.update.find((sub) => sub.fname?.includes(channelName));
108+
expect(invitedSub).toBeUndefined();
109+
});
110+
111+
it('should be able to invite a user to a room if the user has access-federation permission', async () => {
112+
await expect(hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId)).resolves.not.toThrow();
113+
114+
retry('waiting for invitation to be processed', async () => {
115+
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
116+
117+
const pendingInvitation = subscriptions.update.find(
118+
(subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName),
119+
);
120+
expect(pendingInvitation).not.toBeUndefined();
121+
});
122+
});
104123
});
105124

106-
expect(createResponse.status).toBe(200);
107-
expect(createResponse.body).toHaveProperty('success', true);
108-
expect(createResponse.body).toHaveProperty('group');
109-
expect(createResponse.body.group).toHaveProperty('_id');
110-
expect(createResponse.body.group).toHaveProperty('t', 'p');
111-
expect(createResponse.body.group).toHaveProperty('federated', true);
112-
113-
const addUserResponse = await addUserToRoom({
114-
usernames: [federationConfig.hs1.adminMatrixUserId],
115-
rid: createResponse.body.group._id,
116-
config: rc1User1RequestConfig,
125+
it('should throw an error if a user without access-federation permission tries to create a federated room', async () => {
126+
const channelName = `federated-room-${Date.now()}`;
127+
const createResponse = await createRoom({
128+
type: 'p',
129+
name: channelName,
130+
members: [],
131+
extraData: {
132+
federated: true,
133+
},
134+
config: rc1User1RequestConfig,
135+
});
136+
137+
expect(createResponse.status).toBe(400);
138+
expect(createResponse.body).toHaveProperty('success', false);
139+
expect(createResponse.body).toHaveProperty('errorType', 'error-not-authorized-federation');
117140
});
118141

119-
expect(addUserResponse.status).toBe(200);
120-
expect(addUserResponse.body).toHaveProperty('success', true);
121-
expect(addUserResponse.body.message).toMatch(/error-not-allowed/);
142+
it('should not be able to add a user without access-federation permission to a room', async () => {
143+
const createResponse = await createRoom({
144+
type: 'p',
145+
name: `federated-room-${Date.now()}`,
146+
members: [],
147+
extraData: {
148+
federated: true,
149+
},
150+
config: rc1AdminRequestConfig,
151+
});
152+
153+
expect(createResponse.status).toBe(200);
154+
expect(createResponse.body).toHaveProperty('success', true);
155+
expect(createResponse.body).toHaveProperty('group');
156+
expect(createResponse.body.group).toHaveProperty('_id');
157+
expect(createResponse.body.group).toHaveProperty('t', 'p');
158+
expect(createResponse.body.group).toHaveProperty('federated', true);
159+
160+
const addUserResponse = await addUserToRoom({
161+
usernames: [federationConfig.hs1.adminMatrixUserId],
162+
rid: createResponse.body.group._id,
163+
config: rc1User1RequestConfig,
164+
});
165+
166+
expect(addUserResponse.status).toBe(200);
167+
expect(addUserResponse.body).toHaveProperty('success', true);
168+
expect(addUserResponse.body.message).toMatch(/error-not-allowed/);
169+
});
122170
});
123171

124-
describe('Add a user with access-federation permission to a room', () => {
172+
describe('Users with access-federation permission', () => {
125173
let user: TestUser<IUser>;
126174

127175
beforeAll(async () => {
@@ -133,23 +181,17 @@ import { SynapseClient } from '../helper/synapse-client';
133181
},
134182
rc1AdminRequestConfig,
135183
);
136-
137-
await rc1AdminRequestConfig.request
138-
.post(api('permissions.update'))
139-
.set(rc1AdminRequestConfig.credentials)
140-
.send({ permissions: [{ _id: 'access-federation', roles: ['admin', 'user'] }] })
141-
.expect('Content-Type', 'application/json')
142-
.expect(200);
143184
});
144185

145186
afterAll(async () => {
146187
await deleteUser(user, {}, rc1AdminRequestConfig);
147188
});
148189

149-
it('should be able to add a user with access-federation permission to a room', async () => {
190+
it('should be able to create a federated room if the user has access-federation permission', async () => {
191+
const channelName = `federated-room-${Date.now()}`;
150192
const createResponse = await createRoom({
151193
type: 'p',
152-
name: `federated-room-${Date.now()}`,
194+
name: channelName,
153195
members: [],
154196
extraData: {
155197
federated: true,
@@ -158,16 +200,46 @@ import { SynapseClient } from '../helper/synapse-client';
158200
});
159201

160202
expect(createResponse.status).toBe(200);
161-
const addUserResponse = await addUserToRoom({
162-
usernames: [user.username],
163-
rid: createResponse.body.group._id,
164-
config: rc1AdminRequestConfig,
165-
});
203+
expect(createResponse.body).toHaveProperty('success', true);
204+
expect(createResponse.body).toHaveProperty('group');
205+
expect(createResponse.body.group).toHaveProperty('_id');
206+
expect(createResponse.body.group).toHaveProperty('t', 'p');
207+
expect(createResponse.body.group).toHaveProperty('federated', true);
208+
});
166209

167-
expect(addUserResponse.status).toBe(200);
168-
expect(addUserResponse.body).toHaveProperty('success', true);
169-
expect(addUserResponse.body).toHaveProperty('message');
170-
expect(addUserResponse.body.message).toMatch('{"msg":"result","id":"id","result":true}');
210+
describe('Add a user with access-federation permission to a room', () => {
211+
beforeAll(async () =>
212+
rc1AdminRequestConfig.request
213+
.post(api('permissions.update'))
214+
.set(rc1AdminRequestConfig.credentials)
215+
.send({ permissions: [{ _id: 'access-federation', roles: ['admin', 'user'] }] })
216+
.expect('Content-Type', 'application/json')
217+
.expect(200),
218+
);
219+
220+
it('should be able to add a user with access-federation permission to a room', async () => {
221+
const createResponse = await createRoom({
222+
type: 'p',
223+
name: `federated-room-${Date.now()}`,
224+
members: [],
225+
extraData: {
226+
federated: true,
227+
},
228+
config: rc1AdminRequestConfig,
229+
});
230+
231+
expect(createResponse.status).toBe(200);
232+
const addUserResponse = await addUserToRoom({
233+
usernames: [user.username],
234+
rid: createResponse.body.group._id,
235+
config: rc1AdminRequestConfig,
236+
});
237+
238+
expect(addUserResponse.status).toBe(200);
239+
expect(addUserResponse.body).toHaveProperty('success', true);
240+
expect(addUserResponse.body).toHaveProperty('message');
241+
expect(addUserResponse.body.message).toMatch('{"msg":"result","id":"id","result":true}');
242+
});
171243
});
172244
});
173245
});

0 commit comments

Comments
 (0)