use std::future::{ready, Ready}; use actix_identity::Identity; use actix_web::{dev::Payload, web, Error, FromRequest, HttpRequest, HttpResponse}; use diesel::prelude::*; use diesel::PgConnection; use serde::Deserialize; use crate::errors::ServiceError; use crate::models::{Pool, SlimUser, User}; use crate::utils::verify; #[derive(Debug, Deserialize)] pub struct AuthData { pub email: String, pub password: String, } // we need the same data // simple aliasing makes the intentions clear and its more readable pub type LoggedUser = SlimUser; impl FromRequest for LoggedUser { type Error = Error; type Future = Ready>; fn from_request(req: &HttpRequest, pl: &mut Payload) -> Self::Future { if let Ok(identity) = Identity::from_request(req, pl).into_inner() { if let Some(user_json) = identity.identity() { if let Ok(user) = serde_json::from_str(&user_json) { return ready(Ok(user)); } } } ready(Err(ServiceError::Unauthorized.into())) } } pub async fn logout(id: Identity) -> HttpResponse { id.forget(); HttpResponse::Ok().finish() } pub async fn login( auth_data: web::Json, id: Identity, pool: web::Data, ) -> Result { let user = web::block(move || query(auth_data.into_inner(), pool)).await??; let user_string = serde_json::to_string(&user).unwrap(); id.remember(user_string); Ok(HttpResponse::Ok().finish()) } pub async fn get_me(logged_user: LoggedUser) -> HttpResponse { HttpResponse::Ok().json(logged_user) } /// Diesel query fn query(auth_data: AuthData, pool: web::Data) -> Result { use crate::schema::users::dsl::{email, users}; let conn: &PgConnection = &pool.get().unwrap(); let mut items = users .filter(email.eq(&auth_data.email)) .load::(conn)?; if let Some(user) = items.pop() { if let Ok(matching) = verify(&user.hash, &auth_data.password) { if matching { return Ok(user.into()); } } } Err(ServiceError::Unauthorized) }