83 lines
2.9 KiB
Rust
83 lines
2.9 KiB
Rust
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<S, B> FromRequest<S, B> for LoginForm
|
|
where
|
|
Json<LoginForm>: FromRequest<(), B>,
|
|
Form<LoginForm>: FromRequest<(), B>,
|
|
TypedMultipart<LoginForm>: FromRequest<S, B>,
|
|
B::Data: Into<axum::body::Bytes>,
|
|
B::Error: Into<axum::BoxError> + Send + std::error::Error,
|
|
B: Send + 'static + axum::body::HttpBody,
|
|
S: Send
|
|
{
|
|
type Rejection = Response;
|
|
|
|
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
|
|
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<LoginForm> = 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<LoginForm> = 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<LoggedInUser>, cookies: Cookies, state: State<RoutableAppState>, form: LoginForm) -> Result<String, Error> {
|
|
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))
|
|
}
|
|
}
|