use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use crate::tenancy::auth::{Operator, User};
use crate::tenancy::middleware::{request_org, request_pool, session_keys};
use crate::tenancy::{operator_console, tenant_console};
pub struct SessionUser(pub Option<User>);
impl<S: Send + Sync> FromRequestParts<S> for SessionUser {
type Rejection = std::convert::Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let Some(keys) = session_keys(&parts.extensions) else {
return Ok(SessionUser(None));
};
let org = match request_org(parts, &parts.extensions).await {
Some(Ok(Some(o))) => o,
_ => return Ok(SessionUser(None)),
};
let cookie_value = match extract_cookie(parts, tenant_console::COOKIE_NAME) {
Some(v) => v,
None => return Ok(SessionUser(None)),
};
let payload = match tenant_console::decode(keys.session, &org.slug, &cookie_value) {
Ok(p) => p,
Err(_) => return Ok(SessionUser(None)),
};
let pool = match request_pool(&parts.extensions, &org).await {
Some(Ok(p)) => p,
_ => return Ok(SessionUser(None)),
};
use crate::core::Column as _;
use crate::sql::FetcherPool as _;
let users = User::objects()
.where_(User::id.eq(payload.uid))
.fetch(&pool)
.await
.unwrap_or_default();
let user = users.into_iter().next().filter(|u| u.active);
let user = user.filter(|u| {
crate::tenancy::session::survives_password_change(
keys.session,
&payload.pwf,
payload.iat,
&u.password_hash,
u.password_changed_at,
)
});
Ok(SessionUser(user))
}
}
pub struct SessionOperator(pub Option<Operator>);
impl<S: Send + Sync> FromRequestParts<S> for SessionOperator {
type Rejection = std::convert::Infallible;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
let Some(keys) = session_keys(&parts.extensions) else {
return Ok(SessionOperator(None));
};
let cookie_value = match extract_cookie(parts, operator_console::session::COOKIE_NAME) {
Some(v) => v,
None => return Ok(SessionOperator(None)),
};
let payload = match operator_console::session::decode(keys.operator, &cookie_value) {
Ok(p) => p,
Err(_) => return Ok(SessionOperator(None)),
};
use crate::core::Column as _;
use crate::sql::FetcherPool as _;
let ops = Operator::objects()
.where_(Operator::id.eq(payload.oid))
.fetch(&keys.registry)
.await
.unwrap_or_default();
let op = ops.into_iter().next().filter(|o| {
o.active
&& crate::tenancy::session::survives_password_change(
keys.operator,
&payload.pwf,
payload.iat,
&o.password_hash,
o.password_changed_at,
)
});
Ok(SessionOperator(op))
}
}
fn extract_cookie(parts: &Parts, name: &str) -> Option<String> {
crate::cookies::cookie_from_headers(&parts.headers, name).map(str::to_owned)
}