use crate::{
Error, User, UserId,
error::AuthError,
repositories::{PasswordRepository, UserRepository},
services::UserService,
};
use std::sync::Arc;
pub struct PasswordService<U: UserRepository, P: PasswordRepository> {
user_service: Arc<UserService<U>>,
password_repository: Arc<P>,
}
impl<U: UserRepository, P: PasswordRepository> PasswordService<U, P> {
pub fn new(user_repository: Arc<U>, password_repository: Arc<P>) -> Self {
let user_service = Arc::new(UserService::new(user_repository));
Self {
user_service,
password_repository,
}
}
pub async fn register_user(
&self,
email: &str,
password: &str,
name: Option<String>,
) -> Result<User, Error> {
if (self.user_service.get_user_by_email(email).await?).is_some() {
return Err(Error::Auth(AuthError::UserAlreadyExists));
}
let password_hash = Self::hash_password(password)?;
let user = self.user_service.create_user(email, name).await?;
self.password_repository
.set_password_hash(&user.id, &password_hash)
.await?;
Ok(user)
}
pub async fn authenticate(&self, email: &str, password: &str) -> Result<User, Error> {
let user = self
.user_service
.get_user_by_email(email)
.await?
.ok_or(Error::Auth(AuthError::InvalidCredentials))?;
let password_hash = self
.password_repository
.get_password_hash(&user.id)
.await?
.ok_or(Error::Auth(AuthError::InvalidCredentials))?;
if !Self::verify_password(password, &password_hash)? {
return Err(Error::Auth(AuthError::InvalidCredentials));
}
Ok(user)
}
pub async fn change_password(
&self,
user_id: &UserId,
old_password: &str,
new_password: &str,
) -> Result<(), Error> {
let current_hash = self
.password_repository
.get_password_hash(user_id)
.await?
.ok_or(Error::Auth(AuthError::InvalidCredentials))?;
if !Self::verify_password(old_password, ¤t_hash)? {
return Err(Error::Auth(AuthError::InvalidCredentials));
}
let new_hash = Self::hash_password(new_password)?;
self.password_repository
.set_password_hash(user_id, &new_hash)
.await?;
Ok(())
}
pub async fn set_password(&self, user_id: &UserId, password: &str) -> Result<(), Error> {
let password_hash = Self::hash_password(password)?;
self.password_repository
.set_password_hash(user_id, &password_hash)
.await
}
pub async fn remove_password(&self, user_id: &UserId) -> Result<(), Error> {
self.password_repository.remove_password_hash(user_id).await
}
fn hash_password(password: &str) -> Result<String, Error> {
use password_auth::generate_hash;
Ok(generate_hash(password))
}
fn verify_password(password: &str, hash: &str) -> Result<bool, Error> {
use password_auth::verify_password;
Ok(verify_password(password, hash).is_ok())
}
}