- Value↔JSON: From impls on domain Value behind `json` feature, delete 4 duplicate converters - ConfigRepository split into ConfigRepository (12), UserRepository (3), WidgetStateCache (2) - polling orchestration moved from bootstrap to application::polling_service - WidgetRenderer in client-domain owns scroll/cache, both clients use it - network loop consolidated into client-application::run_connection_loop - protocol crate drops domain dep, Wire↔Domain conversions move to adapters
80 lines
1.9 KiB
Rust
80 lines
1.9 KiB
Rust
use domain::{AuthPort, PasswordHashPort, User, UserRepository};
|
|
|
|
pub enum AuthError<E> {
|
|
InvalidCredentials,
|
|
RegistrationClosed,
|
|
Repository(E),
|
|
Hash(String),
|
|
}
|
|
|
|
impl<E: std::fmt::Debug> std::fmt::Display for AuthError<E> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::InvalidCredentials => write!(f, "invalid credentials"),
|
|
Self::RegistrationClosed => write!(f, "registration closed (users already exist)"),
|
|
Self::Repository(e) => write!(f, "repository error: {e:?}"),
|
|
Self::Hash(e) => write!(f, "hash error: {e}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn login<C, A, H>(
|
|
config: &C,
|
|
auth: &A,
|
|
hasher: &H,
|
|
username: &str,
|
|
password: &str,
|
|
) -> Result<String, AuthError<C::Error>>
|
|
where
|
|
C: UserRepository,
|
|
A: AuthPort,
|
|
H: PasswordHashPort,
|
|
{
|
|
let user = config
|
|
.get_user_by_username(username)
|
|
.await
|
|
.map_err(AuthError::Repository)?
|
|
.ok_or(AuthError::InvalidCredentials)?;
|
|
|
|
let valid = hasher
|
|
.verify(password, &user.password_hash)
|
|
.await
|
|
.map_err(AuthError::Hash)?;
|
|
|
|
if !valid {
|
|
return Err(AuthError::InvalidCredentials);
|
|
}
|
|
|
|
Ok(auth.generate_token(user.id))
|
|
}
|
|
|
|
pub async fn register<C, H>(
|
|
config: &C,
|
|
hasher: &H,
|
|
username: &str,
|
|
password: &str,
|
|
) -> Result<(), AuthError<C::Error>>
|
|
where
|
|
C: UserRepository,
|
|
H: PasswordHashPort,
|
|
{
|
|
let count = config.count_users().await.map_err(AuthError::Repository)?;
|
|
if count > 0 {
|
|
return Err(AuthError::RegistrationClosed);
|
|
}
|
|
|
|
let hash = hasher.hash(password).await.map_err(AuthError::Hash)?;
|
|
|
|
let user = User {
|
|
id: 0,
|
|
username: username.to_string(),
|
|
password_hash: hash,
|
|
};
|
|
|
|
config
|
|
.save_user(&user)
|
|
.await
|
|
.map_err(AuthError::Repository)?;
|
|
Ok(())
|
|
}
|