use axum::{ extract::{FromRequest, Form, Json, State}, http::{self, Request, StatusCode}, response::{IntoResponse, Response}, RequestExt, }; use axum_typed_multipart::{TryFromMultipart, TypedMultipart}; use snafu::prelude::*; use tower_cookies::{Cookies, Cookie}; use yunohost_api::{Username, Password}; use crate::{ error::*, state::{COOKIE_NAME, RoutableAppState, sessions::LoggedInUser}, }; #[derive(Debug, TryFromMultipart, Deserialize)] pub struct LoginForm { username: Username, #[allow(dead_code)] password: Password, } #[async_trait] impl FromRequest for LoginForm where Json: FromRequest<(), B>, Form: FromRequest<(), B>, TypedMultipart: FromRequest, B::Data: Into, B::Error: Into + Send + std::error::Error, B: Send + 'static + axum::body::HttpBody, S: Send { type Rejection = Response; async fn from_request(req: Request, _state: &S) -> Result { let headers = req.headers(); if let Some(mime) = headers.get(http::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()) { if mime.starts_with("application/json") { let Json(login_form): Json = req.extract().await.map_err(IntoResponse::into_response)?; return Ok(login_form); } if mime.starts_with("application/x-www-form-urlencoded") { let Form(login_form) = req.extract().await.map_err(IntoResponse::into_response)?; return Ok(login_form); } if mime.starts_with("multipart/form-data") { let TypedMultipart(login_form): TypedMultipart = req.extract().await.map_err(IntoResponse::into_response)?; return Ok(login_form); } Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()) } else { Err("No POST Content-Type".into_response()) } } } #[debug_handler] pub async fn route(user: Option, cookies: Cookies, state: State, form: LoginForm) -> Result { trace!("ROUTE: /login/"); if let Some(username) = user { return Ok(format!("Welcome back, {}! You were already logged in.", username)); } debug!("Performing login attempt for user {}", &form.username); // No cookie, or cookie is invalid. Perform login. if state.check_login(&form.username, &form.password).await.unwrap() { debug!("Login was successful for user {}. Saving cookie now.", &form.username); let session = state.sessions.make_session(COOKIE_NAME, &form.username).await; cookies.add(session.cookie()); Ok(format!("Welcome {}", &form.username)) } else { debug!("Login failed for user {}", &form.username); Ok(format!("Invalid login for {}", &form.username)) } }