use actix::{Actor, Handler, Message, SyncContext}; use argonautica::{input::SecretKey, Verifier}; use diesel::MysqlConnection; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; use crate::error::ServiceError; use crate::model::DbExecutor; use crate::model::User; use crate::schema::passwords::{columns as password_columns, dsl::passwords}; use crate::schema::users::{columns as user_columns, dsl::users}; pub struct HashVerifyExecutor(pub Verifier<'static>); impl Actor for HashVerifyExecutor { type Context = SyncContext; } pub struct HashVerifyRequest { hash: String, password: String, secret: String, } impl HashVerifyRequest { pub fn new(hash: String, password: String, secret: String) -> Self { Self { hash, password, secret, } } } impl Message for HashVerifyRequest { type Result = Result; } impl Handler for HashVerifyExecutor { type Result = Result; fn handle(&mut self, req: HashVerifyRequest, _: &mut Self::Context) -> Self::Result { let secret = SecretKey::from_base64_encoded(req.secret)?; Ok(self .0 .with_hash(req.hash) .with_password(req.password) .with_secret_key(secret) .verify()?) } } pub struct LoginRequest(pub String); impl Message for LoginRequest { type Result = Result<(User, String), ServiceError>; } impl Handler for DbExecutor { type Result = Result<(User, String), ServiceError>; fn handle(&mut self, login: LoginRequest, _: &mut Self::Context) -> Self::Result { let conn: &MysqlConnection = &self.0.get().unwrap(); let user = users .filter(user_columns::name.eq(login.0)) .first::(conn) .map_err(|_| ServiceError::NotFound)?; let hash = passwords .filter(password_columns::id.eq(user.id)) .select(password_columns::hash) .first::(conn) .map_err(|_| ServiceError::NotFound)?; Ok((user, hash)) } }