Skip to content
8 changes: 8 additions & 0 deletions apps/meteor/app/lib/server/functions/createRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ async function createUsersSubscriptions({
await Rooms.incUsersCountById(room._id, subs.length);
}

// eslint-disable-next-line complexity
Comment thread
ggazzo marked this conversation as resolved.
Outdated
export const createRoom = async <T extends RoomType>(
type: T,
name: T extends 'd' ? undefined : string,
Expand All @@ -141,6 +142,13 @@ export const createRoom = async <T extends RoomType>(
return member.username?.includes(':') && member.username?.includes('@');
});

// Prevent adding federated users to rooms that are not marked as federated explicitly
if (hasFederatedMembers && optionalExtraData.federated !== true) {
throw new Meteor.Error('error-federated-users-in-non-federated-rooms', 'Cannot add federated users to non-federated rooms', {
method: 'createRoom',
});
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const extraData = {
...optionalExtraData,
...((hasFederatedMembers || optionalExtraData.federated) && {
Expand Down
16 changes: 10 additions & 6 deletions apps/meteor/app/lib/server/methods/addUsersToRoom.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { api } from '@rocket.chat/core-services';
import type { IUser } from '@rocket.chat/core-typings';
import { isRoomNativeFederated, isUserNativeFederated } from '@rocket.chat/core-typings';
import { isRoomNativeFederated } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { validateFederatedUsername } from '@rocket.chat/federation-matrix';
import { Subscriptions, Users, Rooms } from '@rocket.chat/models';
import { Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
Expand Down Expand Up @@ -88,15 +89,18 @@ export const addUsersToRoomMethod = async (userId: string, data: { rid: string;

await Promise.all(
data.users.map(async (username) => {
const newUser = await Users.findOneByUsernameIgnoringCase(sanitizeUsername(username));
if (!newUser) {
throw new Meteor.Error('error-user-not-found', 'User not found', {
const sanitizedUsername = sanitizeUsername(username);

// If it's a federated username format and the room is not federated, throw error immediately
if (validateFederatedUsername(sanitizedUsername) && !isRoomNativeFederated(room)) {
throw new Meteor.Error('error-federated-users-in-non-federated-rooms', 'Cannot add federated users to non-federated rooms', {
method: 'addUsersToRoom',
});
}

if (isUserNativeFederated(newUser) && !isRoomNativeFederated(room)) {
throw new Meteor.Error('error-federated-users-in-non-federated-rooms', 'Cannot add federated users to non-federated rooms', {
const newUser = await Users.findOneByUsernameIgnoringCase(sanitizedUsername);
if (!newUser) {
throw new Meteor.Error('error-user-not-found', 'User not found', {
method: 'addUsersToRoom',
});
}
Expand Down
38 changes: 35 additions & 3 deletions apps/meteor/app/slashcommands-invite/server/server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { api } from '@rocket.chat/core-services';
import { api, FederationMatrix } from '@rocket.chat/core-services';
import type { IUser, SlashCommandCallbackParams } from '@rocket.chat/core-typings';
import { Subscriptions, Users } from '@rocket.chat/models';
import { validateFederatedUsername } from '@rocket.chat/federation-matrix';
import { Subscriptions, Users, Rooms } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { i18n } from '../../../server/lib/i18n';
import { FederationActions } from '../../../server/services/room/hooks/BeforeFederationActions';
import { addUsersToRoomMethod, sanitizeUsername } from '../../lib/server/methods/addUsersToRoom';
import { settings } from '../../settings/server';
import { slashCommands } from '../../utils/server/slashCommand';
Expand All @@ -15,13 +17,43 @@ import { slashCommands } from '../../utils/server/slashCommand';
slashCommands.add({
command: 'invite',
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'invite'>): Promise<void> => {
const usernames = params
let usernames = params
.split(/[\s,]/)
.map((username) => sanitizeUsername(username))
.filter((a) => a !== '');
if (usernames.length === 0) {
return;
}

// Get room information for federation check
const room = await Rooms.findOneById(message.rid);
if (!room) {
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
msg: i18n.t('error-invalid-room', { lng: settings.get('Language') || 'en' }),
});
return;
}

// Ensure federated users exist locally before looking them up
const federatedUsernames = usernames.filter((u) => validateFederatedUsername(u)) as string[];
if (federatedUsernames.length > 0) {
if (FederationActions.shouldPerformFederationAction(room)) {
await FederationMatrix.ensureFederatedUsersExistLocally(federatedUsernames);
} else {
await Promise.all(
federatedUsernames.map(async () => {
return api.broadcast('notify.ephemeralMessage', userId, message.rid, {
msg: i18n.t('You_cannot_add_external_users_to_non_federated_room', { lng: settings.get('Language') || 'en' }),
});
}),
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
// These federated users shouldn't be invited and we already broadcasted the error message
usernames = usernames.filter((username) => {
return !federatedUsernames.includes(username);
});
}
}
Comment thread
dhulke marked this conversation as resolved.

const users = await Users.findByUsernames(usernames).toArray();
if (users.length === 0) {
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
Expand Down
Loading