From 6bc8fb2ae7a895628e52c88f2b347dd1389b8858 Mon Sep 17 00:00:00 2001
From: Nyaaori <+@nyaaori.cat>
Date: Fri, 24 Sep 2021 07:16:34 +0000
Subject: [PATCH] Implement admin check and add config option for allowing room
 creation

---
 src/client_server/room.rs | 10 ++++++++++
 src/database.rs           |  2 ++
 src/database/globals.rs   |  4 ++++
 src/database/users.rs     | 19 ++++++++++++++++++-
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/client_server/room.rs b/src/client_server/room.rs
index 5a026997..f6c3a50e 100644
--- a/src/client_server/room.rs
+++ b/src/client_server/room.rs
@@ -61,6 +61,16 @@ pub async fn create_room_route(
     );
     let state_lock = mutex_state.lock().await;
 
+    if !db.globals.allow_room_creation()
+        && !body.from_appservice
+        && !db.users.is_admin(sender_user, &db.rooms, &db.globals)?
+    {
+        return Err(Error::BadRequest(
+            ErrorKind::Forbidden,
+            "Room creation has been disabled.",
+        ));
+    }
+
     let alias: Option<RoomAliasId> =
         body.room_alias_name
             .as_ref()
diff --git a/src/database.rs b/src/database.rs
index 110d4d0c..8a589299 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -61,6 +61,8 @@ pub struct Config {
     allow_encryption: bool,
     #[serde(default = "false_fn")]
     allow_federation: bool,
+    #[serde(default = "true_fn")]
+    allow_room_creation: bool,
     #[serde(default = "false_fn")]
     pub allow_jaeger: bool,
     #[serde(default = "false_fn")]
diff --git a/src/database/globals.rs b/src/database/globals.rs
index 2f1b45ad..c2ef1c36 100644
--- a/src/database/globals.rs
+++ b/src/database/globals.rs
@@ -211,6 +211,10 @@ impl Globals {
         self.config.allow_federation
     }
 
+    pub fn allow_room_creation(&self) -> bool {
+        self.config.allow_room_creation
+    }
+
     pub fn trusted_servers(&self) -> &[Box<ServerName>] {
         &self.config.trusted_servers
     }
diff --git a/src/database/users.rs b/src/database/users.rs
index 63ed0710..ee064903 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -5,7 +5,8 @@ use ruma::{
     events::{AnyToDeviceEvent, EventType},
     identifiers::MxcUri,
     serde::Raw,
-    DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, UInt, UserId,
+    DeviceId, DeviceKeyAlgorithm, DeviceKeyId, MilliSecondsSinceUnixEpoch, RoomAliasId, UInt,
+    UserId,
 };
 use std::{collections::BTreeMap, convert::TryFrom, mem, sync::Arc};
 use tracing::warn;
@@ -53,6 +54,22 @@ impl Users {
             .is_empty())
     }
 
+    /// Check if a user is an admin
+    #[tracing::instrument(skip(self, user_id, rooms, globals))]
+    pub fn is_admin(
+        &self,
+        user_id: &UserId,
+        rooms: &super::rooms::Rooms,
+        globals: &super::globals::Globals,
+    ) -> Result<bool> {
+        let admin_room_alias_id =
+            RoomAliasId::try_from(format!("#admins:{}", globals.server_name()))
+                .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid alias."))?;
+        let admin_room_id = rooms.id_from_alias(&admin_room_alias_id)?.unwrap();
+
+        Ok(rooms.is_joined(user_id, &admin_room_id)?)
+    }
+
     /// Create a new user account on this homeserver.
     #[tracing::instrument(skip(self, user_id, password))]
     pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {