diff --git a/packages/federation-sdk/src/index.ts b/packages/federation-sdk/src/index.ts index 7bee626e..42b4069a 100644 --- a/packages/federation-sdk/src/index.ts +++ b/packages/federation-sdk/src/index.ts @@ -124,6 +124,10 @@ export type HomeserverEventSignatures = { room_id: string; // room where the change happened role: 'moderator' | 'owner' | 'user'; // 50, 100, 0 }; + 'homeserver.matrix.membership.rejected': { + event: PduForType<'m.room.member'>; + reason: string; + }; }; export { roomIdSchema, userIdSchema, eventIdSchema, extractDomainFromId } from '@rocket.chat/federation-room'; diff --git a/packages/federation-sdk/src/services/event.service.ts b/packages/federation-sdk/src/services/event.service.ts index 9d9817bd..56aff611 100644 --- a/packages/federation-sdk/src/services/event.service.ts +++ b/packages/federation-sdk/src/services/event.service.ts @@ -167,12 +167,19 @@ export class EventService { try { await this.validateEvent(event); } catch (err) { - this.logger.error({ - msg: 'Event validation failed', - origin, - event, - err, - }); + if (err instanceof Error && err.name === 'UnknownRoomError' && event.type === 'm.room.member') { + await this.eventEmitterService.emit('homeserver.matrix.membership.rejected', { + event, + reason: err.message, + }); + } else { + this.logger.error({ + msg: 'Event validation failed', + origin, + event, + err, + }); + } continue; } diff --git a/packages/federation-sdk/src/services/state.service.spec.ts b/packages/federation-sdk/src/services/state.service.spec.ts index d6161451..9611aadb 100644 --- a/packages/federation-sdk/src/services/state.service.spec.ts +++ b/packages/federation-sdk/src/services/state.service.spec.ts @@ -2262,7 +2262,7 @@ describe('StateService', async () => { expect(servers.size).toBe(1); }); - it('should exclude servers with non-joined members', async () => { + it('should include servers with banned or invited members but exclude left', async () => { const { roomCreateEvent } = await createRoom('public'); const creator = '@alice:example.com'; // Room creator with admin permissions @@ -2284,9 +2284,9 @@ describe('StateService', async () => { expect(servers.has('joined.com')).toBe(true); expect(servers.has('left.com')).toBe(false); - expect(servers.has('banned.com')).toBe(false); - expect(servers.has('invited.com')).toBe(false); - expect(servers.size).toBe(2); // example.com (creator) + joined.com + expect(servers.has('banned.com')).toBe(true); + expect(servers.has('invited.com')).toBe(true); + expect(servers.size).toBe(4); // example.com (creator) + joined.com + banned.com + invited.com }); it('should return creator server for room with only creator', async () => { diff --git a/packages/federation-sdk/src/services/state.service.ts b/packages/federation-sdk/src/services/state.service.ts index 9eb6ba38..657568a1 100644 --- a/packages/federation-sdk/src/services/state.service.ts +++ b/packages/federation-sdk/src/services/state.service.ts @@ -697,8 +697,10 @@ export class StateService { const servers = new Set(); + const residentMemberships = new Set(['join', 'invite', 'ban']); + for (const event of state.values()) { - if (!event.isMembershipEvent() || event.getMembership() !== 'join') { + if (!event.isMembershipEvent() || !residentMemberships.has(event.getMembership() ?? '')) { continue; }