#![allow(clippy::exhaustive_structs, reason = "Handlers have auto-generated OpenAPI documentation")]
#![allow(clippy::unused_async, reason = "Handler functions need to be async")]
use super::{
errors::AuthError,
middleware::{Context, Credentials, User, UserProvider},
requests::PostLogin,
state::StateProvider,
utility::{build_uri, extract_uri_query_parts},
};
use crate::app::{
errors::AppError,
state::StateProvider as AppStateProvider,
};
use axum::{
Form,
extract::State,
http::Uri,
response::{Html, Redirect},
};
use rubedo::sugar::s;
use std::sync::Arc;
use tera::Context as Template;
use tracing::{info, warn};
pub async fn get_login<SP: AppStateProvider>(
State(state): State<Arc<SP>>,
mut uri: Uri,
) -> Result<Html<String>, AppError> {
let mut params = extract_uri_query_parts(&uri);
let mut failed = false;
if params.contains_key("failed") {
failed = true;
drop(params.remove("failed"));
}
uri = build_uri(uri.path(), ¶ms).map_err(AuthError::from)?;
let mut template = Template::new();
template.insert("Title", &state.title());
template.insert("PageURL", &uri.path_and_query().map_or_else(|| s!("/"), ToString::to_string));
template.insert("Failed", &failed);
Ok(Html(state.render("login", &template).await?))
}
pub async fn post_login<SP, C, U, UP>(
State(state): State<Arc<SP>>,
mut auth: Context<U>,
Form(login): Form<PostLogin<C>>,
) -> Result<Redirect, AuthError>
where
SP: StateProvider,
C: Credentials,
U: User,
UP: UserProvider<Credentials = C, User = U>,
{
let uri = login.uri.parse::<Uri>()?;
let mut params = extract_uri_query_parts(&uri);
if let Some(ref user) = UP::find_by_credentials(&*state, &login.credentials) {
info!("Logging in user: {}", user.to_loggable_string());
auth.login(user).await?;
} else {
drop(params.insert(s!("failed"), s!("")));
let credentials_string = login.credentials.to_loggable_string();
warn!("Failed login attempt for user: {}", &credentials_string);
}
Ok(Redirect::to(
&build_uri(uri.path(), ¶ms)?
.path_and_query()
.map_or_else(|| s!("/"), ToString::to_string)
))
}
pub async fn get_logout<U: User>(
auth: Context<U>,
) -> Redirect {
if let Some(ref user) = auth.current_user {
info!("Logging out user: {}", user.to_loggable_string());
}
auth.logout().await;
Redirect::to("/")
}