From 895b178720a4a40c9adc36d06a24d9f682ec7334 Mon Sep 17 00:00:00 2001 From: strawberry Date: Sun, 15 Sep 2024 11:36:47 -0400 Subject: [PATCH] add admin command to force demote a local user from a room Signed-off-by: strawberry --- src/admin/user/commands.rs | 76 +++++++++++++++++++++++++++++++++++++- src/admin/user/mod.rs | 7 ++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 551ce20b..b51f32ef 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -4,9 +4,13 @@ use api::client::{join_room_by_id_helper, leave_all_rooms, leave_room, update_av use conduit::{error, info, utils, warn, PduBuilder, Result}; use ruma::{ events::{ - room::{message::RoomMessageEventContent, redaction::RoomRedactionEventContent}, + room::{ + message::RoomMessageEventContent, + power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, + redaction::RoomRedactionEventContent, + }, tag::{TagEvent, TagEventContent, TagInfo}, - RoomAccountDataEventType, TimelineEventType, + RoomAccountDataEventType, StateEventType, TimelineEventType, }, EventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, RoomId, }; @@ -387,6 +391,74 @@ pub(super) async fn force_leave_room( ))) } +#[admin_command] +pub(super) async fn force_demote( + &self, user_id: String, room_id: OwnedRoomOrAliasId, +) -> Result { + let user_id = parse_local_user_id(self.services, &user_id)?; + let room_id = self.services.rooms.alias.resolve(&room_id).await?; + + assert!( + self.services.globals.user_is_local(&user_id), + "Parsed user_id must be a local user" + ); + + let state_lock = self.services.rooms.state.mutex.lock(&room_id).await; + + let room_power_levels = self + .services + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomPowerLevels, "")? + .as_ref() + .and_then(|event| serde_json::from_str(event.content.get()).ok()?) + .and_then(|content: RoomPowerLevelsEventContent| content.into()); + + let user_can_demote_self = room_power_levels + .as_ref() + .is_some_and(|power_levels_content| { + RoomPowerLevels::from(power_levels_content.clone()).user_can_change_user_power_level(&user_id, &user_id) + }) || self + .services + .rooms + .state_accessor + .room_state_get(&room_id, &StateEventType::RoomCreate, "")? + .as_ref() + .is_some_and(|event| event.sender == user_id); + + if !user_can_demote_self { + return Ok(RoomMessageEventContent::notice_markdown( + "User is not allowed to modify their own power levels in the room.", + )); + } + + let mut power_levels_content = room_power_levels.unwrap_or_default(); + power_levels_content.users.remove(&user_id); + + let event_id = self + .services + .rooms + .timeline + .build_and_append_pdu( + PduBuilder { + event_type: TimelineEventType::RoomPowerLevels, + content: to_raw_value(&power_levels_content).expect("event is valid, we just created it"), + unsigned: None, + state_key: Some(String::new()), + redacts: None, + timestamp: None, + }, + &user_id, + &room_id, + &state_lock, + ) + .await?; + + Ok(RoomMessageEventContent::notice_markdown(format!( + "User {user_id} demoted themselves to the room default power level in {room_id} - {event_id}" + ))) +} + #[admin_command] pub(super) async fn make_user_admin(&self, user_id: String) -> Result { let user_id = parse_local_user_id(self.services, &user_id)?; diff --git a/src/admin/user/mod.rs b/src/admin/user/mod.rs index d8d740bf..e7bb5c73 100644 --- a/src/admin/user/mod.rs +++ b/src/admin/user/mod.rs @@ -79,6 +79,13 @@ pub(super) enum UserCommand { room_id: OwnedRoomOrAliasId, }, + /// - Forces the specified user to drop their power levels to the room + /// default, if their permissions allow and the auth check permits + ForceDemote { + user_id: String, + room_id: OwnedRoomOrAliasId, + }, + /// - Grant server-admin privileges to a user. MakeUserAdmin { user_id: String,