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
14 changes: 3 additions & 11 deletions packages/federation-sdk/src/repositories/event.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,15 @@ export class EventRepository {
queries = [baseQueries.create, baseQueries.powerLevels];
break;

case 'm.reaction':
case 'm.room.name':
case 'm.room.message':
case 'm.room.encrypted':
case 'm.room.member':
case 'm.room.power_levels':
case 'm.room.topic':
case 'm.room.server_acl':
default:
// for all other events (known and unknown), we need to fetch the create,
// power levels, and membership events for proper authorization
queries = [
baseQueries.create,
baseQueries.powerLevels,
baseQueries.membership,
];
break;

default:
throw new Error(`Unsupported event type: ${eventType}`);
}

return this.collection.find({ $or: queries.map((q) => q.query) });
Expand Down
149 changes: 145 additions & 4 deletions packages/federation-sdk/src/services/state.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('StateService', async () => {
}

const databaseConfig = {
uri: 'mongodb://localhost:27017',
uri: process.env.MONGO_URI || 'mongodb://localhost:27017',
name: 'matrix_test',
poolSize: 100,
};
Expand All @@ -139,8 +139,8 @@ describe('StateService', async () => {

beforeEach(async () => {
await Promise.all([
eventCollection.deleteMany(),
stateGraphCollection.deleteMany(),
eventCollection.deleteMany({}),
stateGraphCollection.deleteMany({}),
]);
});

Expand All @@ -157,6 +157,7 @@ describe('StateService', async () => {
const createRoom = async (
joinRule: PduJoinRuleEventContent['join_rule'],
userPowers: PduPowerLevelsEventContent['users'] = {},
eventsPowers: PduPowerLevelsEventContent['events'] = {},
) => {
const username = '@alice:example.com';
const name = 'Test Room';
Expand Down Expand Up @@ -212,7 +213,9 @@ describe('StateService', async () => {
...userPowers,
},
users_default: 0,
events: {},
events: {
...eventsPowers,
},
events_default: 0,
state_default: 50,
ban: 50,
Expand Down Expand Up @@ -1275,6 +1278,144 @@ describe('StateService', async () => {
expect(bobLeaveEvent.rejected).toBeTrue();
expect(bobLeaveEvent.rejectCode).toBe(RejectCodes.AuthError);
});
it('should reject event if the power level is not high enough', async () => {
const { roomCreateEvent } = await createRoom(
'public',
{},
{ 'rc.message': 70 },
);
const joinEvent = await joinUser(
roomCreateEvent.roomId,
'@bob:example.com',
);

const messageEvent = await stateService.buildEvent(
{
// @ts-expect-error - testing unknown event type
type: 'rc.message',
room_id: roomCreateEvent.roomId,
sender: joinEvent.sender,
content: { body: 'hello world', msgtype: 'm.text' },
...getDefaultFields(),
},
roomCreateEvent.getContent<PduCreateEventContent>().room_version,
);

await expect(() => stateService.handlePdu(messageEvent)).toThrow(
RejectCodes.AuthError,
);
});

it('should allow event if the power level is high enough', async () => {
const { roomCreateEvent, creatorMembershipEvent } = await createRoom(
'public',
{},
{ 'rc.message': 70 },
);

const messageEvent = await stateService.buildEvent(
{
// @ts-expect-error - testing unknown event type
type: 'rc.message',
room_id: roomCreateEvent.roomId,
sender: creatorMembershipEvent.sender,
content: { body: 'hello world', msgtype: 'm.text' },
...getDefaultFields(),
},
roomCreateEvent.getContent<PduCreateEventContent>().room_version,
);

await stateService.handlePdu(messageEvent);
expect(messageEvent.rejected).toBeFalsy();
});

it('12 should save unknown and custom events to database without throwing errors', async () => {
const { roomCreateEvent } = await createRoom('public');
const roomId = roomCreateEvent.roomId;
const roomVersion =
roomCreateEvent.getContent<PduCreateEventContent>().room_version;

// add a user with power to send events (events_default is 0 by default)
const bob = '@bob:example.com' as room.UserID;
await joinUser(roomId, bob);

// test 1: custom application event as timeline event (io.rocketchat.*)
const customEvent = await stateService.buildEvent(
{
// @ts-expect-error - testing unknown event type
type: 'io.rocketchat.custom',
room_id: roomId,
sender: bob,
// @ts-expect-error - testing unknown event type
content: { custom_field: 'test_value' },
...getDefaultFields(),
},
roomVersion,
);

// should not throw when processing custom event
await stateService.handlePdu(customEvent);
expect(customEvent.rejected).toBeFalsy();

// verify custom event is saved in database
const savedCustomEvent = await eventRepository.findById(
customEvent.eventId,
);
expect(savedCustomEvent).toBeDefined();
// @ts-expect-error - testing unknown event type
expect(savedCustomEvent?.event.type).toBe('io.rocketchat.custom');

// test 2: unknown Matrix standard event as timeline event (m.poll.start)
const unknownMatrixEvent = await stateService.buildEvent(
{
// @ts-expect-error - testing unknown event type
type: 'm.poll.start',
room_id: roomId,
sender: bob,
// @ts-expect-error - testing unknown event type
content: { question: 'Test poll?' },
...getDefaultFields(),
},
roomVersion,
);

// should not throw when processing unknown Matrix event
await stateService.handlePdu(unknownMatrixEvent);
expect(unknownMatrixEvent.rejected).toBeFalsy();

// verify unknown Matrix event is saved in database
const savedUnknownEvent = await eventRepository.findById(
unknownMatrixEvent.eventId,
);
expect(savedUnknownEvent).toBeDefined();
// @ts-expect-error - testing unknown event type
expect(savedUnknownEvent?.event.type).toBe('m.poll.start');

// test 3: custom event as timeline event (com.example.*)
const anotherCustomEvent = await stateService.buildEvent(
{
// @ts-expect-error - testing unknown event type
type: 'com.example.test',
room_id: roomId,
sender: bob,
// @ts-expect-error - testing unknown event type
content: { data: 'example' },
...getDefaultFields(),
},
roomVersion,
);

await stateService.handlePdu(anotherCustomEvent);
expect(anotherCustomEvent.rejected).toBeFalsy();

// verify it's saved
const savedExampleEvent = await eventRepository.findById(
anotherCustomEvent.eventId,
);
expect(savedExampleEvent).toBeDefined();
// @ts-expect-error - testing unknown event type
expect(savedExampleEvent?.event.type).toBe('com.example.test');
});

it('01#arriving_late should fix state in case of older event arriving late', async () => {
const { roomCreateEvent, powerLevelEvent, roomNameEvent } =
Expand Down
Loading