refactor: HttpSession/OptionalHttpSession axum extractors
This commit is contained in:
parent
e28c584b26
commit
44c083a8ef
7 changed files with 111 additions and 81 deletions
|
|
@ -1,23 +1,17 @@
|
||||||
use axum::extract::{Form, Path, State};
|
use axum::extract::{Form, Path, State};
|
||||||
use axum::response::{Html, IntoResponse, Redirect, Response};
|
use axum::response::{Html, IntoResponse, Redirect, Response};
|
||||||
use axum_extra::extract::cookie::CookieJar;
|
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use minijinja::context;
|
use minijinja::context;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::db::{DatabaseInterface, Operation};
|
use crate::db::{DatabaseInterface, Operation};
|
||||||
use crate::http::HttpState;
|
use crate::http::{HttpSession, HttpState};
|
||||||
use crate::http::login::login_page;
|
|
||||||
|
|
||||||
pub async fn get_domain<D: DatabaseInterface>(
|
pub async fn get_domain<D: DatabaseInterface>(
|
||||||
State(state): State<HttpState<D>>,
|
State(state): State<HttpState<D>>,
|
||||||
cookies: CookieJar,
|
session: HttpSession,
|
||||||
Path(domain): Path<String>,
|
Path(domain): Path<String>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let Some(session) = state.sessions.get_session(&cookies) else {
|
|
||||||
return login_page(State(state), None).await.into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let domain = match state.db.get_domain(&domain).await {
|
let domain = match state.db.get_domain(&domain).await {
|
||||||
Ok(Some(domain)) => domain,
|
Ok(Some(domain)) => domain,
|
||||||
Ok(None) => return format!("Domain not found: {domain}").into_response(),
|
Ok(None) => return format!("Domain not found: {domain}").into_response(),
|
||||||
|
|
@ -61,13 +55,9 @@ pub struct DomainCreationForm {
|
||||||
|
|
||||||
pub async fn create_domain<D: DatabaseInterface>(
|
pub async fn create_domain<D: DatabaseInterface>(
|
||||||
State(mut state): State<HttpState<D>>,
|
State(mut state): State<HttpState<D>>,
|
||||||
cookies: CookieJar,
|
session: HttpSession,
|
||||||
Form(form): Form<DomainCreationForm>,
|
Form(form): Form<DomainCreationForm>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let Some(session) = state.sessions.get_session(&cookies) else {
|
|
||||||
return login_page(State(state), None).await.into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
let op = Operation::CreateDomain;
|
let op = Operation::CreateDomain;
|
||||||
if !session.user.can_perform(&op) {
|
if !session.user.can_perform(&op) {
|
||||||
return "Not authorized to create a new domain".into_response();
|
return "Not authorized to create a new domain".into_response();
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::{Html, IntoResponse, Response};
|
use axum::response::{Html, IntoResponse, Response};
|
||||||
use axum_extra::extract::cookie::CookieJar;
|
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use minijinja::context;
|
use minijinja::context;
|
||||||
|
|
||||||
use crate::db::{DatabaseInterface, Operation};
|
use crate::db::{DatabaseInterface, Operation};
|
||||||
use crate::http::HttpState;
|
use crate::http::{HttpSession, HttpState};
|
||||||
use crate::http::login::login_page;
|
|
||||||
|
|
||||||
pub async fn home<D: DatabaseInterface>(
|
pub async fn home<D: DatabaseInterface>(
|
||||||
State(state): State<HttpState<D>>,
|
State(state): State<HttpState<D>>,
|
||||||
cookies: CookieJar,
|
// Only logged in users are allowed here
|
||||||
|
session: HttpSession,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
if let Some(session) = state.sessions.get_session(&cookies) {
|
|
||||||
// When the user has no domain (service admin) list all domains
|
// When the user has no domain (service admin) list all domains
|
||||||
let op = Operation::ListUsers(session.user.domain.clone());
|
let op = Operation::ListUsers(session.user.domain.clone());
|
||||||
let other_users = if session.user.can_perform(&op) {
|
let other_users = if session.user.can_perform(&op) {
|
||||||
|
|
@ -57,7 +55,4 @@ pub async fn home<D: DatabaseInterface>(
|
||||||
.render(ctx)
|
.render(ctx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
(StatusCode::OK, Html(page)).into_response()
|
(StatusCode::OK, Html(page)).into_response()
|
||||||
} else {
|
|
||||||
login_page(State(state), None).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use minijinja::context;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::db::{DatabaseInterface, UserRef};
|
use crate::db::{DatabaseInterface, UserRef};
|
||||||
use crate::http::HttpState;
|
use crate::http::{HttpState, OptionalHttpSession};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct LoginForm {
|
pub struct LoginForm {
|
||||||
|
|
@ -33,12 +33,24 @@ pub async fn login_page<D: DatabaseInterface>(
|
||||||
(StatusCode::OK, Html(page)).into_response()
|
(StatusCode::OK, Html(page)).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_login<D: DatabaseInterface>(
|
||||||
|
State(state): State<HttpState<D>>,
|
||||||
|
maybe_session: Option<OptionalHttpSession>,
|
||||||
|
) -> Response {
|
||||||
|
if maybe_session.is_some() {
|
||||||
|
return Redirect::to("/").into_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
login_page(State(state), None).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn post_login<D: DatabaseInterface>(
|
pub async fn post_login<D: DatabaseInterface>(
|
||||||
State(state): State<HttpState<D>>,
|
State(state): State<HttpState<D>>,
|
||||||
|
session: Option<OptionalHttpSession>,
|
||||||
cookies: CookieJar,
|
cookies: CookieJar,
|
||||||
Form(form): Form<LoginForm>,
|
Form(form): Form<LoginForm>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
if let Some(_session) = state.sessions.get_session(&cookies) {
|
if session.is_some() {
|
||||||
// Already logged in
|
// Already logged in
|
||||||
return (cookies, Redirect::to("/")).into_response();
|
return (cookies, Redirect::to("/")).into_response();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ use axum::response::{IntoResponse, Redirect, Response};
|
||||||
use axum_extra::extract::cookie::CookieJar;
|
use axum_extra::extract::cookie::CookieJar;
|
||||||
|
|
||||||
use crate::db::DatabaseInterface;
|
use crate::db::DatabaseInterface;
|
||||||
use crate::http::HttpState;
|
|
||||||
use crate::http::login::{LoginError, login_page};
|
use crate::http::login::{LoginError, login_page};
|
||||||
|
use crate::http::{HttpState, OptionalHttpSession};
|
||||||
|
|
||||||
pub async fn logout<D: DatabaseInterface>(
|
pub async fn logout<D: DatabaseInterface>(
|
||||||
State(state): State<HttpState<D>>,
|
State(state): State<HttpState<D>>,
|
||||||
|
session: Option<OptionalHttpSession>,
|
||||||
cookies: CookieJar,
|
cookies: CookieJar,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let Some(session) = state.sessions.get_session(&cookies) else {
|
let Some(session) = session else {
|
||||||
return (cookies, Redirect::to("/")).into_response();
|
return (cookies, Redirect::to("/")).into_response();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ mod home;
|
||||||
mod login;
|
mod login;
|
||||||
mod logout;
|
mod logout;
|
||||||
mod session;
|
mod session;
|
||||||
use session::HttpSessionManager;
|
use session::{HttpSession, HttpSessionManager, OptionalHttpSession};
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
impl AxumListener for Listener {
|
impl AxumListener for Listener {
|
||||||
|
|
@ -98,7 +98,7 @@ pub async fn http_listen<D: DatabaseInterface>(listener: Listener, db: Database<
|
||||||
let app = { Router::new().nest_service("/assets", ServeDir::new("assets")) };
|
let app = { Router::new().nest_service("/assets", ServeDir::new("assets")) };
|
||||||
let app = app
|
let app = app
|
||||||
.route("/", get(home::home))
|
.route("/", get(home::home))
|
||||||
.route("/login", get(home::home))
|
.route("/login", get(login::get_login))
|
||||||
.route("/login", post(login::post_login))
|
.route("/login", post(login::post_login))
|
||||||
.route("/logout", get(logout::logout))
|
.route("/logout", get(logout::logout))
|
||||||
.route("/domain/{domain}", get(domain::get_domain))
|
.route("/domain/{domain}", get(domain::get_domain))
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
|
use axum::extract::{FromRequestParts, OptionalFromRequestParts};
|
||||||
|
use axum::response::Redirect;
|
||||||
use axum_extra::extract::cookie::{Cookie, CookieJar};
|
use axum_extra::extract::cookie::{Cookie, CookieJar};
|
||||||
|
use http::request::Parts;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::db::User;
|
use crate::db::{DatabaseInterface, User};
|
||||||
|
use crate::http::HttpState;
|
||||||
|
|
||||||
pub const COOKIE_NAME: &str = "lldap_session";
|
pub const COOKIE_NAME: &str = "lldap_session";
|
||||||
|
|
||||||
|
|
@ -78,3 +82,43 @@ impl PartialEq<HttpSession> for HttpSession {
|
||||||
self.uuid == other.uuid
|
self.uuid == other.uuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D: DatabaseInterface> FromRequestParts<HttpState<D>> for HttpSession {
|
||||||
|
type Rejection = Redirect;
|
||||||
|
|
||||||
|
async fn from_request_parts(
|
||||||
|
parts: &mut Parts,
|
||||||
|
state: &HttpState<D>,
|
||||||
|
) -> Result<Self, Self::Rejection> {
|
||||||
|
let cookies = CookieJar::from_request_parts(parts, state).await.unwrap();
|
||||||
|
state
|
||||||
|
.sessions
|
||||||
|
.get_session(&cookies)
|
||||||
|
.ok_or(Redirect::to("/login"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OptionalHttpSession(pub HttpSession);
|
||||||
|
|
||||||
|
impl std::ops::Deref for OptionalHttpSession {
|
||||||
|
type Target = HttpSession;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: DatabaseInterface> OptionalFromRequestParts<HttpState<D>> for OptionalHttpSession {
|
||||||
|
type Rejection = Redirect;
|
||||||
|
|
||||||
|
async fn from_request_parts(
|
||||||
|
parts: &mut Parts,
|
||||||
|
state: &HttpState<D>,
|
||||||
|
) -> Result<Option<Self>, Self::Rejection> {
|
||||||
|
let maybe_session = HttpSession::from_request_parts(parts, state)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.map(Self);
|
||||||
|
Ok(maybe_session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use axum::extract::{Form, State};
|
use axum::extract::{Form, State};
|
||||||
use axum::response::{IntoResponse, Redirect, Response};
|
use axum::response::{IntoResponse, Redirect, Response};
|
||||||
use axum_extra::extract::cookie::CookieJar;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::db::{DatabaseInterface, Role, User};
|
use crate::db::{DatabaseInterface, Role, User};
|
||||||
use crate::http::HttpState;
|
use crate::http::{HttpSession, HttpState};
|
||||||
use crate::http::login::login_page;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct UserCreationForm {
|
pub struct UserCreationForm {
|
||||||
|
|
@ -16,19 +14,9 @@ pub struct UserCreationForm {
|
||||||
|
|
||||||
pub async fn create_user<D: DatabaseInterface>(
|
pub async fn create_user<D: DatabaseInterface>(
|
||||||
State(mut state): State<HttpState<D>>,
|
State(mut state): State<HttpState<D>>,
|
||||||
cookies: CookieJar,
|
session: HttpSession,
|
||||||
Form(form): Form<UserCreationForm>,
|
Form(form): Form<UserCreationForm>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let Some(session) = state.sessions.get_session(&cookies) else {
|
|
||||||
return login_page(State(state), None).await.into_response();
|
|
||||||
};
|
|
||||||
|
|
||||||
// let domain = form.domain;
|
|
||||||
// let op = Operation::CreateUser(domain.clone());
|
|
||||||
// if !session.user.can_perform(&op) {
|
|
||||||
// return format!("Not authorized to create a new user on domain {domain}").into_response()
|
|
||||||
// }
|
|
||||||
|
|
||||||
let new_user = User {
|
let new_user = User {
|
||||||
mail: format!("{}@{}", form.username, form.domain),
|
mail: format!("{}@{}", form.username, form.domain),
|
||||||
username: form.username.clone(),
|
username: form.username.clone(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue