Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export const permissions = [
{ _id: 'mobile-upload-file', roles: ['user', 'admin'] },
{ _id: 'send-mail', roles: ['admin'] },
{ _id: 'view-federation-data', roles: ['admin'] },
{ _id: 'access-federation', roles: ['admin', 'user'] },
{ _id: 'add-all-to-room', roles: ['admin'] },
{ _id: 'get-server-info', roles: ['admin'] },
{ _id: 'register-on-cloud', roles: ['admin'] },
Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/app/lib/server/functions/createRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppsEngineException } from '@rocket.chat/apps-engine/definition/excepti
import { Message, Team } from '@rocket.chat/core-services';
import type { ICreateRoomParams, ISubscriptionExtraData } from '@rocket.chat/core-services';
import type { ICreatedRoom, IUser, IRoom, RoomType } from '@rocket.chat/core-typings';
import { isRoomNativeFederated } from '@rocket.chat/core-typings';
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

Expand All @@ -13,6 +14,7 @@ import { beforeCreateRoomCallback, prepareCreateRoomCallback } from '../../../..
import { calculateRoomRolePriorityFromRoles } from '../../../../lib/roles/calculateRoomRolePriorityFromRoles';
import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig';
import { syncRoomRolePriorityForUserAndRoom } from '../../../../server/lib/roles/syncRoomRolePriority';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { getDefaultSubscriptionPref } from '../../../utils/lib/getDefaultSubscriptionPref';
import { getValidRoomName } from '../../../utils/server/lib/getValidRoomName';
import { notifyOnRoomChanged, notifyOnSubscriptionChangedById } from '../lib/notifyListener';
Expand Down Expand Up @@ -162,6 +164,13 @@ export const createRoom = async <T extends RoomType>(
// options,
});

const shouldBeHandledByFederation = isRoomNativeFederated(extraData);
if (shouldBeHandledByFederation && owner && !(await hasPermissionAsync(owner._id, 'access-federation'))) {
throw new Meteor.Error('error-not-authorized-federation', 'Not authorized to access federation', {
method: 'createRoom',
});
}

if (type === 'd') {
return createDirectRoom(members as IUser[], extraData, { ...options, creator: options?.creator || owner?.username });
}
Expand Down Expand Up @@ -255,8 +264,6 @@ export const createRoom = async <T extends RoomType>(
Object.assign(roomProps, eventResult);
}

const shouldBeHandledByFederation = roomProps.federated === true || owner.username.includes(':');

await beforeCreateRoomCallback.run({
owner,
room: roomProps,
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/ee/server/hooks/federation/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FederationMatrix } from '@rocket.chat/core-services';
import { FederationMatrix, Authorization, MeteorError } from '@rocket.chat/core-services';
import { isEditedMessage, type IMessage, type IRoom, type IUser } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';

Expand Down Expand Up @@ -83,6 +83,9 @@ beforeAddUserToRoom.add(
}

if (FederationActions.shouldPerformFederationAction(room)) {
if (!(await Authorization.hasPermission(user._id, 'access-federation'))) {
throw new MeteorError('error-not-authorized-federation', 'Not authorized to access federation');
}
Comment thread
ricardogarim marked this conversation as resolved.
await FederationMatrix.inviteUsersToRoom(room, [user.username], inviter);
}
},
Expand Down
4 changes: 4 additions & 0 deletions apps/meteor/server/services/room/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ export class RoomService extends ServiceClassInternal implements IRoomService {
throw new MeteorError('error-not-allowed', 'Not allowed', { method: 'joinRoom' });
}

if (FederationActions.shouldPerformFederationAction(room) && !(await Authorization.hasPermission(user._id, 'access-federation'))) {
throw new MeteorError('error-not-authorized-federation', 'Not authorized to access federation', { method: 'joinRoom' });
}

if (isRoomWithJoinCode(room) && !(await Authorization.hasPermission(user._id, 'join-without-join-code'))) {
if (!joinCode) {
throw new MeteorError('error-code-required', 'Code required', { method: 'joinRoom' });
Expand Down
25 changes: 23 additions & 2 deletions ee/packages/federation-matrix/src/api/_matrix/invite.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Room } from '@rocket.chat/core-services';
import { Authorization, Room } from '@rocket.chat/core-services';
import { isUserNativeFederated, type IUser } from '@rocket.chat/core-typings';
import type { PduMembershipEventContent, PersistentEventBase, RoomVersion } from '@rocket.chat/federation-sdk';
import { eventIdSchema, roomIdSchema, NotAllowedError, federationSDK } from '@rocket.chat/federation-sdk';
Expand All @@ -10,6 +10,8 @@ import { ajv } from '@rocket.chat/rest-typings/dist/v1/Ajv';
import { createOrUpdateFederatedUser, getUsernameServername } from '../../FederationMatrix';
import { isAuthenticatedMiddleware } from '../middlewares/isAuthenticated';

const logger = new Logger('federation-matrix:invite');

const EventBaseSchema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -139,8 +141,14 @@ async function runWithBackoff(fn: () => Promise<void>, delaySec = 5) {
try {
await fn();
} catch (e) {
// don't retry on authorization/validation errors - they won't succeed on retry
if (e instanceof NotAllowedError) {
logger.error('Authorization error, not retrying:', e);
return;
}

const delay = Math.min(625, delaySec ** 2);
console.error(`error occurred, retrying in ${delay}s`, e);
logger.error(`error occurred, retrying in ${delay}s`, e);
setTimeout(() => {
runWithBackoff(fn, delay);
}, delay * 1000);
Expand Down Expand Up @@ -346,6 +354,19 @@ export const getMatrixInviteRoutes = () => {
throw new Error('user not found not processing invite');
}

// check federation permission before processing the invite
if (!(await Authorization.hasPermission(ourUser._id, 'access-federation'))) {
logger.info(`User ${userToCheck} denied federation access, rejecting invite to room ${roomId}`);

return {
body: {
errcode: 'M_FORBIDDEN',
error: 'User does not have permission to access federation',
},
statusCode: 403,
};
}

try {
const inviteEvent = await federationSDK.processInvite(
event,
Expand Down
3 changes: 3 additions & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -5957,6 +5957,8 @@
"access-permissions_description": "Modify permissions for various roles.",
"access-setting-permissions": "Modify Setting-Based Permissions",
"access-setting-permissions_description": "Permission to modify setting-based permissions",
"access-federation": "Access Federation",
"access-federation_description": "Permission to access federation features, create and join federated rooms",
"active": "active",
"add-all-to-room": "Add all users to a room",
"add-all-to-room_description": "Permission to add all users to a room",
Expand Down Expand Up @@ -6255,6 +6257,7 @@
"error-no-tokens-for-this-user": "There are no tokens for this user",
"error-not-allowed": "Not allowed",
"error-not-authorized": "Not authorized",
"error-not-authorized-federation": "Not authorized to access federation",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"error-office-hours-are-closed": "The office hours are closed.",
"error-password-in-history": "Entered password has been previously used",
"error-password-policy-not-met": "Password does not meet the server's policy",
Expand Down
Loading