diff --git a/src/admin/admin.rs b/src/admin/admin.rs index 793bf1f85..bb9574a5c 100644 --- a/src/admin/admin.rs +++ b/src/admin/admin.rs @@ -19,47 +19,48 @@ use crate::{ #[derive(Debug, Parser)] #[command(name = conduwuit_core::BRANDING, version = conduwuit_core::version())] pub enum AdminCommand { - #[command(subcommand)] /// Commands for managing appservices + #[command(subcommand)] Appservices(AppserviceCommand), - #[command(subcommand)] /// Commands for managing local users + #[command(subcommand)] Users(UserCommand), - #[command(subcommand)] /// Commands for managing registration tokens + #[command(subcommand)] Token(TokenCommand), + /// Commands for managing OIDC #[command(subcommand)] Oidc(OidcCommand), - #[command(subcommand)] /// Commands for managing rooms + #[command(subcommand)] Rooms(RoomCommand), - #[command(subcommand)] /// Commands for managing federation + #[command(subcommand)] Federation(FederationCommand), - #[command(subcommand)] /// Commands for managing the server + #[command(subcommand)] Server(ServerCommand), - #[command(subcommand)] /// Commands for managing media + #[command(subcommand)] Media(MediaCommand), - #[command(subcommand)] /// Commands for checking integrity + #[command(subcommand)] Check(CheckCommand), - #[command(subcommand)] /// Commands for debugging things + #[command(subcommand)] Debug(DebugCommand), - #[command(subcommand)] /// Low-level queries for database getters and iterators + #[command(subcommand)] Query(QueryCommand), } diff --git a/src/admin/oidc/commands.rs b/src/admin/oidc/commands.rs index c5f9d049d..439d40e2c 100644 --- a/src/admin/oidc/commands.rs +++ b/src/admin/oidc/commands.rs @@ -6,12 +6,20 @@ impl crate::Context<'_> { pub(super) async fn oidc_link(&self, user_id: String, subject: String) -> Result { let user_id = parse_active_local_user_id(self.services, &user_id).await?; - self.services.oidc.link_user(&user_id, &subject).await; + self.services.oidc.link_user(&user_id, &subject); - self.write_str("Account linked successfully").await?; + self.write_str(&format!("Subject `{subject}` linked to account `{user_id}`.")) + .await?; Ok(()) } - pub(super) async fn oidc_unlink(&self, _user_id: String) -> Result { todo!() } + pub(super) async fn oidc_unlink(&self, subject: String) -> Result { + self.services.oidc.unlink_user(&subject); + + self.write_str(&format!("Subject `{subject}` unlinked.")) + .await?; + + Ok(()) + } } diff --git a/src/admin/oidc/mod.rs b/src/admin/oidc/mod.rs index 94baab797..591563919 100644 --- a/src/admin/oidc/mod.rs +++ b/src/admin/oidc/mod.rs @@ -14,8 +14,9 @@ pub enum OidcCommand { subject: String, }, + /// Unlink the given subject claim from its associated user ID. #[clap(name = "unlink")] OidcUnlink { - user_id: String, + subject: String, }, } diff --git a/src/service/oidc/mod.rs b/src/service/oidc/mod.rs index 8b52c74a3..82710d149 100644 --- a/src/service/oidc/mod.rs +++ b/src/service/oidc/mod.rs @@ -263,7 +263,7 @@ impl Service { Self::SERVER_MISCONFIGURED })?; - self.link_user(&user_id, subject).await; + self.link_user(&user_id, subject); info!(?subject, ?user_id, "Shadow user created for {user_id}"); }, @@ -275,9 +275,11 @@ impl Service { Ok(SessionCompletionStatus::Complete(user_id)) } - pub async fn link_user(&self, user_id: &UserId, subject: &str) { + pub fn link_user(&self, user_id: &UserId, subject: &str) { self.db .openidsubject_localpart .insert(subject, user_id.localpart()); } + + pub fn unlink_user(&self, subject: &str) { self.db.openidsubject_localpart.remove(subject); } } diff --git a/src/service/users/account.rs b/src/service/users/account.rs index a3d7e2b16..816ab974b 100644 --- a/src/service/users/account.rs +++ b/src/service/users/account.rs @@ -298,28 +298,7 @@ impl super::Service { &supplied_username, self.services.globals.server_name(), ) { - | Ok(user_id) => { - if let Err(e) = user_id.validate_strict() { - // Unless we are in emergency mode, we should follow synapse's behaviour - // on not allowing things like spaces and UTF-8 characters in - // usernames - if !emergency_mode_enabled { - return Err!(Request(InvalidUsername(debug_warn!( - "Username {supplied_username} contains disallowed characters or \ - spaces: {e}" - )))); - } - } - - // Don't allow registration with user IDs that aren't local - if !self.services.globals.user_is_local(&user_id) { - return Err!(Request(InvalidUsername( - "Username {supplied_username} is not local to this server" - ))); - } - - user_id - }, + | Ok(user_id) => user_id, | Err(e) => { return Err!(Request(InvalidUsername(debug_warn!( "Username {supplied_username} is not valid: {e}" @@ -327,8 +306,27 @@ impl super::Service { }, }; + if let Err(e) = user_id.validate_strict() { + // Unless we are in emergency mode, we should follow synapse's behaviour + // on not allowing things like spaces and UTF-8 characters in + // usernames + if !emergency_mode_enabled { + return Err!(Request(InvalidUsername(debug_warn!( + "Username {supplied_username} contains disallowed characters or spaces: \ + {e}" + )))); + } + } + + // Don't allow registration with user IDs that aren't local + if !self.services.globals.user_is_local(&user_id) { + return Err!(Request(InvalidUsername( + "Username {supplied_username} is not local to this server" + ))); + } + if self.status(&user_id).await.is_found() { - return Err!(Request(UserInUse("User ID is not available."))); + return Err!(Request(UserInUse("Username is not available."))); } // Check that the user ID is/is not in an appservice's namespace