refactor: Remove support for guest user registration

This commit is contained in:
Ginger
2026-05-05 09:09:38 -04:00
parent 7436e2f4e1
commit 8c2cf67783
19 changed files with 152 additions and 356 deletions
+1 -4
View File
@@ -121,10 +121,7 @@ impl Service {
.unwrap_or(false)
{
// Reactivate the appservice user if it was accidentally deactivated
self.services
.users
.set_password(&appservice_user_id, None)
.await?;
self.services.users.set_password(&appservice_user_id, None);
}
self.registration_info
+13 -5
View File
@@ -9,7 +9,10 @@ use ruma::{
push::Ruleset,
};
use crate::{Dep, account_data, config, globals, users};
use crate::{
Dep, account_data, config, globals,
users::{self, HashedPassword},
};
pub struct Service {
services: Services,
@@ -51,10 +54,15 @@ impl Service {
async fn set_emergency_access(&self) -> Result {
let server_user = &self.services.globals.server_user;
self.services
.users
.set_password(server_user, self.services.config.emergency_password.as_deref())
.await?;
self.services.users.set_password(
server_user,
self.services
.config
.emergency_password
.as_deref()
.map(HashedPassword::new)
.transpose()?,
);
let (ruleset, pwd_set) = match self.services.config.emergency_password {
| Some(_) => (Ruleset::server_default(server_user), true),
+5 -3
View File
@@ -6,7 +6,10 @@ use conduwuit::{Err, Result, utils};
use data::{Data, ResetTokenInfo};
use ruma::OwnedUserId;
use crate::{Dep, globals, users};
use crate::{
Dep, globals,
users::{self, HashedPassword},
};
pub const PASSWORD_RESET_PATH: &str = "/_continuwuity/account/reset_password";
pub const RESET_TOKEN_QUERY_PARAM: &str = "token";
@@ -100,8 +103,7 @@ impl Service {
self.db.remove_token(&token);
self.services
.users
.set_password(&info.user, Some(new_password))
.await?;
.set_password(&info.user, Some(HashedPassword::new(new_password)?));
}
Ok(())
+2 -2
View File
@@ -238,7 +238,7 @@ pub async fn room_joined_count(&self, room_id: &RoomId) -> Result<u64> {
#[implement(Service)]
#[tracing::instrument(skip(self), level = "debug")]
/// Returns an iterator of all our local users in the room, even if they're
/// deactivated/guests
/// deactivated
pub fn local_users_in_room<'a>(
&'a self,
room_id: &'a RoomId,
@@ -248,7 +248,7 @@ pub fn local_users_in_room<'a>(
}
/// Returns an iterator of all our local joined users in a room who are
/// active (not deactivated, not guest)
/// active (not deactivated)
#[implement(Service)]
#[tracing::instrument(skip(self), level = "trace")]
pub fn active_local_users_in_room<'a>(
+8 -10
View File
@@ -4,7 +4,7 @@ use std::{
sync::Arc,
};
use conduwuit::{Err, Error, Result, error, utils, utils::hash};
use conduwuit::{Err, Error, Result, error, utils};
use lettre::Address;
use ruma::{
UserId,
@@ -377,15 +377,13 @@ impl Service {
));
};
// Check if password is correct
let mut password_verified = false;
// First try local password hash verification
if let Ok(hash) = self.services.users.password_hash(&user_id).await {
password_verified = hash::verify_password(password, &hash).is_ok();
}
if password_verified {
if self
.services
.users
.check_password(&user_id, password)
.await
.is_ok()
{
identity.try_set_localpart(user_id.localpart().to_owned())?;
Ok(AuthType::Password)
+52 -20
View File
@@ -3,7 +3,7 @@ pub(super) mod dehydrated_device;
use std::{collections::BTreeMap, mem, net::IpAddr, sync::Arc};
use conduwuit::{
Err, Error, Result, Server, debug_warn, err, trace,
Err, Error, Result, Server, debug_error, debug_warn, err, trace,
utils::{self, ReadyExt, stream::TryIgnore, string::Unquoted},
};
use database::{Deserialized, Ignore, Interfix, Json, Map};
@@ -38,6 +38,19 @@ pub struct UserSuspension {
pub suspended_by: String,
}
/// A password hash. This is only for use when setting a user's password,
/// if the hash needs to be kept around for a while without keeping the password
/// in memory.
pub struct HashedPassword(String);
impl HashedPassword {
pub fn new(password: &str) -> Result<Self> {
Ok(Self(utils::hash::password(password).map_err(|e| {
err!(Request(InvalidParam("Password does not meet the requirements: {e}")))
})?))
}
}
pub struct Service {
services: Services,
db: Data,
@@ -171,16 +184,23 @@ impl Service {
/// Create a new user account on this homeserver.
#[inline]
pub async fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
pub async fn create(&self, user_id: &UserId, password: Option<HashedPassword>) -> Result<()> {
if !self.services.globals.user_is_local(user_id) && password.is_some() {
return Err!("Cannot create a nonlocal user with a set password");
}
self.set_password(user_id, password).await?;
self.set_password(user_id, password);
Ok(())
}
// /// Create a new account for a local human or bot user.
// pub async fn create_local_account(
// &self,
// username: String,
// password:
// )
/// Deactivate account
pub async fn deactivate_account(&self, user_id: &UserId) -> Result<()> {
// Remove all associated devices
@@ -192,7 +212,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).await?;
self.set_password(user_id, None);
// TODO: Unhook 3PID
Ok(())
@@ -338,25 +358,37 @@ impl Service {
.ready_filter_map(|(u, p): (OwnedUserId, &[u8])| (!p.is_empty()).then_some(u))
}
/// Returns the password hash for the given user.
pub async fn password_hash(&self, user_id: &UserId) -> Result<String> {
self.db.userid_password.get(user_id).await.deserialized()
/// Set a user's password.
pub fn set_password(&self, user_id: &UserId, password: Option<HashedPassword>) {
if let Some(hash) = password {
self.db.userid_password.insert(user_id, hash.0);
} else {
self.db.userid_password.insert(user_id, b"");
}
}
/// Hash and set the user's password to the Argon2 hash
pub async 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),
);
/// Check a user's password.
pub async fn check_password(&self, user_id: &UserId, password: &str) -> Result<OwnedUserId> {
let (hash, user_id): (String, OwnedUserId) =
if let Ok(hash) = self.db.userid_password.get(user_id).await.deserialized() {
(hash, user_id.to_owned())
} else {
// We also check the lowercased version of the user ID to handle legacy user IDs
// better
let lowercase_user_id = UserId::parse(user_id.as_str().to_lowercase()).unwrap();
Ok(())
if let Ok(hash) = self.db.userid_password.get(user_id).await.deserialized() {
(hash, lowercase_user_id)
} else {
return Err!(Request(UserDeactivated("This user is deactivated.")));
}
};
utils::hash::verify_password(password, &hash)
.inspect_err(|e| debug_error!("{e}"))
.map_err(|_| err!(Request(Forbidden("Invalid identifier or password."))))?;
Ok(user_id)
}
/// Returns the displayname of a user on this homeserver.