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
2 changes: 1 addition & 1 deletion apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"@rocket.chat/emitter": "^0.32.0",
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/federation-matrix": "workspace:^",
"@rocket.chat/federation-sdk": "0.6.2",
"@rocket.chat/federation-sdk": "0.6.3",
"@rocket.chat/fuselage": "^0.73.0",
"@rocket.chat/fuselage-forms": "^1.0.0",
"@rocket.chat/fuselage-hooks": "^0.40.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ rc_registration_token_validity:
per_second: 10000
burst_count: 10000

# Room creation
rc_room_creation:
per_second: 10000
burst_count: 10000

# Login (IP, account, and failed-attempt buckets)
rc_login:
address:
Expand Down
2 changes: 1 addition & 1 deletion ee/packages/federation-matrix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@rocket.chat/core-services": "workspace:^",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/emitter": "^0.32.0",
"@rocket.chat/federation-sdk": "0.6.2",
"@rocket.chat/federation-sdk": "0.6.3",
"@rocket.chat/http-router": "workspace:^",
"@rocket.chat/license": "workspace:^",
"@rocket.chat/models": "workspace:^",
Expand Down
140 changes: 140 additions & 0 deletions ee/packages/federation-matrix/tests/end-to-end/room.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,146 @@ import { SynapseClient } from '../helper/synapse-client';
});
});

describe('Re-inviting a RC user from Synapse after leaving or being kicked', () => {
describe('Kick a RC user from Synapse, send a message, then re-invite the same user', () => {
let matrixRoomId: string;
let channelName: string;

beforeAll(async () => {
channelName = `federated-channel-reinvite-kicked-${Date.now()}`;

// Step 1: Create a room on Synapse
matrixRoomId = await hs1AdminApp.createRoom(channelName);

// Step 2: Invite RC user from Synapse
await hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId);

// Step 3: RC user accepts the invite
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
const pendingInvitation = subscriptions.update.find(
(subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName),
);
expect(pendingInvitation).not.toBeUndefined();

const rid = pendingInvitation!.rid!;
await acceptRoomInvite(rid, rc1AdminRequestConfig);

// Wait for join to propagate to Synapse
await retry(
'waiting for RC user join to propagate to Synapse',
async () => {
const member = await hs1AdminApp.findRoomMember(channelName, federationConfig.rc1.adminMatrixUserId);
expect(member?.membership).toBe('join');
},
{ delayMs: 500 },
);

// Step 4: Kick RC user from Synapse
await hs1AdminApp.matrixClient.kick(matrixRoomId, federationConfig.rc1.adminMatrixUserId, 'Kicked for re-invite test');

// Wait for kick to propagate to Synapse
await retry(
'waiting for RC user kick to propagate to Synapse',
async () => {
const member = await hs1AdminApp.findRoomMember(channelName, federationConfig.rc1.adminMatrixUserId);
expect(member?.membership).toBe('leave');
},
{ delayMs: 500 },
);

// Step 5: Send a message from Synapse (while RC user is kicked)
await hs1AdminApp.matrixClient.sendTextMessage(matrixRoomId, 'Message sent after kicking RC user');
}, 30000);

it('should allow re-inviting the same RC user after being kicked', async () => {
// Step 6: Re-invite the same RC user from Synapse
await hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId);

// Step 7: Validate the invite was created successfully on the RC side
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
const pendingInvitation = subscriptions.update.find(
(subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName),
);

expect(pendingInvitation).not.toBeUndefined();
expect(pendingInvitation).toHaveProperty('rid');
expect(pendingInvitation).toHaveProperty('fname');
expect(pendingInvitation!.fname).toContain(channelName);
});
});

describe('RC user leaves the room, Synapse sends a message, then re-invites the same user', () => {
let matrixRoomId: string;
let channelName: string;

beforeAll(async () => {
channelName = `federated-channel-reinvite-left-${Date.now()}`;

// Step 1: Create a room on Synapse
matrixRoomId = await hs1AdminApp.createRoom(channelName);

// Step 2: Invite RC user from Synapse
await hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId);

// Step 3: RC user accepts the invite
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
const pendingInvitation = subscriptions.update.find(
(subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName),
);
expect(pendingInvitation).not.toBeUndefined();

const rid = pendingInvitation!.rid!;
await acceptRoomInvite(rid, rc1AdminRequestConfig);

// Wait for join to propagate to Synapse
await retry(
'waiting for RC user join to propagate to Synapse',
async () => {
const member = await hs1AdminApp.findRoomMember(channelName, federationConfig.rc1.adminMatrixUserId);
expect(member?.membership).toBe('join');
},
{ delayMs: 500 },
);

// Step 4: RC user leaves the room
await rc1AdminRequestConfig.request
.post(api('rooms.leave'))
.set(rc1AdminRequestConfig.credentials)
.send({ roomId: rid })
.expect(200);

// Wait for leave to propagate to Synapse
await retry(
'waiting for RC user leave to propagate to Synapse',
async () => {
const member = await hs1AdminApp.findRoomMember(channelName, federationConfig.rc1.adminMatrixUserId);
expect(member?.membership).toBe('leave');
},
{ delayMs: 500 },
);

// Step 5: Send a message from Synapse (while RC user has left)
await hs1AdminApp.matrixClient.sendTextMessage(matrixRoomId, 'Message sent after RC user left');
}, 30000);

it('should allow re-inviting the same RC user after they left', async () => {
// Step 6: Re-invite the same RC user from Synapse
await hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId);

// Step 7: Validate the invite was created successfully on the RC side
const subscriptions = await getSubscriptions(rc1AdminRequestConfig);
const pendingInvitation = subscriptions.update.find(
(subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName),
);

expect(pendingInvitation).not.toBeUndefined();
expect(pendingInvitation).toHaveProperty('rid');
expect(pendingInvitation).toHaveProperty('fname');
expect(pendingInvitation!.fname).toContain(channelName);
});
});
});

describe.skip('Synchronizing user names across federated servers', () => {
const ts = Date.now();

Expand Down
2 changes: 1 addition & 1 deletion packages/core-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/federation-sdk": "0.6.2",
"@rocket.chat/federation-sdk": "0.6.3",
"@rocket.chat/http-router": "workspace:^",
"@rocket.chat/icons": "~0.47.0",
"@rocket.chat/media-signaling": "workspace:^",
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9140,7 +9140,7 @@ __metadata:
dependencies:
"@rocket.chat/apps-engine": "workspace:^"
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/federation-sdk": "npm:0.6.2"
"@rocket.chat/federation-sdk": "npm:0.6.3"
"@rocket.chat/http-router": "workspace:^"
"@rocket.chat/icons": "npm:~0.47.0"
"@rocket.chat/jest-presets": "workspace:~"
Expand Down Expand Up @@ -9350,7 +9350,7 @@ __metadata:
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/ddp-client": "workspace:^"
"@rocket.chat/emitter": "npm:^0.32.0"
"@rocket.chat/federation-sdk": "npm:0.6.2"
"@rocket.chat/federation-sdk": "npm:0.6.3"
"@rocket.chat/http-router": "workspace:^"
"@rocket.chat/license": "workspace:^"
"@rocket.chat/models": "workspace:^"
Expand Down Expand Up @@ -9379,9 +9379,9 @@ __metadata:
languageName: unknown
linkType: soft

"@rocket.chat/federation-sdk@npm:0.6.2":
version: 0.6.2
resolution: "@rocket.chat/federation-sdk@npm:0.6.2"
"@rocket.chat/federation-sdk@npm:0.6.3":
version: 0.6.3
resolution: "@rocket.chat/federation-sdk@npm:0.6.3"
dependencies:
"@datastructures-js/priority-queue": "npm:^6.3.5"
"@noble/ed25519": "npm:^3.0.0"
Expand All @@ -9394,7 +9394,7 @@ __metadata:
zod: "npm:~4.3.6"
peerDependencies:
typescript: ~5.9.2
checksum: 10/dd21576de694e4f18dcde46d3357f5ca3b817e3f9f20d2b66b526a35c716faee870f7a72356c86c1b59c27beddd53225725e126c460cd5f52eaba964bb807bdf
checksum: 10/71c8667f3d63e0b4ef0d82ee2b7c7c707494c286cfadf0c6be7e0feed7abc8817b0dfd44bb249861f8411c7747fdcfcde996a1c63d9c2396bba19d7ecb5689ac
languageName: node
linkType: hard

Expand Down Expand Up @@ -10001,7 +10001,7 @@ __metadata:
"@rocket.chat/emitter": "npm:^0.32.0"
"@rocket.chat/favicon": "workspace:^"
"@rocket.chat/federation-matrix": "workspace:^"
"@rocket.chat/federation-sdk": "npm:0.6.2"
"@rocket.chat/federation-sdk": "npm:0.6.3"
"@rocket.chat/fuselage": "npm:^0.73.0"
"@rocket.chat/fuselage-forms": "npm:^1.0.0"
"@rocket.chat/fuselage-hooks": "npm:^0.40.0"
Expand Down Expand Up @@ -11366,7 +11366,7 @@ __metadata:
peerDependencies:
"@rocket.chat/layout": "*"
"@rocket.chat/tools": 0.2.5-rc.0
"@rocket.chat/ui-contexts": 29.0.0-rc.2
"@rocket.chat/ui-contexts": 29.0.0-rc.3
"@tanstack/react-query": "*"
react: "*"
react-hook-form: "*"
Expand Down
Loading