From efea13a675178e052966c21e079dfa57b8560b26 Mon Sep 17 00:00:00 2001 From: strawberry Date: Fri, 5 Jul 2024 22:05:52 -0400 Subject: [PATCH] add access control checks for room directory publishing/removing Signed-off-by: strawberry --- src/api/client/directory.rs | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/api/client/directory.rs b/src/api/client/directory.rs index 512dface..7d2aff0d 100644 --- a/src/api/client/directory.rs +++ b/src/api/client/directory.rs @@ -10,10 +10,13 @@ use ruma::{ }, directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork}, events::{ - room::join_rules::{JoinRule, RoomJoinRulesEventContent}, + room::{ + join_rules::{JoinRule, RoomJoinRulesEventContent}, + power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, + }, StateEventType, }, - uint, ServerName, UInt, + uint, RoomId, ServerName, UInt, UserId, }; use tracing::{error, info, warn}; @@ -103,8 +106,6 @@ pub(crate) async fn get_public_rooms_route( /// # `PUT /_matrix/client/r0/directory/list/room/{roomId}` /// /// Sets the visibility of a given room in the room directory. -/// -/// - TODO: Access control checks #[tracing::instrument(skip_all, fields(%client), name = "room_directory")] pub(crate) async fn set_room_visibility_route( InsecureClientIp(client): InsecureClientIp, body: Ruma, @@ -116,6 +117,8 @@ pub(crate) async fn set_room_visibility_route( return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found")); } + user_can_publish_room(sender_user, &body.room_id)?; + match &body.visibility { room::Visibility::Public => { if services().globals.config.lockdown_public_room_directory && !services().users.is_admin(sender_user)? { @@ -351,3 +354,32 @@ pub(crate) async fn get_public_rooms_filtered_helper( total_room_count_estimate: Some(total_room_count_estimate), }) } + +/// Check whether the user can publish to the room directory via power levels of +/// room history visibility event or room creator +fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result { + if let Some(event) = + services() + .rooms + .state_accessor + .room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? + { + serde_json::from_str(event.content.get()) + .map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels")) + .map(|content: RoomPowerLevelsEventContent| { + RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility) + }) + } else if let Some(event) = + services() + .rooms + .state_accessor + .room_state_get(room_id, &StateEventType::RoomCreate, "")? + { + Ok(event.sender == user_id) + } else { + return Err(Error::BadRequest( + ErrorKind::Unauthorized, + "You are not allowed to publish this room to the room directory", + )); + } +}