diff --git a/.changeset/dull-tips-look.md b/.changeset/dull-tips-look.md new file mode 100644 index 0000000000000..8867fc650dc12 --- /dev/null +++ b/.changeset/dull-tips-look.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/core-services": patch +"@rocket.chat/federation-matrix": patch +--- + +Fixes an issue where cases of invites that were canceled or disinvited were not being handled. diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 8fc0c859861e6..4cb03f5afd636 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -98,7 +98,7 @@ "@rocket.chat/emitter": "~0.31.25", "@rocket.chat/favicon": "workspace:^", "@rocket.chat/federation-matrix": "workspace:^", - "@rocket.chat/federation-sdk": "0.3.3", + "@rocket.chat/federation-sdk": "0.3.5", "@rocket.chat/freeswitch": "workspace:^", "@rocket.chat/fuselage": "^0.69.0", "@rocket.chat/fuselage-forms": "~0.1.1", diff --git a/ee/packages/federation-matrix/package.json b/ee/packages/federation-matrix/package.json index 6b9a5ca9eac4b..69d6c83ad5131 100644 --- a/ee/packages/federation-matrix/package.json +++ b/ee/packages/federation-matrix/package.json @@ -22,7 +22,7 @@ "@rocket.chat/core-services": "workspace:^", "@rocket.chat/core-typings": "workspace:^", "@rocket.chat/emitter": "^0.31.25", - "@rocket.chat/federation-sdk": "0.3.3", + "@rocket.chat/federation-sdk": "0.3.5", "@rocket.chat/http-router": "workspace:^", "@rocket.chat/license": "workspace:^", "@rocket.chat/models": "workspace:^", diff --git a/ee/packages/federation-matrix/src/FederationMatrix.ts b/ee/packages/federation-matrix/src/FederationMatrix.ts index 32e8a7e98b7c3..9d5f0a4cf0ab2 100644 --- a/ee/packages/federation-matrix/src/FederationMatrix.ts +++ b/ee/packages/federation-matrix/src/FederationMatrix.ts @@ -8,7 +8,7 @@ import { UserStatus, } from '@rocket.chat/core-typings'; import type { MessageQuoteAttachment, IMessage, IRoom, IUser, IRoomNativeFederated } from '@rocket.chat/core-typings'; -import { eventIdSchema, roomIdSchema, userIdSchema, federationSDK } from '@rocket.chat/federation-sdk'; +import { eventIdSchema, roomIdSchema, userIdSchema, federationSDK, FederationRequestError } from '@rocket.chat/federation-sdk'; import type { EventID, UserID, FileMessageType, PresenceState } from '@rocket.chat/federation-sdk'; import { Logger } from '@rocket.chat/logger'; import { Users, Subscriptions, Messages, Rooms, Settings } from '@rocket.chat/models'; @@ -923,7 +923,15 @@ export class FederationMatrix extends ServiceClass implements IFederationMatrixS await Room.performAcceptRoomInvite(room, subscription, user); } if (action === 'reject') { - await federationSDK.rejectInvite(room.federation.mrid, matrixUserId); + try { + await federationSDK.rejectInvite(room.federation.mrid, matrixUserId); + } catch (error) { + if (error instanceof FederationRequestError && error.response.status === 403) { + return Room.performUserRemoval(room, user); + } + this.logger.error(error, 'Failed to reject invite in Matrix'); + throw error; + } } } } diff --git a/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts b/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts index ecbd82b2bae4f..8a76d7ef1a4e4 100644 --- a/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts +++ b/ee/packages/federation-matrix/tests/end-to-end/room.spec.ts @@ -1647,18 +1647,62 @@ import { SynapseClient } from '../helper/synapse-client'; rid = pendingInvitation?.rid!; }, 15000); - it('It should allow RC user to reject the invite', async () => { + it('should allow RC user to reject the invite and remove the subscription', async () => { const rejectResponse = await rejectRoomInvite(rid, rc1AdminRequestConfig); expect(rejectResponse.success).toBe(true); - }); - it('It should remove the subscription after rejection', async () => { const subscriptions = await getSubscriptions(rc1AdminRequestConfig); - const invitedSub = subscriptions.update.find((sub) => sub.fname?.includes(channelName)); - expect(invitedSub).toBeFalsy(); }); }); + + describe('Revoked invitation flow from Synapse', () => { + describe('Synapse revokes an invitation before the RC user responds', () => { + let matrixRoomId: string; + let channelName: string; + let rid: string; + + beforeAll(async () => { + channelName = `federated-channel-revoked-${Date.now()}`; + matrixRoomId = await hs1AdminApp.createRoom(channelName); + + // hs1 invites RC user + await hs1AdminApp.matrixClient.invite(matrixRoomId, federationConfig.rc1.adminMatrixUserId); + const subscriptions = await getSubscriptions(rc1AdminRequestConfig); + + const pendingInvitation = subscriptions.update.find( + (subscription) => subscription.status === 'INVITED' && subscription.fname?.includes(channelName), + ); + + expect(pendingInvitation).not.toBeUndefined(); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + rid = pendingInvitation?.rid!; + + // hs1 revokes the invitation by kicking the invited user + await hs1AdminApp.matrixClient.kick(matrixRoomId, federationConfig.rc1.adminMatrixUserId, 'Invitation revoked'); + }, 15000); + + it('should fail when RC user tries to accept the revoked invitation', async () => { + const acceptResponse = await acceptRoomInvite(rid, rc1AdminRequestConfig); + expect(acceptResponse.success).toBe(false); + }); + + it('should allow RC user to reject the revoked invitation and remove the subscription', async () => { + const rejectResponse = await rejectRoomInvite(rid, rc1AdminRequestConfig); + expect(rejectResponse.success).toBe(true); + + const subscriptions = await getSubscriptions(rc1AdminRequestConfig); + const invitedSub = subscriptions.update.find((sub) => sub.fname?.includes(channelName)); + expect(invitedSub).toBeFalsy(); + }); + + it('should have the RC user with leave membership on Synapse side after revoked invitation', async () => { + const member = await hs1AdminApp.findRoomMember(channelName, federationConfig.rc1.adminMatrixUserId); + expect(member?.membership).toBe('leave'); + }); + }); + }); }); }); diff --git a/packages/core-services/package.json b/packages/core-services/package.json index 70ddd59351cd2..395b780ebf464 100644 --- a/packages/core-services/package.json +++ b/packages/core-services/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@rocket.chat/core-typings": "workspace:^", - "@rocket.chat/federation-sdk": "0.3.3", + "@rocket.chat/federation-sdk": "0.3.5", "@rocket.chat/http-router": "workspace:^", "@rocket.chat/icons": "~0.45.0", "@rocket.chat/media-signaling": "workspace:^", diff --git a/yarn.lock b/yarn.lock index af877dec39f25..860d7c99de08f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8284,7 +8284,7 @@ __metadata: "@rocket.chat/apps-engine": "workspace:^" "@rocket.chat/core-typings": "workspace:^" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/federation-sdk": "npm:0.3.3" + "@rocket.chat/federation-sdk": "npm:0.3.5" "@rocket.chat/http-router": "workspace:^" "@rocket.chat/icons": "npm:~0.45.0" "@rocket.chat/jest-presets": "workspace:~" @@ -8495,7 +8495,7 @@ __metadata: "@rocket.chat/ddp-client": "workspace:^" "@rocket.chat/emitter": "npm:^0.31.25" "@rocket.chat/eslint-config": "workspace:^" - "@rocket.chat/federation-sdk": "npm:0.3.3" + "@rocket.chat/federation-sdk": "npm:0.3.5" "@rocket.chat/http-router": "workspace:^" "@rocket.chat/license": "workspace:^" "@rocket.chat/models": "workspace:^" @@ -8521,9 +8521,9 @@ __metadata: languageName: unknown linkType: soft -"@rocket.chat/federation-sdk@npm:0.3.3": - version: 0.3.3 - resolution: "@rocket.chat/federation-sdk@npm:0.3.3" +"@rocket.chat/federation-sdk@npm:0.3.5": + version: 0.3.5 + resolution: "@rocket.chat/federation-sdk@npm:0.3.5" dependencies: "@datastructures-js/priority-queue": "npm:^6.3.5" "@noble/ed25519": "npm:^3.0.0" @@ -8536,7 +8536,7 @@ __metadata: zod: "npm:^3.24.1" peerDependencies: typescript: ~5.9.2 - checksum: 10/e93f0d59da8508ee0ecfb53f6d599f93130ab4b9f651d87da77615355261e4852d6f4659aae9f4c8881cf4b26a628512e6363ecc407d6e01a31a27d20b9d7684 + checksum: 10/47de2265555649b375620c7a90cf84134b14f3e38d171d84276dee9196b7b303a2d8c6f693223d63ba1c464ce12c128c5f6a795c0bb42c0e5339587b2429d034 languageName: node linkType: hard @@ -9185,7 +9185,7 @@ __metadata: "@rocket.chat/eslint-config": "workspace:^" "@rocket.chat/favicon": "workspace:^" "@rocket.chat/federation-matrix": "workspace:^" - "@rocket.chat/federation-sdk": "npm:0.3.3" + "@rocket.chat/federation-sdk": "npm:0.3.5" "@rocket.chat/freeswitch": "workspace:^" "@rocket.chat/fuselage": "npm:^0.69.0" "@rocket.chat/fuselage-forms": "npm:~0.1.1"