mirror of
https://github.com/RocketChat/Rocket.Chat.git
synced 2025-12-28 06:47:25 +00:00
feat(federation): add access-federation permission (#37377)
This commit is contained in:
parent
cd0f72faa5
commit
22c6e0b907
@ -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'] },
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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';
|
||||
@ -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 });
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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');
|
||||
}
|
||||
await FederationMatrix.inviteUsersToRoom(room, [user.username], inviter);
|
||||
}
|
||||
},
|
||||
|
||||
@ -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' });
|
||||
|
||||
@ -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';
|
||||
@ -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: {
|
||||
@ -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);
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
@ -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",
|
||||
"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",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user