Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
15721f2
Regression: Icons on federated rooms should not appear nor show hover…
Jul 15, 2022
a7ea5af
Regression: Do not allow to remove users from federated DMs (#26254)
Jul 15, 2022
e91eb25
Regression: Do not allow create federated DMs from the UI on CE (#26279)
Jul 15, 2022
68dbf24
Regression: disable readonly on channel fed creation (#26217)
carlosrodrigues94 Jul 15, 2022
43bc4d1
Regression: Do not show mentions on federated rooms (#26212)
Jul 15, 2022
92573d9
Regression: Federation DM Ids were not being generated properly (#26193)
Jul 15, 2022
198e1fb
Regression: Federated username detection failing every other time (#2…
gabriellsh Jul 15, 2022
b9939b6
Merge branch 'develop' into feat/federation
Jul 18, 2022
76ab249
Merge branch 'feat/federation' of github.com:RocketChat/Rocket.Chat i…
Jul 18, 2022
d97bc22
Regression: Do not allow non-owners edit rooms or add/remove users on…
Jul 18, 2022
283f820
fix: do not prevent external users from being added in channels on CE…
Jul 18, 2022
ae2ca38
fix: fix wrong type
Jul 18, 2022
3e6e09f
fix: clear the cache to make the build pass and test it
Jul 18, 2022
9c85db0
fix: fix lint
Jul 18, 2022
c680bf4
Regression: Do not allow non-owners edit rooms or add/remove users on…
Jul 18, 2022
d90308d
[FIX] Do not prevent external users from being added in channels on C…
Jul 18, 2022
9200b3d
fix: bad validation
Jul 18, 2022
158d3e9
Merge branch 'feat/federation' of github.com:RocketChat/Rocket.Chat i…
Jul 18, 2022
9d5f46b
fix: do not allow change readonly when federated
Jul 19, 2022
45a5da5
fix: move listeners for common events to CE
Jul 19, 2022
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 @@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Match } from 'meteor/check';

import { Rooms, Messages } from '../../../models/server';
import { callbacks } from '../../../../lib/callbacks';

export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true) {
if (!Match.test(rid, String)) {
Expand All @@ -14,5 +15,6 @@ export const saveRoomTopic = function (rid, roomTopic, user, sendMessage = true)
if (update && sendMessage) {
Messages.createRoomSettingsChangedWithTypeRoomIdMessageAndUser('room_changed_topic', rid, roomTopic, user);
}
callbacks.run('afterRoomTopicChange', { rid, topic: roomTopic });
return update;
};
16 changes: 13 additions & 3 deletions apps/meteor/app/federation-v2/client/Federation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ValueOf } from '@rocket.chat/core-typings';
import { IRoom, IUser, ValueOf } from '@rocket.chat/core-typings';
import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';

import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';

Expand All @@ -9,6 +10,15 @@ const allowedActionsInFederatedRooms: ValueOf<typeof RoomMemberActions>[] = [
RoomMemberActions.LEAVE,
];

export const actionAllowed = (action: ValueOf<typeof RoomMemberActions>): boolean => {
return allowedActionsInFederatedRooms.includes(action);
export const actionAllowed = (room: Partial<IRoom>, action: ValueOf<typeof RoomMemberActions>): boolean => {
return room.t === RoomType.DIRECT_MESSAGE && action === RoomMemberActions.REMOVE_USER
? false
: allowedActionsInFederatedRooms.includes(action);
};

export const isEditableByTheUser = (user: IUser | undefined, room: IRoom | undefined): boolean => {
if (!user || !room) {
return false;
}
return user._id === room.u?._id;
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
FederationRoomCreateInputDto,
FederationRoomChangeMembershipDto,
FederationRoomSendInternalMessageDto,
FederationRoomChangeJoinRulesDto,
FederationRoomChangeNameDto,
FederationRoomChangeTopicDto,
} from './input/RoomReceiverDto';

export class FederationRoomServiceReceiver {
Expand Down Expand Up @@ -189,4 +192,72 @@ export class FederationRoomServiceReceiver {

await this.rocketMessageAdapter.sendMessage(senderUser, text, federatedRoom);
}

public async changeJoinRules(roomJoinRulesChangeInput: FederationRoomChangeJoinRulesDto): Promise<void> {
const { externalRoomId, roomType } = roomJoinRulesChangeInput;

const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
if (!federatedRoom) {
return;
}

if (federatedRoom.isDirectMessage()) {
return;
}

federatedRoom.setRoomType(roomType);
await this.rocketRoomAdapter.updateRoomType(federatedRoom);
}

public async changeRoomName(roomChangeNameInput: FederationRoomChangeNameDto): Promise<void> {
const { externalRoomId, normalizedRoomName, externalSenderId } = roomChangeNameInput;

const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
if (!federatedRoom) {
return;
}

if (federatedRoom.isDirectMessage()) {
return;
}

if (federatedRoom.internalReference?.name === normalizedRoomName) {
return;
}

const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId);
if (!federatedUser) {
return;
}

federatedRoom.changeRoomName(normalizedRoomName);

await this.rocketRoomAdapter.updateRoomName(federatedRoom, federatedUser);
}

public async changeRoomTopic(roomChangeTopicInput: FederationRoomChangeTopicDto): Promise<void> {
const { externalRoomId, roomTopic, externalSenderId } = roomChangeTopicInput;

const federatedRoom = await this.rocketRoomAdapter.getFederatedRoomByExternalId(externalRoomId);
if (!federatedRoom) {
return;
}

if (federatedRoom.internalReference?.topic === roomTopic) {
return;
}

if (federatedRoom.isDirectMessage()) {
return;
}

const federatedUser = await this.rocketUserAdapter.getFederatedUserByExternalId(externalSenderId);
if (!federatedUser) {
return;
}

federatedRoom.changeRoomTopic(roomTopic);

await this.rocketRoomAdapter.updateRoomTopic(federatedRoom, federatedUser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,50 @@ export class FederationRoomServiceSender {
}
}

public async canAddUsersToTheRoom(internalUser: IUser | string, internalRoom: IRoom): Promise<void> {
const newUserBeingAdded = typeof internalUser === 'string';
if (newUserBeingAdded) {
public async canAddUsersToTheRoom(internalUser: IUser | string, internalInviter: IUser, internalRoom: IRoom): Promise<void> {
if (!internalRoom.federated) {
return;
}
const tryingToAddNewFederatedUser = typeof internalUser === 'string';
if (tryingToAddNewFederatedUser) {
throw new Error('error-this-is-an-ee-feature');
}

if (!internalRoom.federated) {
const invitee = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id);
const inviter = await this.rocketUserAdapter.getFederatedUserByInternalId((internalInviter as IUser)._id);
const externalRoom = await this.rocketRoomAdapter.getFederatedRoomByInternalId(internalRoom._id);
if (!externalRoom || !inviter) {
return;
}

const user = await this.rocketUserAdapter.getFederatedUserByInternalId((internalUser as IUser)._id);
if (user && !user.existsOnlyOnProxyServer && internalRoom.t !== RoomType.DIRECT_MESSAGE) {
const isARoomFromTheProxyServer = this.bridge.isRoomFromTheSameHomeserver(
externalRoom.externalId,
this.rocketSettingsAdapter.getHomeServerDomain(),
);
const isInviterFromTheProxyServer = this.bridge.isUserIdFromTheSameHomeserver(
inviter.externalId,
this.rocketSettingsAdapter.getHomeServerDomain(),
);

if (!isARoomFromTheProxyServer && !isInviterFromTheProxyServer) {
return;
}
if (invitee && !invitee.existsOnlyOnProxyServer && internalRoom.t !== RoomType.DIRECT_MESSAGE) {
throw new Error('error-this-is-an-ee-feature');
}
}

public async beforeCreateDirectMessageFromUI(internalUsers: (IUser | string)[]): Promise<void> {
const usernames = internalUsers.map((user) => {
if (typeof user === 'string') {
return user;
}
return user.username;
});
const isThereAnyFederatedUser =
usernames.some((username) => username?.includes(':')) ||
internalUsers.filter((user) => typeof user !== 'string').some((user) => (user as IUser).federated);
if (isThereAnyFederatedUser) {
throw new Error('error-this-is-an-ee-feature');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,19 @@ export class FederationRoomSendInternalMessageDto extends BaseRoom {

text: string;
}

export class FederationRoomChangeJoinRulesDto extends BaseRoom {
roomType: RoomType;
}

export class FederationRoomChangeNameDto extends BaseRoom {
normalizedRoomName: string;

externalSenderId: string;
}

export class FederationRoomChangeTopicDto extends BaseRoom {
roomTopic: string;

externalSenderId: string;
}
30 changes: 29 additions & 1 deletion apps/meteor/app/federation-v2/server/domain/FederatedRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,34 @@ export class FederatedRoom {
}

public static buildRoomIdForDirectMessages(inviter: FederatedUser, invitee: FederatedUser): string {
return inviter.internalReference?._id + invitee.internalReference?._id;
if (!inviter.internalReference || !invitee.internalReference) {
throw new Error('Cannot create room Id without the user ids');
}
return [inviter.internalReference, invitee.internalReference]
.map(({ _id }) => _id)
.sort()
.join('');
}

public setRoomType(type: RoomType): void {
if (this.isDirectMessage()) {
throw new Error('Its not possible to change a direct message type');
}
this.internalReference.t = type;
}

public changeRoomName(name: string): void {
if (this.isDirectMessage()) {
throw new Error('Its not possible to change a direct message name');
}
this.internalReference.name = name;
this.internalReference.fname = name;
}

public changeRoomTopic(topic: string): void {
if (this.isDirectMessage()) {
throw new Error('Its not possible to change a direct message topic');
}
this.internalReference.topic = topic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface IFederationBridge {
sendMessage(externalRoomId: string, externaSenderId: string, text: string): Promise<void>;
createUser(username: string, name: string, domain: string): Promise<string>;
isUserIdFromTheSameHomeserver(externalUserId: string, domain: string): boolean;
isRoomFromTheSameHomeserver(externalRoomId: string, domain: string): boolean;
leaveRoom(externalRoomId: string, externalUserId: string): Promise<void>;
kickUserFromRoom(externalRoomId: string, externalUserId: string, externalOwnerId: string): Promise<void>;
}
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/app/federation-v2/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export const FEDERATION_PROCESSING_CONCURRENCY = 1;

const rocketSettingsAdapter = FederationFactory.buildRocketSettingsAdapter();
rocketSettingsAdapter.initialize();
const queueInstance = FederationFactory.buildQueue();
const federation = FederationFactory.buildBridge(rocketSettingsAdapter, queueInstance);
export const federationQueueInstance = FederationFactory.buildQueue();
const federation = FederationFactory.buildBridge(rocketSettingsAdapter, federationQueueInstance);
const rocketRoomAdapter = FederationFactory.buildRocketRoomAdapter();
const rocketUserAdapter = FederationFactory.buildRocketUserAdapter();
const rocketMessageAdapter = FederationFactory.buildRocketMessageAdapter();
Expand All @@ -31,13 +31,13 @@ FederationFactory.setupListeners(federationRoomServiceSender);
let cancelSettingsObserver: Function;

export const runFederation = async (): Promise<void> => {
federationQueueInstance.setHandler(federationEventsHandler.handleEvent.bind(federationEventsHandler), FEDERATION_PROCESSING_CONCURRENCY);
cancelSettingsObserver = rocketSettingsAdapter.onFederationEnabledStatusChanged(
federation.onFederationAvailabilityChanged.bind(federation),
);
if (!rocketSettingsAdapter.isFederationEnabled()) {
return;
}
queueInstance.setHandler(federationEventsHandler.handleEvent.bind(federationEventsHandler), FEDERATION_PROCESSING_CONCURRENCY);
await federation.start();
require('./infrastructure/rocket-chat/slash-commands');
};
Expand Down
25 changes: 19 additions & 6 deletions apps/meteor/app/federation-v2/server/infrastructure/Factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { FederationRoomServiceReceiver } from '../application/RoomServiceReceive
import { FederationRoomServiceSender } from '../application/RoomServiceSender';
import { MatrixBridge } from './matrix/Bridge';
import { MatrixEventsHandler } from './matrix/handlers';
import { MatrixRoomCreatedHandler, MatrixRoomMembershipChangedHandler, MatrixRoomMessageSentHandler } from './matrix/handlers/Room';
import {
MatrixRoomCreatedHandler,
MatrixRoomJoinRulesChangedHandler,
MatrixRoomMembershipChangedHandler,
MatrixRoomMessageSentHandler,
MatrixRoomNameChangedHandler,
MatrixRoomTopicChangedHandler,
} from './matrix/handlers/Room';
import { InMemoryQueue } from './queue/InMemoryQueue';
import { RocketChatMessageAdapter } from './rocket-chat/adapters/Message';
import { RocketChatRoomAdapter } from './rocket-chat/adapters/Room';
Expand Down Expand Up @@ -74,13 +81,16 @@ export class FederationFactory {
}

public static getEventHandlers(
roomServiceReceive: FederationRoomServiceReceiver,
roomServiceReceiver: FederationRoomServiceReceiver,
rocketSettingsAdapter: RocketChatSettingsAdapter,
): any[] {
return [
new MatrixRoomCreatedHandler(roomServiceReceive),
new MatrixRoomMembershipChangedHandler(roomServiceReceive, rocketSettingsAdapter),
new MatrixRoomMessageSentHandler(roomServiceReceive),
new MatrixRoomCreatedHandler(roomServiceReceiver),
new MatrixRoomMembershipChangedHandler(roomServiceReceiver, rocketSettingsAdapter),
new MatrixRoomMessageSentHandler(roomServiceReceiver),
new MatrixRoomJoinRulesChangedHandler(roomServiceReceiver),
new MatrixRoomNameChangedHandler(roomServiceReceiver),
new MatrixRoomTopicChangedHandler(roomServiceReceiver),
];
}

Expand All @@ -92,7 +102,10 @@ export class FederationFactory {
roomServiceSender.leaveRoom(FederationRoomSenderConverter.toAfterLeaveRoom(user._id, room._id, userWhoRemoved._id)),
);
FederationHooks.canAddTheUserToTheRoom((user: IUser | string, room: IRoom) => roomServiceSender.canAddThisUserToTheRoom(user, room));
FederationHooks.canAddUsersToTheRoom((user: IUser | string, room: IRoom) => roomServiceSender.canAddUsersToTheRoom(user, room));
FederationHooks.canAddUsersToTheRoom((user: IUser | string, inviter: IUser, room: IRoom) =>
roomServiceSender.canAddUsersToTheRoom(user, inviter, room),
);
FederationHooks.beforeCreateDirectMessage((members: (IUser | string)[]) => roomServiceSender.beforeCreateDirectMessageFromUI(members));
}

public static removeListeners(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ export class MatrixBridge implements IFederationBridge {
this.bridgeInstance.getIntent(externalOwnerId).kick(externalRoomId, externalUserId);
}

public isRoomFromTheSameHomeserver(externalRoomId: string, domain: string): boolean {
return this.isUserIdFromTheSameHomeserver(externalRoomId, domain);
}

protected async createInstance(): Promise<void> {
bridgeLogger.info('Performing Dynamic Import of matrix-appservice-bridge');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { RoomType } from '@rocket.chat/apps-engine/definition/rooms';

import {
FederationRoomChangeJoinRulesDto,
FederationRoomChangeMembershipDto,
FederationRoomChangeNameDto,
FederationRoomChangeTopicDto,
FederationRoomCreateInputDto,
FederationRoomSendInternalMessageDto,
} from '../../../application/input/RoomReceiverDto';
Expand Down Expand Up @@ -54,6 +57,35 @@ export class MatrixRoomReceiverConverter {
});
}

public static toRoomChangeJoinRulesDto(
externalEvent: IMatrixEvent<MatrixEventType.ROOM_JOIN_RULES_CHANGED>,
): FederationRoomChangeJoinRulesDto {
return Object.assign(new FederationRoomChangeJoinRulesDto(), {
...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id),
roomType: MatrixRoomReceiverConverter.convertMatrixJoinRuleToRCRoomType(externalEvent.content?.join_rule),
});
}

public static toRoomChangeNameDto(externalEvent: IMatrixEvent<MatrixEventType.ROOM_NAME_CHANGED>): FederationRoomChangeNameDto {
return Object.assign(new FederationRoomChangeNameDto(), {
...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id),
externalSenderId: externalEvent.sender,
normalizedRoomName: MatrixRoomReceiverConverter.normalizeRoomNameToRCFormat(externalEvent.content?.name),
});
}

public static toRoomChangeTopicDto(externalEvent: IMatrixEvent<MatrixEventType.ROOM_TOPIC_CHANGED>): FederationRoomChangeTopicDto {
return Object.assign(new FederationRoomChangeTopicDto(), {
...MatrixRoomReceiverConverter.getBasicRoomsFields(externalEvent.room_id),
externalSenderId: externalEvent.sender,
roomTopic: externalEvent.content?.topic,
});
}

private static normalizeRoomNameToRCFormat(matrixRoomName = ''): string {
return matrixRoomName.replace('@', '');
}

protected static convertMatrixUserIdFormatToRCFormat(matrixUserId = ''): string {
return matrixUserId.replace('@', '');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { IMatrixEventContentAddMemberToRoom } from './IMatrixEventContentAddMemb
import { IMatrixEventContentSendMessage } from './IMatrixEventContentSendMessage';
import { IMatrixEventContentSetRoomJoinRules } from './IMatrixEventContentSetRoomJoinRules';
import { IMatrixEventContentSetRoomName } from './IMatrixEventContentSetRoomName';
import { IMatrixEventContentSetRoomTopic } from './IMatrixEventContentSetRoomTopic';

export type EventContent = {
[MatrixEventType.ROOM_CREATED]: IMatrixEventContentCreateRoom;
[MatrixEventType.ROOM_MEMBERSHIP_CHANGED]: IMatrixEventContentAddMemberToRoom;
[MatrixEventType.ROOM_MESSAGE_SENT]: IMatrixEventContentSendMessage;
[MatrixEventType.ROOM_JOIN_RULES_CHANGED]: IMatrixEventContentSetRoomJoinRules;
[MatrixEventType.ROOM_NAME_CHANGED]: IMatrixEventContentSetRoomName;
[MatrixEventType.ROOM_TOPIC_CHANGED]: IMatrixEventContentSetRoomTopic;
};
Loading