diff --git a/packages/room/src/authorizartion-rules/rules-v2.1.ts b/packages/room/src/authorizartion-rules/rules-v2.1.ts new file mode 100644 index 000000000..bf9247826 --- /dev/null +++ b/packages/room/src/authorizartion-rules/rules-v2.1.ts @@ -0,0 +1,23 @@ +import { PersistentEventFactory } from ".."; +import { PersistentEventBase } from "../manager/event-wrapper"; + + +function isCreateAllowed(event: PersistentEventBase<'12', 'm.room.create'>): boolean { + if (event.getPreviousEventIds().length > 0) { + return false; + } + + if (event.event.room_id) { + // changed in this version + return false; + } + + // SPEC: If content.room_version is present and is not a recognised version, reject. + if (!PersistentEventFactory.isSupportedRoomVersion( event.getContent().room_version)) { + return false; + } + // SPEC: [New in this version] If additional_creators is present in content and is not an array of strings where each string passes the same user ID validation applied to sender, reject. + // ^ TODO + + return true; +} \ No newline at end of file diff --git a/packages/room/src/manager/event-wrapper.ts b/packages/room/src/manager/event-wrapper.ts index 7328dd937..dafb1870b 100644 --- a/packages/room/src/manager/event-wrapper.ts +++ b/packages/room/src/manager/event-wrapper.ts @@ -6,7 +6,7 @@ import { PowerLevelEvent } from './power-level-event-wrapper'; import { type RoomVersion } from './type'; import { type RejectCode, RejectCodes } from '../authorizartion-rules/errors'; import { type EventStore, getStateMapKey } from '../state_resolution/definitions/definitions'; -import type { EventID, PduForType, StateMapKey } from '../types/_common'; +import type { EventID, RoomID, PduForType, StateMapKey } from '../types/_common'; import type { Pdu, PduContent, PduType, Signature, PduJoinRuleEventContent, PduMembershipEventContent } from '../types/v3-11'; export function extractDomainFromId(identifier: string) { @@ -85,10 +85,6 @@ export abstract class PersistentEventBase>(): T { return this.rawEvent.content as T; diff --git a/packages/room/src/manager/factory.ts b/packages/room/src/manager/factory.ts index 38ed5f027..2a8d123a9 100644 --- a/packages/room/src/manager/factory.ts +++ b/packages/room/src/manager/factory.ts @@ -8,6 +8,7 @@ import { PersistentEventV9 } from './v9'; import { RoomID, roomIdSchema } from '../types/_common'; import type { PduForType, UserID } from '../types/_common'; import type { Pdu, PduType, PduCreateEventContent } from '../types/v3-11'; +import { PersistentEventV12 } from './v12'; // Utility function to create a random ID for room creation function createRoomIdPrefix(length: number) { @@ -36,9 +37,10 @@ export class PersistentEventFactory { '9', '10', '11', + '12', ]; - static defaultRoomVersion = '10' as const; // same as synapse + static defaultRoomVersion = '12' as const; // same as synapse static isSupportedRoomVersion(roomVersion: string): roomVersion is RoomVersion { return PersistentEventFactory.supportedRoomVersions.includes(roomVersion); @@ -68,6 +70,8 @@ export class PersistentEventFactory { return new PersistentEventV9(event, roomVersion, partial); case '11': return new PersistentEventV11(event, roomVersion, partial); + case '12': + return new PersistentEventV12(event, roomVersion, partial); default: throw new Error(`Unknown room version: ${roomVersion}`); } diff --git a/packages/room/src/manager/type.ts b/packages/room/src/manager/type.ts index 5ec8eb401..32317deee 100644 --- a/packages/room/src/manager/type.ts +++ b/packages/room/src/manager/type.ts @@ -1,5 +1,5 @@ export type RoomVersion1And2 = '1' | '2'; -export type RoomVersion3To11 = '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11'; +export type RoomVersion3To12 = '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12'; -export type RoomVersion = RoomVersion1And2 | RoomVersion3To11; +export type RoomVersion = RoomVersion1And2 | RoomVersion3To12; diff --git a/packages/room/src/manager/v12.ts b/packages/room/src/manager/v12.ts new file mode 100644 index 000000000..7d4bb7655 --- /dev/null +++ b/packages/room/src/manager/v12.ts @@ -0,0 +1,13 @@ +import { REDACT_ALLOW_ALL_KEYS } from './event-wrapper'; +import { PersistentEventV9 } from './v9'; +import { type PduType } from '../types/v3-11'; +import { RoomID } from '../types/_common'; +import { PersistentEventV11 } from './v11'; + +export class PersistentEventV12 extends PersistentEventV11 { + get roomId(): RoomID { + const eid = this.eventId; + // SPEC: Note: The room ID is the event ID of the event with sigil ! instead of $. + return eid.replace(/^\$/, '!') as RoomID; + } +} diff --git a/packages/room/src/manager/v3.ts b/packages/room/src/manager/v3.ts index 00ddf0604..e39ddc94b 100644 --- a/packages/room/src/manager/v3.ts +++ b/packages/room/src/manager/v3.ts @@ -2,12 +2,12 @@ import { toUnpaddedBase64 } from '@rocket.chat/federation-crypto'; import { PersistentEventBase } from './event-wrapper'; import type { REDACT_ALLOW_ALL_KEYS } from './event-wrapper'; -import type { RoomVersion3To11 } from './type'; -import type { EventID } from '../types/_common'; +import type { RoomVersion3To12 } from './type'; +import type { EventID, RoomID } from '../types/_common'; import type { PduType } from '../types/v3-11'; // v3 is where it changes first -export class PersistentEventV3 extends PersistentEventBase { +export class PersistentEventV3 extends PersistentEventBase { private _eventId?: EventID; get eventId(): EventID { @@ -22,6 +22,10 @@ export class PersistentEventV3 extends Persisten this._eventId = `\$${toUnpaddedBase64(referenceHash, { urlSafe: true })}` as EventID; return this._eventId; } + + get roomId(): RoomID { + return this.rawEvent.room_id as RoomID; + } getAllowedKeys(): string[] { return [ diff --git a/packages/room/src/types/v3-11.ts b/packages/room/src/types/v3-11.ts index 1cec0ff8c..f51e3e15b 100644 --- a/packages/room/src/types/v3-11.ts +++ b/packages/room/src/types/v3-11.ts @@ -1,7 +1,8 @@ import * as z from 'zod'; -import type { PduForType } from './_common'; +import type { PduForType, UserID } from './_common'; import { eventIdSchema, roomIdSchema, userIdSchema } from './_common'; +import type { RoomVersion } from '../manager/type'; // Copied from: https://github.com/element-hq/synapse/blob/2277df2a1eb685f85040ef98fa21d41aa4cdd389/synapse/api/constants.py#L103-L141 @@ -116,7 +117,7 @@ export const PduCreateEventContentSchema = z.object({ }) .optional(), 'room_version': z - .enum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']) + .enum(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']) .describe(" The version of the room. Defaults to '1' if the key does not exist.") .optional() .default('1'), @@ -623,6 +624,22 @@ export const PduTimelineSchema = z.discriminatedUnion('type', [ export const PduSchema = z.discriminatedUnion('type', [...PduTimelineSchema.options, ...PduStateEventSchema.options]); -export type Pdu = z.infer; +type PduV3To11 = z.infer; + +// SPEC: https://spec.matrix.org/v1.19/rooms/v12/ +// - `room_id` is derived from the m.room.create event's own event ID, so it is omitted from that event. +// - `content.additional_creators` may list extra user IDs who are also treated as room creators. +type PduCreateEventContentV12 = Omit & { + room_version: '12'; + additional_creators?: UserID[]; +}; + +type PduCreateEventV12 = Omit, 'room_id' | 'content'> & { + content: PduCreateEventContentV12; +}; + +export type Pdu> = V extends '12' + ? Exclude | PduCreateEventV12 + : PduV3To11; export type PduContent = PduForType['content'];