From 78a02edbbfcc86665e7f768515790ed243918739 Mon Sep 17 00:00:00 2001 From: RatCornu Date: Sat, 19 Apr 2025 18:48:14 +0200 Subject: [PATCH] feat: add userid_origin field in the database --- src/admin/user/commands.rs | 4 +- src/api/client/account.rs | 5 ++- src/api/client/profile.rs | 6 +-- src/api/client/unstable.rs | 4 +- src/database/maps.rs | 4 ++ src/service/admin/create.rs | 2 +- src/service/emergency/mod.rs | 3 +- src/service/rooms/state_cache/mod.rs | 2 +- src/service/users/mod.rs | 57 ++++++++++++++++++++-------- 9 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 47211312..ea4ae27c 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -68,7 +68,8 @@ pub(super) async fn create_user(&self, username: String, password: Option return Err!("Couldn't reset the password for user {user_id}: {e}"), | Ok(()) => diff --git a/src/api/client/account.rs b/src/api/client/account.rs index 2706d727..cbb0c48b 100644 --- a/src/api/client/account.rs +++ b/src/api/client/account.rs @@ -372,7 +372,7 @@ pub(crate) async fn register_route( let password = if is_guest { None } else { body.password.as_deref() }; // Create user - services.users.create(&user_id, password)?; + services.users.create(&user_id, password, None).await?; // Default to pretty displayname let mut displayname = user_id.localpart().to_owned(); @@ -663,7 +663,8 @@ pub(crate) async fn change_password_route( services .users - .set_password(sender_user, Some(&body.new_password))?; + .set_password(sender_user, Some(&body.new_password)) + .await?; if body.logout_devices { // Logout all devices except the current one diff --git a/src/api/client/profile.rs b/src/api/client/profile.rs index e6a3023c..9c65a243 100644 --- a/src/api/client/profile.rs +++ b/src/api/client/profile.rs @@ -90,7 +90,7 @@ pub(crate) async fn get_displayname_route( .await { if !services.users.exists(&body.user_id).await { - services.users.create(&body.user_id, None)?; + services.users.create(&body.user_id, None, None).await?; } services @@ -193,7 +193,7 @@ pub(crate) async fn get_avatar_url_route( .await { if !services.users.exists(&body.user_id).await { - services.users.create(&body.user_id, None)?; + services.users.create(&body.user_id, None, None).await?; } services @@ -255,7 +255,7 @@ pub(crate) async fn get_profile_route( .await { if !services.users.exists(&body.user_id).await { - services.users.create(&body.user_id, None)?; + services.users.create(&body.user_id, None, None).await?; } services diff --git a/src/api/client/unstable.rs b/src/api/client/unstable.rs index 2282f18f..c0aad378 100644 --- a/src/api/client/unstable.rs +++ b/src/api/client/unstable.rs @@ -306,7 +306,7 @@ pub(crate) async fn get_timezone_key_route( .await { if !services.users.exists(&body.user_id).await { - services.users.create(&body.user_id, None)?; + services.users.create(&body.user_id, None, None).await?; } services @@ -366,7 +366,7 @@ pub(crate) async fn get_profile_key_route( .await { if !services.users.exists(&body.user_id).await { - services.users.create(&body.user_id, None)?; + services.users.create(&body.user_id, None, None).await?; } services diff --git a/src/database/maps.rs b/src/database/maps.rs index 2152353e..fbd86a0a 100644 --- a/src/database/maps.rs +++ b/src/database/maps.rs @@ -374,6 +374,10 @@ pub(super) static MAPS: &[Descriptor] = &[ name: "userid_masterkeyid", ..descriptor::RANDOM_SMALL }, + Descriptor { + name: "userid_origin", + ..descriptor::RANDOM + }, Descriptor { name: "userid_password", ..descriptor::RANDOM diff --git a/src/service/admin/create.rs b/src/service/admin/create.rs index 6ce498fd..324f66d2 100644 --- a/src/service/admin/create.rs +++ b/src/service/admin/create.rs @@ -38,7 +38,7 @@ pub async fn create_admin_room(services: &Services) -> Result { // Create a user for the server let server_user = services.globals.server_user.as_ref(); - services.users.create(server_user, None)?; + services.users.create(server_user, None, None).await?; let create_content = { use RoomVersionId::*; diff --git a/src/service/emergency/mod.rs b/src/service/emergency/mod.rs index 4ffa2bc2..e47067ea 100644 --- a/src/service/emergency/mod.rs +++ b/src/service/emergency/mod.rs @@ -61,7 +61,8 @@ impl Service { self.services .users - .set_password(server_user, self.services.config.emergency_password.as_deref())?; + .set_password(server_user, self.services.config.emergency_password.as_deref()) + .await?; let (ruleset, pwd_set) = match self.services.config.emergency_password { | Some(_) => (Ruleset::server_default(server_user), true), diff --git a/src/service/rooms/state_cache/mod.rs b/src/service/rooms/state_cache/mod.rs index dbb0691f..c453dca3 100644 --- a/src/service/rooms/state_cache/mod.rs +++ b/src/service/rooms/state_cache/mod.rs @@ -133,7 +133,7 @@ impl Service { #[allow(clippy::collapsible_if)] if !self.services.globals.user_is_local(user_id) { if !self.services.users.exists(user_id).await { - self.services.users.create(user_id, None)?; + self.services.users.create(user_id, None, None).await?; } /* diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs index d7fd2f0f..8bb6742d 100644 --- a/src/service/users/mod.rs +++ b/src/service/users/mod.rs @@ -52,6 +52,7 @@ struct Data { userid_lastonetimekeyupdate: Arc, userid_masterkeyid: Arc, userid_password: Arc, + userid_origin: Arc, userid_selfsigningkeyid: Arc, userid_usersigningkeyid: Arc, useridprofilekey_value: Arc, @@ -87,6 +88,7 @@ impl crate::Service for Service { userid_lastonetimekeyupdate: args.db["userid_lastonetimekeyupdate"].clone(), userid_masterkeyid: args.db["userid_masterkeyid"].clone(), userid_password: args.db["userid_password"].clone(), + userid_origin: args.db["userid_origin"].clone(), userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(), userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(), useridprofilekey_value: args.db["useridprofilekey_value"].clone(), @@ -122,8 +124,17 @@ impl Service { /// Create a new user account on this homeserver. #[inline] - pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { - self.set_password(user_id, password) + pub async fn create( + &self, + user_id: &UserId, + password: Option<&str>, + origin: Option<&str>, + ) -> Result<()> { + origin.map_or_else( + || self.db.userid_origin.insert(user_id, "password"), + |origin| self.db.userid_origin.insert(user_id, origin), + ); + self.set_password(user_id, password).await } /// Deactivate account @@ -137,7 +148,7 @@ impl Service { // result in an empty string, so the user will not be able to log in again. // Systems like changing the password without logging in should check if the // account is deactivated. - self.set_password(user_id, None)?; + self.set_password(user_id, None).await?; // TODO: Unhook 3PID Ok(()) @@ -209,6 +220,11 @@ impl Service { .ready_filter_map(|(u, p): (&UserId, &[u8])| (!p.is_empty()).then_some(u)) } + /// Returns the origin of the user (password/LDAP/...). + pub async fn origin(&self, user_id: &UserId) -> Result { + self.db.userid_origin.get(user_id).await.deserialized() + } + /// Returns the password hash for the given user. pub async fn password_hash(&self, user_id: &UserId) -> Result { self.db @@ -219,19 +235,30 @@ impl Service { } /// Hash and set the user's password to the Argon2 hash - pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { - password - .map(utils::hash::password) - .transpose() - .map_err(|e| { - err!(Request(InvalidParam("Password does not meet the requirements: {e}"))) - })? - .map_or_else( - || self.db.userid_password.insert(user_id, b""), - |hash| self.db.userid_password.insert(user_id, hash), - ); + pub async fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> { + if self + .db + .userid_origin + .get(user_id) + .await + .deserialized::()? + == "ldap" + { + Err!(Request(InvalidParam("Cannot change password of a LDAP user"))) + } else { + password + .map(utils::hash::password) + .transpose() + .map_err(|e| { + err!(Request(InvalidParam("Password does not meet the requirements: {e}"))) + })? + .map_or_else( + || self.db.userid_password.insert(user_id, b""), + |hash| self.db.userid_password.insert(user_id, hash), + ); - Ok(()) + Ok(()) + } } /// Returns the displayname of a user on this homeserver.