use std::sync::Arc;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use crate::tenancy::auth::{Operator, User};
use crate::tenancy::{operator_console, tenant_console, OrgResolver as _};
use super::TenantContext;
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(ctx) = parts.extensions.get::<Arc<TenantContext>>().cloned() else {
return Ok(SessionUser(None));
};
let org = match ctx
.resolver
.resolve(parts, &ctx.pools.registry_pool())
.await
{
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(&ctx.session_secret, &org.slug, &cookie_value) {
Ok(p) => p,
Err(_) => return Ok(SessionUser(None)),
};
let mut conn = match ctx.pools.acquire(&org).await {
Ok(c) => c,
Err(_) => return Ok(SessionUser(None)),
};
use crate::core::Column as _;
let users = User::objects()
.where_(User::id.eq(payload.uid))
.fetch_on(&mut **conn)
.await
.unwrap_or_default();
let user = users.into_iter().next().filter(|u| u.active);
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(ctx) = parts.extensions.get::<Arc<TenantContext>>().cloned() 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(&ctx.operator_secret, &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_pool(&ctx.pools.registry_pool())
.await
.unwrap_or_default();
let op = ops.into_iter().next().filter(|o| o.active);
Ok(SessionOperator(op))
}
}
fn extract_cookie<'a>(parts: &'a Parts, name: &str) -> Option<String> {
let header = parts
.headers
.get(axum::http::header::COOKIE)?
.to_str()
.ok()?;
for pair in header.split(';') {
let pair = pair.trim();
if let Some(val) = pair.strip_prefix(name) {
if val.starts_with('=') {
return Some(val[1..].to_owned());
}
}
}
None
}