diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 2a36e921..d1871309 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1590,6 +1590,18 @@ # #admin_room_tag = "m.server_notice" +# A list of Matrix IDs that are qualified as server admins. +# +# Any Matrix IDs within this list are regarded as an admin +# regardless of whether they are in the admin room or not +# +#admins_list = [] + +# Defines whether those within the admin room are added to the +# admins_list. +# +#admins_from_room = true + # Sentry.io crash/panic reporting, performance monitoring/metrics, etc. # This is NOT enabled by default. # diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 2cf344f6..0a41e259 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -461,8 +461,10 @@ pub(super) async fn force_join_list_of_local_users( ); } - let Ok(admin_room) = self.services.admin.get_admin_room().await else { - return Err!("There is not an admin room to check for server admins.",); + let server_admins = self.services.admin.get_admins().await; + + if server_admins.is_empty() { + return Err!("There are no admins set for this server."); }; let (room_id, servers) = self @@ -482,15 +484,6 @@ pub(super) async fn force_join_list_of_local_users( return Err!("We are not joined in this room."); } - let server_admins: Vec<_> = self - .services - .rooms - .state_cache - .active_local_users_in_room(&admin_room) - .map(ToOwned::to_owned) - .collect() - .await; - if !self .services .rooms @@ -583,8 +576,10 @@ pub(super) async fn force_join_all_local_users( ); } - let Ok(admin_room) = self.services.admin.get_admin_room().await else { - return Err!("There is not an admin room to check for server admins.",); + let server_admins = self.services.admin.get_admins().await; + + if server_admins.is_empty() { + return Err!("There are no admins set for this server."); }; let (room_id, servers) = self @@ -604,15 +599,6 @@ pub(super) async fn force_join_all_local_users( return Err!("We are not joined in this room."); } - let server_admins: Vec<_> = self - .services - .rooms - .state_cache - .active_local_users_in_room(&admin_room) - .map(ToOwned::to_owned) - .collect() - .await; - if !self .services .rooms diff --git a/src/api/client/well_known.rs b/src/api/client/well_known.rs index 2d17eb5c..2eeaa350 100644 --- a/src/api/client/well_known.rs +++ b/src/api/client/well_known.rs @@ -1,6 +1,5 @@ use axum::{Json, extract::State, response::IntoResponse}; use conduwuit::{Error, Result}; -use futures::StreamExt; use ruma::api::client::{ discovery::{ discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo}, @@ -71,21 +70,18 @@ pub(crate) async fn well_known_support( // Try to add admin users as contacts if no contacts are configured if contacts.is_empty() { - if let Ok(admin_room) = services.admin.get_admin_room().await { - let admin_users = services.rooms.state_cache.room_members(&admin_room); - let mut stream = admin_users; + let admin_users = services.admin.get_admins().await; - while let Some(user_id) = stream.next().await { - // Skip server user - if *user_id == services.globals.server_user { - continue; - } - contacts.push(Contact { - role: role_value.clone(), - email_address: None, - matrix_id: Some(user_id.to_owned()), - }); + for user_id in admin_users.iter() { + if *user_id == services.globals.server_user { + continue; } + + contacts.push(Contact { + role: role_value.clone(), + email_address: None, + matrix_id: Some(user_id.to_owned()), + }); } } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index e3a447bd..665ce055 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1819,6 +1819,22 @@ pub struct Config { #[serde(default = "default_admin_room_tag")] pub admin_room_tag: String, + /// A list of Matrix IDs that are qualified as server admins. + /// + /// Any Matrix IDs within this list are regarded as an admin + /// regardless of whether they are in the admin room or not + /// + /// default: [] + #[serde(default)] + pub admins_list: Vec, + + /// Defines whether those within the admin room are added to the + /// admins_list. + /// + /// default: true + #[serde(default = "true_fn")] + pub admins_from_room: bool, + /// Sentry.io crash/panic reporting, performance monitoring/metrics, etc. /// This is NOT enabled by default. #[serde(default)] diff --git a/src/service/admin/grant.rs b/src/service/admin/grant.rs index d3d5d491..0e605dcc 100644 --- a/src/service/admin/grant.rs +++ b/src/service/admin/grant.rs @@ -1,6 +1,8 @@ use std::collections::BTreeMap; -use conduwuit::{Err, Result, debug_info, debug_warn, error, implement, matrix::pdu::PduBuilder}; +use conduwuit::{ + Err, Result, debug_info, debug_warn, error, implement, matrix::pdu::PduBuilder, warn, +}; use ruma::{ RoomId, UserId, events::{ @@ -176,6 +178,19 @@ async fn set_room_tag(&self, room_id: &RoomId, user_id: &UserId, tag: &str) -> R pub async fn revoke_admin(&self, user_id: &UserId) -> Result { use MembershipState::{Invite, Join, Knock, Leave}; + if self + .services + .server + .config + .admins_list + .contains(&user_id.to_owned()) + { + warn!( + "Revoking the admin status of {user_id} will not be persistent as they are within \ + the admins_list." + ) + } + let Ok(room_id) = self.get_admin_room().await else { return Err!(error!("No admin room available or created.")); }; diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index b07ca3b8..3d0ea461 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -14,10 +14,10 @@ use conduwuit_core::{ Error, Event, Result, Server, debug, err, error, error::default_log, pdu::PduBuilder, }; pub use create::create_admin_room; -use futures::{Future, FutureExt, TryFutureExt}; +use futures::{Future, FutureExt, StreamExt, TryFutureExt}; use loole::{Receiver, Sender}; use ruma::{ - Mxc, OwnedEventId, OwnedMxcUri, OwnedRoomId, RoomId, UInt, UserId, + Mxc, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UInt, UserId, events::{ Mentions, room::{ @@ -349,16 +349,30 @@ impl Service { handle(services, command).await } + /// Returns the list of admins for this server. First loads + /// the admin_list from the configuration, then adds users from + /// the admin room if applicable. + pub async fn get_admins(&self) -> Vec { + let mut generated_admin_list: Vec = + self.services.server.config.admins_list.clone(); + + if self.services.server.config.admins_from_room { + if let Ok(admin_room) = self.get_admin_room().await { + let admin_users = self.services.state_cache.room_members(&admin_room); + let mut stream = admin_users; + + while let Some(user_id) = stream.next().await { + generated_admin_list.push(user_id.to_owned()); + } + } + } + + generated_admin_list + } + /// Checks whether a given user is an admin of this server pub async fn user_is_admin(&self, user_id: &UserId) -> bool { - let Ok(admin_room) = self.get_admin_room().await else { - return false; - }; - - self.services - .state_cache - .is_joined(user_id, &admin_room) - .await + self.get_admins().await.contains(&user_id.to_owned()) } /// Gets the room ID of the admin room