use actix::{Actor, Handler, Message, SyncContext}; use argonautica::{input::SecretKey, Hasher}; use diesel::MysqlConnection; use diesel::{ dsl::sql, types::{Bigint, Unsigned}, RunQueryDsl, }; use crate::error::ServiceError; use crate::model::{DbExecutor, NewPassword, NewUser}; use crate::schema::passwords::dsl::passwords; use crate::schema::users::dsl::users; static LAST_ID_SQL: &'static str = "SELECT LAST_INSERT_ID()"; pub struct PassHashExecutor(pub Hasher<'static>); impl Actor for PassHashExecutor { type Context = SyncContext; } pub struct PassHashRequest { password: String, secret: String, } impl PassHashRequest { pub fn new(password: String, secret: String) -> Self { Self { password, secret } } } impl Message for PassHashRequest { type Result = Result; } impl Handler for PassHashExecutor { type Result = Result; fn handle(&mut self, req: PassHashRequest, _: &mut Self::Context) -> Self::Result { let secret = SecretKey::from_base64_encoded(req.secret)?; Ok(self .0 .with_password(req.password) .with_secret_key(secret) .hash()?) } } pub struct SaveUserRequest { user: NewUser, pass_hash: String, } impl SaveUserRequest { pub fn new(user: NewUser, pass_hash: String) -> Self { Self { user, pass_hash } } } impl Message for SaveUserRequest { type Result = Result<(), ServiceError>; } impl Handler for DbExecutor { type Result = Result<(), ServiceError>; fn handle(&mut self, msg: SaveUserRequest, _: &mut Self::Context) -> Self::Result { let conn: &MysqlConnection = &self.0.get().unwrap(); diesel::insert_into(users).values(&msg.user).execute(conn)?; let id = sql::>(LAST_ID_SQL).get_result(conn)?; let new_pass = NewPassword { id: id, hash: &msg.pass_hash, }; diesel::insert_into(passwords) .values(&new_pass) .execute(conn)?; Ok(()) } }