From a8fa237fad109142d83d0373037151d7d83a40c7 Mon Sep 17 00:00:00 2001 From: Matthias Ahouansou Date: Wed, 2 Jul 2025 19:39:03 +0100 Subject: [PATCH] refactor: use RoomVersionRules instead of matching against RoomVersionId --- src/api/client_server/room.rs | 88 ++++++--------- src/api/server_server.rs | 15 +-- src/service/admin/mod.rs | 21 ++-- src/service/globals/mod.rs | 15 ++- src/service/rooms/event_handler/mod.rs | 67 +++++------- src/service/rooms/helpers/mod.rs | 7 +- src/service/rooms/timeline/mod.rs | 141 ++++++++++--------------- 7 files changed, 135 insertions(+), 219 deletions(-) diff --git a/src/api/client_server/room.rs b/src/api/client_server/room.rs index 9c099984..fcd61a54 100644 --- a/src/api/client_server/room.rs +++ b/src/api/client_server/room.rs @@ -23,7 +23,7 @@ use ruma::{ }, int, serde::JsonObject, - CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, RoomVersionId, + CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, }; use serde_json::{json, value::to_raw_value}; use std::{cmp::max, collections::BTreeMap, sync::Arc}; @@ -137,6 +137,10 @@ pub async fn create_room_route( } None => services().globals.default_room_version(), }; + let rules = room_version + .rules() + .expect("Supported room version must have rules.") + .authorization; let content = match &body.creation_content { Some(content) => { @@ -144,26 +148,13 @@ pub async fn create_room_route( .deserialize_as::() .expect("Invalid creation content"); - match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - content.insert( - "creator".into(), - json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Invalid creation content") - })?, - ); - } - RoomVersionId::V11 => {} // V11 removed the "creator" key - _ => unreachable!("Validity of room version already checked"), + if !rules.use_room_create_sender { + content.insert( + "creator".into(), + json!(&sender_user).try_into().map_err(|_| { + Error::BadRequest(ErrorKind::BadJson, "Invalid creation content") + })?, + ); } content.insert( @@ -175,19 +166,10 @@ pub async fn create_room_route( content } None => { - let content = match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1(sender_user.clone()), - RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => unreachable!("Validity of room version already checked"), + let content = if rules.use_room_create_sender { + RoomCreateEventContent::new_v11() + } else { + RoomCreateEventContent::new_v1(sender_user.clone()) }; let mut content = serde_json::from_str::( to_raw_value(&content) @@ -602,6 +584,11 @@ pub async fn upgrade_room_route( )); } + let rules = body + .new_version + .rules() + .expect("Supported room version must have rules."); + // Create a replacement room let replacement_room = RoomId::new(services().globals.server_name()); services() @@ -676,30 +663,17 @@ pub async fn upgrade_room_route( )); // Send a m.room.create event containing a predecessor field and the applicable room_version - match body.new_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - create_event_content.insert( - "creator".into(), - json!(&sender_user).try_into().map_err(|_| { - Error::BadRequest(ErrorKind::BadJson, "Error forming creation event") - })?, - ); - } - RoomVersionId::V11 => { - // "creator" key no longer exists in V11 rooms - create_event_content.remove("creator"); - } - _ => unreachable!("Validity of room version already checked"), + if rules.authorization.use_room_create_sender { + create_event_content.remove("creator"); + } else { + create_event_content.insert( + "creator".into(), + json!(&sender_user).try_into().map_err(|_| { + Error::BadRequest(ErrorKind::BadJson, "Error forming creation event") + })?, + ); } + create_event_content.insert( "room_version".into(), json!(&body.new_version) diff --git a/src/api/server_server.rs b/src/api/server_server.rs index 946f02f4..1de6b6c4 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -2021,16 +2021,11 @@ fn user_can_perform_restricted_join( return Ok(false); }; - if matches!( - room_version_id, - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - ) { + let rules = room_version_id + .rules() + .expect("Supported room version must have rules.") + .authorization; + if !rules.restricted_join_rule { return Ok(false); } diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 957a5914..1aa5af3d 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -1693,19 +1693,14 @@ impl Service { services().users.create(conduit_user, None)?; let room_version = services().globals.default_room_version(); - let mut content = match room_version { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => RoomCreateEventContent::new_v1(conduit_user.to_owned()), - RoomVersionId::V11 => RoomCreateEventContent::new_v11(), - _ => unreachable!("Validity of room version already checked"), + let rules = room_version + .rules() + .expect("Supported room version must have rules.") + .authorization; + let mut content = if rules.use_room_create_sender { + RoomCreateEventContent::new_v11() + } else { + RoomCreateEventContent::new_v1(conduit_user.to_owned()) }; content.federate = true; content.predecessor = None; diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index f55e8c1a..0f716271 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -1,8 +1,8 @@ mod data; pub use data::{Data, SigningKeys}; use ruma::{ - serde::Base64, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedRoomAliasId, - OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, + room_version_rules::RoomVersionRules, serde::Base64, MilliSecondsSinceUnixEpoch, OwnedDeviceId, + OwnedEventId, OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, }; use crate::api::server_server::DestinationResponse; @@ -427,11 +427,11 @@ impl Service { &self, keys: BTreeMap, timestamp: MilliSecondsSinceUnixEpoch, - room_version_id: &RoomVersionId, + rules: &RoomVersionRules, ) -> BTreeMap> { keys.into_iter() .filter_map(|(server, keys)| { - self.filter_keys_single_server(keys, timestamp, room_version_id) + self.filter_keys_single_server(keys, timestamp, rules) .map(|keys| (server, keys)) }) .collect() @@ -443,15 +443,12 @@ impl Service { &self, keys: SigningKeys, timestamp: MilliSecondsSinceUnixEpoch, - room_version_id: &RoomVersionId, + rules: &RoomVersionRules, ) -> Option> { if keys.valid_until_ts > timestamp // valid_until_ts MUST be ignored in room versions 1, 2, 3, and 4. // https://spec.matrix.org/v1.10/server-server-api/#get_matrixkeyv2server - || matches!(room_version_id, RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V4 - | RoomVersionId::V3) + || !rules.enforce_key_validity { // Given that either the room version allows stale keys, or the valid_until_ts is // in the future, all verify_keys are valid diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler/mod.rs index f61f59a2..59d1ce03 100644 --- a/src/service/rooms/event_handler/mod.rs +++ b/src/service/rooms/event_handler/mod.rs @@ -34,7 +34,7 @@ use ruma::{ room_version_rules::{AuthorizationRules, RoomVersionRules}, state_res::{self, StateMap}, uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, - OwnedServerName, OwnedServerSigningKeyId, RoomId, RoomVersionId, ServerName, + OwnedServerName, OwnedServerSigningKeyId, RoomId, ServerName, }; use serde_json::value::RawValue as RawJsonValue; use tokio::sync::{RwLock, RwLockWriteGuard, Semaphore}; @@ -373,7 +373,7 @@ impl Service { let filtered_keys = services().globals.filter_keys_server_map( pkey_map, origin_server_ts, - room_version_id, + &room_version_rules, ); let mut val = @@ -846,48 +846,31 @@ impl Service { ) .is_err() || incoming_pdu.kind == TimelineEventType::RoomRedaction - && match room_version_id { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - if let Some(redact_id) = &incoming_pdu.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } - } - RoomVersionId::V11 => { - let content = serde_json::from_str::( - incoming_pdu.content.get(), - ) - .map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?; + && if room_version_rules.redaction.content_field_redacts { + let content = serde_json::from_str::( + incoming_pdu.content.get(), + ) + .map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?; - if let Some(redact_id) = &content.redacts { - !services().rooms.state_accessor.user_can_redact( - redact_id, - &incoming_pdu.sender, - &incoming_pdu.room_id, - true, - )? - } else { - false - } - } - _ => { - unreachable!("Validity of room version already checked") + if let Some(redact_id) = &content.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false } + } else if let Some(redact_id) = &incoming_pdu.redacts { + !services().rooms.state_accessor.user_can_redact( + redact_id, + &incoming_pdu.sender, + &incoming_pdu.room_id, + true, + )? + } else { + false }; // 14. Use state resolution to find new room state diff --git a/src/service/rooms/helpers/mod.rs b/src/service/rooms/helpers/mod.rs index a9d6c409..08f65039 100644 --- a/src/service/rooms/helpers/mod.rs +++ b/src/service/rooms/helpers/mod.rs @@ -722,10 +722,9 @@ async fn validate_and_add_event_id( let unfiltered_keys = (*pub_key_map.read().await).clone(); - let keys = - services() - .globals - .filter_keys_server_map(unfiltered_keys, origin_server_ts, room_version); + let keys = services() + .globals + .filter_keys_server_map(unfiltered_keys, origin_server_ts, rules); if let Err(e) = ruma::signatures::verify_event(&keys, &value, rules) { warn!("Event {} failed verification {:?} {}", event_id, pdu, e); diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index 85236c16..e9975879 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -23,7 +23,7 @@ use ruma::{ push::{Action, Ruleset, Tweak}, state_res::{self, Event}, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, - OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId, ServerName, UserId, + OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId, }; use serde::Deserialize; use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; @@ -383,46 +383,36 @@ impl Service { match pdu.kind { TimelineEventType::RoomRedaction => { let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; - match room_version_id { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - if let Some(redact_id) = &pdu.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; - } + let rules = room_version_id + .rules() + .expect("Supported room version must have rules.") + .redaction; + + if rules.content_field_redacts { + let content = + serde_json::from_str::(pdu.content.get()) + .map_err(|_| { + Error::bad_database("Invalid content in redaction pdu.") + })?; + if let Some(redact_id) = &content.redacts { + if services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + self.redact_pdu(redact_id, pdu, shortroomid)?; } } - RoomVersionId::V11 => { - let content = - serde_json::from_str::(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; - if let Some(redact_id) = &content.redacts { - if services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - self.redact_pdu(redact_id, pdu, shortroomid)?; - } - } + } else if let Some(redact_id) = &pdu.redacts { + if services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + self.redact_pdu(redact_id, pdu, shortroomid)?; } - _ => unreachable!("Validity of room version already checked"), }; } TimelineEventType::SpaceChild => { @@ -958,56 +948,39 @@ impl Service { // If redaction event is not authorized, do not append it to the timeline if pdu.kind == TimelineEventType::RoomRedaction { - match services().rooms.state.get_room_version(&pdu.room_id)? { - RoomVersionId::V1 - | RoomVersionId::V2 - | RoomVersionId::V3 - | RoomVersionId::V4 - | RoomVersionId::V5 - | RoomVersionId::V6 - | RoomVersionId::V7 - | RoomVersionId::V8 - | RoomVersionId::V9 - | RoomVersionId::V10 => { - if let Some(redact_id) = &pdu.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } - }; - } - RoomVersionId::V11 => { - let content = - serde_json::from_str::(pdu.content.get()) - .map_err(|_| { - Error::bad_database("Invalid content in redaction pdu.") - })?; + let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?; + let rules = room_version_id + .rules() + .expect("Supported room version must have rules.") + .redaction; - if let Some(redact_id) = &content.redacts { - if !services().rooms.state_accessor.user_can_redact( - redact_id, - &pdu.sender, - &pdu.room_id, - false, - )? { - return Err(Error::BadRequest( - ErrorKind::forbidden(), - "User cannot redact this event.", - )); - } + if rules.content_field_redacts { + let content = serde_json::from_str::(pdu.content.get()) + .map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?; + + if let Some(redact_id) = &content.redacts { + if !services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { + return Err(Error::BadRequest( + ErrorKind::forbidden(), + "User cannot redact this event.", + )); } } - _ => { + } else if let Some(redact_id) = &pdu.redacts { + if !services().rooms.state_accessor.user_can_redact( + redact_id, + &pdu.sender, + &pdu.room_id, + false, + )? { return Err(Error::BadRequest( - ErrorKind::UnsupportedRoomVersion, - "Unsupported room version", + ErrorKind::forbidden(), + "User cannot redact this event.", )); } }