From c028e0553c541d954b50f82f8ea058c43d8b7d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Wed, 9 Aug 2023 18:27:30 +0200
Subject: [PATCH 1/2] feat: registration tokens

---
 README.md                        |  5 +----
 src/api/client_server/account.rs | 23 ++++++++++++++++-------
 src/api/client_server/sync.rs    |  8 ++++----
 src/config/mod.rs                |  1 +
 src/database/mod.rs              |  4 ++++
 src/service/uiaa/mod.rs          | 11 +++++++++++
 6 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/README.md b/README.md
index 52ea3c1f..5e01c8c1 100644
--- a/README.md
+++ b/README.md
@@ -16,10 +16,7 @@ friends or company.
 #### Can I try it out?
 
 Yes! You can test our Conduit instance by opening a Matrix client (<https://app.element.io> or Element Android for
-example) and registering on the `conduit.rs` homeserver.
-
-*Registration is currently disabled because of scammers. For an account please
-  message us (see contact section below).*
+example) and registering on the `conduit.rs` homeserver. The registration token is "for_testing_only". Don't share personal information.
 
 Server hosting for conduit.rs is donated by the Matrix.org Foundation.
 
diff --git a/src/api/client_server/account.rs b/src/api/client_server/account.rs
index 1d7480a2..46551305 100644
--- a/src/api/client_server/account.rs
+++ b/src/api/client_server/account.rs
@@ -74,7 +74,10 @@ pub async fn get_register_available_route(
 /// - Creates a new account and populates it with default account data
 /// - If `inhibit_login` is false: Creates a device and returns device id and access_token
 pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
-    if !services().globals.allow_registration() && !body.from_appservice {
+    if !services().globals.allow_registration()
+        && !body.from_appservice
+        && services().globals.config.registration_token.is_none()
+    {
         return Err(Error::BadRequest(
             ErrorKind::Forbidden,
             "Registration has been disabled.",
@@ -121,7 +124,11 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
     // UIAA
     let mut uiaainfo = UiaaInfo {
         flows: vec![AuthFlow {
-            stages: vec![AuthType::Dummy],
+            stages: if services().globals.config.registration_token.is_some() {
+                vec![AuthType::RegistrationToken]
+            } else {
+                vec![AuthType::Dummy]
+            },
         }],
         completed: Vec::new(),
         params: Default::default(),
@@ -222,11 +229,13 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
     )?;
 
     info!("New user {} registered on this server.", user_id);
-    services()
-        .admin
-        .send_message(RoomMessageEventContent::notice_plain(format!(
-            "New user {user_id} registered on this server."
-        )));
+    if !body.from_appservice && !is_guest {
+        services()
+            .admin
+            .send_message(RoomMessageEventContent::notice_plain(format!(
+                "New user {user_id} registered on this server."
+            )));
+    }
 
     // If this is the first real user, grant them admin privileges
     // Note: the server user, @conduit:servername, is generated first
diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs
index a0edc437..dd815b52 100644
--- a/src/api/client_server/sync.rs
+++ b/src/api/client_server/sync.rs
@@ -1174,7 +1174,7 @@ pub async fn sync_events_v4_route(
 ) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
     let sender_user = body.sender_user.expect("user is authenticated");
     let sender_device = body.sender_device.expect("user is authenticated");
-    let mut body = dbg!(body.body);
+    let mut body = body.body;
     // Setup watchers, so if there's no response, we can wait for them
     let watcher = services().globals.watch(&sender_user, &sender_device);
 
@@ -1470,7 +1470,7 @@ pub async fn sync_events_v4_route(
     }
 
     let mut known_subscription_rooms = BTreeMap::new();
-    for (room_id, room) in dbg!(&body.room_subscriptions) {
+    for (room_id, room) in &body.room_subscriptions {
         let todo_room = todo_rooms
             .entry(room_id.clone())
             .or_insert((BTreeSet::new(), 0, true));
@@ -1680,7 +1680,7 @@ pub async fn sync_events_v4_route(
         let _ = tokio::time::timeout(duration, watcher).await;
     }
 
-    Ok(dbg!(sync_events::v4::Response {
+    Ok(sync_events::v4::Response {
         initial: since == 0,
         txn_id: body.txn_id.clone(),
         pos: next_batch.to_string(),
@@ -1735,5 +1735,5 @@ pub async fn sync_events_v4_route(
             },
         },
         delta_token: None,
-    }))
+    })
 }
diff --git a/src/config/mod.rs b/src/config/mod.rs
index e2c2ff12..9128c529 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -46,6 +46,7 @@ pub struct Config {
     pub max_fetch_prev_events: u16,
     #[serde(default = "false_fn")]
     pub allow_registration: bool,
+    pub registration_token: Option<String>,
     #[serde(default = "true_fn")]
     pub allow_encryption: bool,
     #[serde(default = "false_fn")]
diff --git a/src/database/mod.rs b/src/database/mod.rs
index b36347df..e247d9f0 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -267,6 +267,10 @@ impl KeyValueDatabase {
             }
         };
 
+        if config.registration_token == Some(String::new()) {
+            return Err(Error::bad_config("Registration token is empty"));
+        }
+
         if config.max_request_size < 1024 {
             error!(?config.max_request_size, "Max request size is less than 1KB. Please increase it.");
         }
diff --git a/src/service/uiaa/mod.rs b/src/service/uiaa/mod.rs
index 147ce4d1..ed39af99 100644
--- a/src/service/uiaa/mod.rs
+++ b/src/service/uiaa/mod.rs
@@ -96,6 +96,17 @@ impl Service {
                 // Password was correct! Let's add it to `completed`
                 uiaainfo.completed.push(AuthType::Password);
             }
+            AuthData::RegistrationToken(t) => {
+                if Some(t.token.trim()) == services().globals.config.registration_token.as_deref() {
+                    uiaainfo.completed.push(AuthType::RegistrationToken);
+                } else {
+                    uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
+                        kind: ErrorKind::Forbidden,
+                        message: "Invalid registration token.".to_owned(),
+                    });
+                    return Ok((false, uiaainfo));
+                }
+            }
             AuthData::Dummy(_) => {
                 uiaainfo.completed.push(AuthType::Dummy);
             }

From 183558150d1c2a022b9be60b22295f78d2326b27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Wed, 9 Aug 2023 22:21:21 +0200
Subject: [PATCH 2/2] fix: don't show removed rooms in space

---
 src/service/rooms/spaces/mod.rs | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/service/rooms/spaces/mod.rs b/src/service/rooms/spaces/mod.rs
index 9b57d53b..53232f46 100644
--- a/src/service/rooms/spaces/mod.rs
+++ b/src/service/rooms/spaces/mod.rs
@@ -19,6 +19,7 @@ use ruma::{
             join_rules::{self, AllowRule, JoinRule, RoomJoinRulesEventContent},
             topic::RoomTopicEventContent,
         },
+        space::child::SpaceChildEventContent,
         StateEventType,
     },
     space::SpaceRoomJoinRule,
@@ -124,11 +125,24 @@ impl Service {
                     if event_type != StateEventType::SpaceChild {
                         continue;
                     }
+
+                    let pdu = services()
+                        .rooms
+                        .timeline
+                        .get_pdu(&id)?
+                        .ok_or_else(|| Error::bad_database("Event in space state not found"))?;
+
+                    if serde_json::from_str::<SpaceChildEventContent>(pdu.content.get())
+                        .ok()
+                        .and_then(|c| c.via)
+                        .map_or(true, |v| v.is_empty())
+                    {
+                        continue;
+                    }
+
                     if let Ok(room_id) = OwnedRoomId::try_from(state_key) {
                         children_ids.push(room_id);
-                        children_pdus.push(services().rooms.timeline.get_pdu(&id)?.ok_or_else(
-                            || Error::bad_database("Event in space state not found"),
-                        )?);
+                        children_pdus.push(pdu);
                     }
                 }