diff --git a/src/admin/user/commands.rs b/src/admin/user/commands.rs index 81a3755d..f652809f 100644 --- a/src/admin/user/commands.rs +++ b/src/admin/user/commands.rs @@ -1017,3 +1017,29 @@ pub(super) async fn unlock(&self, user_id: String) -> Result { self.write_str(&format!("User {user_id} has been unlocked.")) .await } + +#[admin_command] +pub(super) async fn logout(&self, user_id: String) -> Result { + self.bail_restricted()?; + let user_id = parse_local_user_id(self.services, &user_id)?; + assert!( + self.services.globals.user_is_local(&user_id), + "Parsed user_id must be a local user" + ); + if user_id == self.services.globals.server_user { + return Err!("Not allowed to log out the server service account.",); + } + + if !self.services.users.exists(&user_id).await { + return Err!("User {user_id} does not exist."); + } + if self.services.users.is_admin(&user_id).await { + return Err!("You cannot forcefully log out admin users."); + } + self.services + .users + .all_device_ids(&user_id) + .for_each(|device_id| self.services.users.remove_device(&user_id, device_id)) + .await; + self.write_str(&format!("User {user_id} has been logged out from all devices.")) +} diff --git a/src/admin/user/mod.rs b/src/admin/user/mod.rs index a39a854d..f16aeb8a 100644 --- a/src/admin/user/mod.rs +++ b/src/admin/user/mod.rs @@ -59,6 +59,18 @@ pub enum UserCommand { force: bool, }, + /// - Forcefully log a user out of all of their devices. + /// + /// This will invalidate all access tokens for the specified user, + /// effectively logging them out from all sessions. + /// Note that this is destructive and may result in data loss for the user, + /// such as encryption keys. Use with caution. Can only be used in the admin + /// room. + Logout { + /// Username of the user to log out + user_id: String, + }, + /// - Suspend a user /// /// Suspended users are able to log in, sync, and read messages, but are not