use crate::{to_cookie_page, to_home, AcceptedCookies, Authenticated, Error, State}; use actix_session::Session; use actix_web::{error::BlockingError, web, HttpResponse}; pub type LoginPageArgs = ( Option, Option, web::Data, ); pub type LoginArgs = ( Option, Option, web::Data, Session, web::Form, ); #[derive(Debug, serde::Deserialize)] pub struct LoginForm { username: String, password: String, } #[derive(Debug)] pub struct LoginState { state: State, form: Option, error: Option, } pub fn login_page( (cookies, authenticated, state): LoginPageArgs, ) -> Result { if cookies.is_none() { return Err(to_cookie_page(&state)); } if authenticated.is_some() { return Err(to_home(&state)); } Ok(LoginState::new_empty(&state)) } pub async fn login( (cookies, authenticated, state, session, form): LoginArgs, ) -> Result, Error> { if cookies.is_none() { return Ok(Err(to_cookie_page(&state))); } if authenticated.is_some() { return Ok(Err(to_home(&state))); } let form = form.into_inner(); match try_login(session, &form, &state).await? { Ok(res) => Ok(Err(res)), Err(e) => Ok(Ok(LoginState::new_from_request(&state, form, e))), } } impl LoginState { fn new_empty(state: &State) -> Self { LoginState { state: state.clone(), form: None, error: None, } } fn new_from_request(state: &State, form: LoginForm, error: LoginError) -> Self { LoginState { state: state.clone(), form: Some(form), error: Some(error), } } pub fn register_path(&self) -> String { self.state.pages.register_path() } pub fn login_path(&self) -> String { self.state.pages.login_path() } pub fn home_path(&self) -> String { self.state.pages.home_path() } pub fn username(&self) -> Option { self.form.as_ref().map(|form| form.username.clone()) } pub fn username_error(&self) -> Option { self.error.as_ref().and_then(|e| e.username()) } pub fn password(&self) -> Option { self.form.as_ref().map(|form| form.password.clone()) } } #[derive(Debug)] enum LoginError { Invalid, } impl LoginError { fn username(&self) -> Option { match self { LoginError::Invalid => Some("Username or Password is incorrect".to_owned()), } } } async fn try_login( session: Session, form: &LoginForm, state: &State, ) -> Result, Error> { let res = state .user_store .exec(crate::store::Login { username: form.username.clone(), password: form.password.clone(), }) .await; let user = match res { Ok(user) => user, Err(BlockingError::Error(crate::store::StoreError::AuthenticationFailed)) | Err(BlockingError::Error(crate::store::StoreError::NoUser)) => { return Ok(Err(LoginError::Invalid)) } Err(e) => return Err(e.into()), }; crate::extractors::UserData::set_data(user.id(), &session)?; Ok(Ok(to_home(&state))) }