use axum08::extract::{FromRef, FromRequestParts};
use axum08::http::request::Parts;
use crate::Auth;
use crate::core::Error;
use crate::store::{Session, User};
use crate::types::AuthError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SignedIn {
pub user: User,
pub session: Session,
}
impl SignedIn {
async fn load(parts: &mut Parts, auth: &Auth) -> Result<Option<Self>, Error> {
if let Some(signed_in) = parts.extensions.get::<SignedIn>() {
return Ok(Some(signed_in.clone()));
}
let Some(id) = auth.session_id(&parts.headers) else {
return Ok(None);
};
let Some((user, session)) = auth.session(&id).await? else {
return Ok(None);
};
let signed_in = SignedIn { user, session };
parts.extensions.insert(signed_in.clone());
Ok(Some(signed_in))
}
}
impl<S> FromRequestParts<S> for SignedIn
where
Auth: FromRef<S>,
S: Send + Sync,
{
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let auth = Auth::from_ref(state);
match Self::load(parts, &auth).await? {
Some(signed_in) => Ok(signed_in),
None => Err(AuthError::Unauthorized.into()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Admin(pub SignedIn);
impl<S> FromRequestParts<S> for Admin
where
Auth: FromRef<S>,
S: Send + Sync,
{
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let signed_in = SignedIn::from_request_parts(parts, state).await?;
if !signed_in.user.is_admin() {
return Err(AuthError::Forbidden.into());
}
Ok(Admin(signed_in))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaybeSignedIn(pub Option<SignedIn>);
impl<S> FromRequestParts<S> for MaybeSignedIn
where
Auth: FromRef<S>,
S: Send + Sync,
{
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let auth = Auth::from_ref(state);
Ok(MaybeSignedIn(SignedIn::load(parts, &auth).await?))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LiveSession(pub String);
impl<S> FromRequestParts<S> for LiveSession
where
Auth: FromRef<S>,
S: Send + Sync,
{
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
let auth = Auth::from_ref(state);
let Some(id) = auth.session_id(&parts.headers) else {
return Err(AuthError::Unauthorized.into());
};
if !auth.session_live(&id).await? {
return Err(AuthError::Unauthorized.into());
}
Ok(LiveSession(id))
}
}