mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-07-15 17:59:05 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c940706762 | |||
| 93f30642dd | |||
| 743f6a101b | |||
| 3a4b94005d | |||
| 0a24b0bfec | |||
| dd4d4173d9 |
@@ -0,0 +1 @@
|
|||||||
|
Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex.
|
||||||
@@ -280,7 +280,12 @@ pub(super) async fn unsuspend(&self, user_id: String) -> Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
pub(super) async fn reset_password(&self, username: String, password: Option<String>) -> Result {
|
pub(super) async fn reset_password(
|
||||||
|
&self,
|
||||||
|
logout: bool,
|
||||||
|
username: String,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result {
|
||||||
let user_id = parse_local_user_id(self.services, &username)?;
|
let user_id = parse_local_user_id(self.services, &username)?;
|
||||||
|
|
||||||
if user_id == self.services.globals.server_user {
|
if user_id == self.services.globals.server_user {
|
||||||
@@ -303,7 +308,18 @@ pub(super) async fn reset_password(&self, username: String, password: Option<Str
|
|||||||
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`")
|
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.await
|
.await?;
|
||||||
|
|
||||||
|
if logout {
|
||||||
|
self.services
|
||||||
|
.users
|
||||||
|
.all_device_ids(&user_id)
|
||||||
|
.for_each(|device_id| self.services.users.remove_device(&user_id, device_id))
|
||||||
|
.await;
|
||||||
|
write!(self, "\nAll existing sessions have been logged out.").await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[admin_command]
|
#[admin_command]
|
||||||
@@ -1044,3 +1060,46 @@ pub(super) async fn logout(&self, user_id: String) -> Result {
|
|||||||
self.write_str(&format!("User {user_id} has been logged out from all devices."))
|
self.write_str(&format!("User {user_id} has been logged out from all devices."))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn disable_login(&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 disable login for 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!("Admin users cannot have their login disallowed.");
|
||||||
|
}
|
||||||
|
self.services.users.disable_login(&user_id);
|
||||||
|
|
||||||
|
self.write_str(&format!(
|
||||||
|
"{user_id} can no longer log in. Their existing sessions remain unaffected."
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn enable_login(&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 !self.services.users.exists(&user_id).await {
|
||||||
|
return Err!("User {user_id} does not exist.");
|
||||||
|
}
|
||||||
|
self.services.users.enable_login(&user_id);
|
||||||
|
|
||||||
|
self.write_str(&format!("{user_id} can now log in.")).await
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ pub enum UserCommand {
|
|||||||
|
|
||||||
/// - Reset user password
|
/// - Reset user password
|
||||||
ResetPassword {
|
ResetPassword {
|
||||||
|
/// Log out existing sessions
|
||||||
|
#[arg(short, long)]
|
||||||
|
logout: bool,
|
||||||
/// Username of the user for whom the password should be reset
|
/// Username of the user for whom the password should be reset
|
||||||
username: String,
|
username: String,
|
||||||
/// New password for the user, if unspecified one is generated
|
/// New password for the user, if unspecified one is generated
|
||||||
@@ -113,6 +116,22 @@ pub enum UserCommand {
|
|||||||
user_id: String,
|
user_id: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// - Enable login for a user
|
||||||
|
EnableLogin {
|
||||||
|
/// Username of the user to enable login for
|
||||||
|
user_id: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Disable login for a user
|
||||||
|
///
|
||||||
|
/// Disables login for the specified user without deactivating or locking
|
||||||
|
/// their account. This prevents the user from obtaining new access tokens,
|
||||||
|
/// but does not invalidate existing sessions.
|
||||||
|
DisableLogin {
|
||||||
|
/// Username of the user to disable login for
|
||||||
|
user_id: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// - List local users in the database
|
/// - List local users in the database
|
||||||
#[clap(alias = "list")]
|
#[clap(alias = "list")]
|
||||||
ListUsers,
|
ListUsers,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, debug, err, info,
|
Err, Error, Result, debug, err, info,
|
||||||
utils::{self, ReadyExt, hash},
|
utils::{self, ReadyExt, hash},
|
||||||
|
warn,
|
||||||
};
|
};
|
||||||
use conduwuit_core::{debug_error, debug_warn};
|
use conduwuit_core::{debug_error, debug_warn};
|
||||||
use conduwuit_service::{Services, uiaa::SESSION_ID_LENGTH};
|
use conduwuit_service::{Services, uiaa::SESSION_ID_LENGTH};
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedUserId, UserId,
|
OwnedUserId, UserId,
|
||||||
api::client::{
|
api::client::{
|
||||||
|
error::ErrorKind,
|
||||||
session::{
|
session::{
|
||||||
get_login_token,
|
get_login_token,
|
||||||
get_login_types::{
|
get_login_types::{
|
||||||
@@ -184,6 +186,15 @@ pub(crate) async fn handle_login(
|
|||||||
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if services.users.is_locked(&user_id).await? {
|
||||||
|
return Err(Error::BadRequest(ErrorKind::UserLocked, "This account has been locked."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if services.users.is_login_disabled(&user_id).await {
|
||||||
|
warn!(%user_id, "user attempted to log in with a login-disabled account");
|
||||||
|
return Err!(Request(Forbidden("This account is not permitted to log in.")));
|
||||||
|
}
|
||||||
|
|
||||||
if cfg!(feature = "ldap") && services.config.ldap.enable {
|
if cfg!(feature = "ldap") && services.config.ldap.enable {
|
||||||
match Box::pin(ldap_login(services, &user_id, &lowercased_user_id, password)).await {
|
match Box::pin(ldap_login(services, &user_id, &lowercased_user_id, password)).await {
|
||||||
| Ok(user_id) => Ok(user_id),
|
| Ok(user_id) => Ok(user_id),
|
||||||
|
|||||||
@@ -394,6 +394,10 @@ pub(super) fn open_list(db: &Arc<Engine>, maps: &[Descriptor]) -> Result<Maps> {
|
|||||||
name: "userid_lock",
|
name: "userid_lock",
|
||||||
..descriptor::RANDOM_SMALL
|
..descriptor::RANDOM_SMALL
|
||||||
},
|
},
|
||||||
|
Descriptor {
|
||||||
|
name: "userid_login_disabled",
|
||||||
|
..descriptor::RANDOM_SMALL
|
||||||
|
},
|
||||||
Descriptor {
|
Descriptor {
|
||||||
name: "userid_presenceid",
|
name: "userid_presenceid",
|
||||||
..descriptor::RANDOM_SMALL
|
..descriptor::RANDOM_SMALL
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ struct Data {
|
|||||||
userid_password: Arc<Map>,
|
userid_password: Arc<Map>,
|
||||||
userid_suspension: Arc<Map>,
|
userid_suspension: Arc<Map>,
|
||||||
userid_lock: Arc<Map>,
|
userid_lock: Arc<Map>,
|
||||||
|
userid_logindisabled: Arc<Map>,
|
||||||
userid_selfsigningkeyid: Arc<Map>,
|
userid_selfsigningkeyid: Arc<Map>,
|
||||||
userid_usersigningkeyid: Arc<Map>,
|
userid_usersigningkeyid: Arc<Map>,
|
||||||
useridprofilekey_value: Arc<Map>,
|
useridprofilekey_value: Arc<Map>,
|
||||||
@@ -117,6 +118,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
|||||||
userid_password: args.db["userid_password"].clone(),
|
userid_password: args.db["userid_password"].clone(),
|
||||||
userid_suspension: args.db["userid_suspension"].clone(),
|
userid_suspension: args.db["userid_suspension"].clone(),
|
||||||
userid_lock: args.db["userid_lock"].clone(),
|
userid_lock: args.db["userid_lock"].clone(),
|
||||||
|
userid_logindisabled: args.db["userid_logindisabled"].clone(),
|
||||||
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
|
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
|
||||||
userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(),
|
userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(),
|
||||||
useridprofilekey_value: args.db["useridprofilekey_value"].clone(),
|
useridprofilekey_value: args.db["useridprofilekey_value"].clone(),
|
||||||
@@ -295,6 +297,16 @@ pub async fn is_locked(&self, user_id: &UserId) -> Result<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disable_login(&self, user_id: &UserId) {
|
||||||
|
self.db.userid_logindisabled.insert(user_id, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_login(&self, user_id: &UserId) { self.db.userid_logindisabled.remove(user_id); }
|
||||||
|
|
||||||
|
pub async fn is_login_disabled(&self, user_id: &UserId) -> bool {
|
||||||
|
self.db.userid_logindisabled.contains(user_id).await
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if account is active, infallible
|
/// Check if account is active, infallible
|
||||||
pub async fn is_active(&self, user_id: &UserId) -> bool {
|
pub async fn is_active(&self, user_id: &UserId) -> bool {
|
||||||
!self.is_deactivated(user_id).await.unwrap_or(true)
|
!self.is_deactivated(user_id).await.unwrap_or(true)
|
||||||
|
|||||||
Reference in New Issue
Block a user