Skip to content

Commit a356cfc

Browse files
committed
feat(federation): accept remote join at 3rd party
1 parent 1a32eb4 commit a356cfc

File tree

3 files changed

+83
-84
lines changed

3 files changed

+83
-84
lines changed

ee/packages/federation-matrix/src/events/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { Emitter } from '@rocket.chat/emitter';
22
import type { HomeserverEventSignatures } from '@rocket.chat/federation-sdk';
33

44
import { edus } from './edu';
5-
import { invite } from './invite';
65
import { member } from './member';
76
import { message } from './message';
87
import { ping } from './ping';
@@ -16,7 +15,6 @@ export function registerEvents(
1615
) {
1716
ping(emitter);
1817
message(emitter, serverName);
19-
invite(emitter);
2018
reaction(emitter);
2119
member(emitter);
2220
edus(emitter, eduProcessTypes);

ee/packages/federation-matrix/src/events/invite.ts

Lines changed: 0 additions & 52 deletions
This file was deleted.

ee/packages/federation-matrix/src/events/member.ts

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,102 @@
11
import { Room } from '@rocket.chat/core-services';
2+
import { UserStatus } from '@rocket.chat/core-typings';
23
import type { Emitter } from '@rocket.chat/emitter';
34
import type { HomeserverEventSignatures } from '@rocket.chat/federation-sdk';
45
import { Logger } from '@rocket.chat/logger';
56
import { Rooms, Users } from '@rocket.chat/models';
67

78
const logger = new Logger('federation-matrix:member');
89

10+
async function membershipLeaveAction(data: HomeserverEventSignatures['homeserver.matrix.membership']) {
11+
const room = await Rooms.findOne({ 'federation.mrid': data.room_id }, { projection: { _id: 1 } });
12+
if (!room) {
13+
logger.warn(`No bridged room found for Matrix room_id: ${data.room_id}`);
14+
return;
15+
}
16+
17+
// state_key is the user affected by the membership change
18+
const affectedUser = await Users.findOne({ 'federation.mui': data.state_key });
19+
if (!affectedUser) {
20+
logger.error(`No Rocket.Chat user found for bridged user: ${data.state_key}`);
21+
return;
22+
}
23+
24+
// Check if this is a kick (sender != state_key) or voluntary leave (sender == state_key)
25+
if (data.sender === data.state_key) {
26+
// Voluntary leave
27+
await Room.removeUserFromRoom(room._id, affectedUser);
28+
logger.info(`User ${affectedUser.username} left room ${room._id} via Matrix federation`);
29+
} else {
30+
// Kick - find who kicked
31+
const kickerUser = await Users.findOne({ 'federation.mui': data.sender });
32+
33+
await Room.removeUserFromRoom(room._id, affectedUser, {
34+
byUser: kickerUser || { _id: 'matrix.federation', username: 'Matrix User' },
35+
});
36+
37+
const reasonText = data.content.reason ? ` Reason: ${data.content.reason}` : '';
38+
logger.info(`User ${affectedUser.username} was kicked from room ${room._id} by ${data.sender} via Matrix federation.${reasonText}`);
39+
}
40+
}
41+
42+
async function membershipJoinAction(data: HomeserverEventSignatures['homeserver.matrix.membership']) {
43+
const room = await Rooms.findOne({ 'federation.mrid': data.room_id });
44+
if (!room) {
45+
console.warn(`No bridged room found for room_id: ${data.room_id}`);
46+
return;
47+
}
48+
49+
const internalUsername = data.sender;
50+
const localUser = await Users.findOneByUsername(internalUsername);
51+
if (localUser) {
52+
await Room.addUserToRoom(room._id, localUser);
53+
return;
54+
}
55+
56+
const [, serverName] = data.sender.split(':');
57+
if (!serverName) {
58+
throw new Error('Invalid sender format, missing server name');
59+
}
60+
61+
const { insertedId } = await Users.insertOne({
62+
username: internalUsername,
63+
type: 'user',
64+
status: UserStatus.OFFLINE,
65+
active: true,
66+
roles: ['user'],
67+
name: data.content.displayname || internalUsername,
68+
requirePasswordChange: false,
69+
createdAt: new Date(),
70+
_updatedAt: new Date(),
71+
federated: true,
72+
federation: {
73+
version: 1,
74+
mui: data.sender,
75+
origin: serverName,
76+
},
77+
});
78+
79+
const user = await Users.findOneById(insertedId);
80+
if (!user) {
81+
console.warn(`User with ID ${insertedId} not found after insertion`);
82+
return;
83+
}
84+
await Room.addUserToRoom(room._id, user);
85+
}
86+
987
export function member(emitter: Emitter<HomeserverEventSignatures>) {
1088
emitter.on('homeserver.matrix.membership', async (data) => {
1189
try {
12-
// Only handle leave events (including kicks)
13-
if (data.content.membership !== 'leave') {
14-
logger.debug(`Ignoring membership event with membership: ${data.content.membership}`);
15-
return;
90+
if (data.content.membership === 'leave') {
91+
return membershipLeaveAction(data);
1692
}
1793

18-
const room = await Rooms.findOne({ 'federation.mrid': data.room_id }, { projection: { _id: 1 } });
19-
if (!room) {
20-
logger.warn(`No bridged room found for Matrix room_id: ${data.room_id}`);
21-
return;
94+
if (data.content.membership === 'join') {
95+
return membershipJoinAction(data);
2296
}
2397

24-
// state_key is the user affected by the membership change
25-
const affectedUser = await Users.findOne({ 'federation.mui': data.state_key });
26-
if (!affectedUser) {
27-
logger.error(`No Rocket.Chat user found for bridged user: ${data.state_key}`);
28-
return;
29-
}
98+
logger.debug(`Ignoring membership event with membership: ${data.content.membership}`);
3099

31-
// Check if this is a kick (sender != state_key) or voluntary leave (sender == state_key)
32-
if (data.sender === data.state_key) {
33-
// Voluntary leave
34-
await Room.removeUserFromRoom(room._id, affectedUser);
35-
logger.info(`User ${affectedUser.username} left room ${room._id} via Matrix federation`);
36-
} else {
37-
// Kick - find who kicked
38-
const kickerUser = await Users.findOne({ 'federation.mui': data.sender });
39-
40-
await Room.removeUserFromRoom(room._id, affectedUser, {
41-
byUser: kickerUser || { _id: 'matrix.federation', username: 'Matrix User' },
42-
});
43-
44-
const reasonText = data.content.reason ? ` Reason: ${data.content.reason}` : '';
45-
logger.info(`User ${affectedUser.username} was kicked from room ${room._id} by ${data.sender} via Matrix federation.${reasonText}`);
46-
}
47100
} catch (error) {
48101
logger.error('Failed to process Matrix membership event:', error);
49102
}

0 commit comments

Comments
 (0)