use super::*;
pub(super) type SessionPrincipalResolver =
Arc<dyn Fn(&crate::context::Extensions) -> SessionPrincipal + Send + Sync + 'static>;
pub(super) enum SessionPrincipal {
Resolved(Option<String>),
Invalid,
}
impl SessionPrincipal {
pub(super) fn into_bound(self) -> Option<Option<String>> {
match self {
Self::Resolved(principal) => Some(principal),
Self::Invalid => None,
}
}
pub(super) fn matches(&self, bound: &Option<String>) -> bool {
match self {
Self::Resolved(principal) => {
crate::async_task::owner_matches(bound, principal.as_deref())
}
Self::Invalid => false,
}
}
}
pub(super) fn default_session_principal_resolver() -> SessionPrincipalResolver {
Arc::new(|extensions| SessionPrincipal::Resolved(crate::router::oauth_task_owner(extensions)))
}
pub(super) fn custom_session_principal_resolver<F>(resolver: F) -> SessionPrincipalResolver
where
F: Fn(&crate::context::Extensions) -> Option<String> + Send + Sync + 'static,
{
Arc::new(move |extensions| {
let resolved =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| resolver(extensions)));
match resolved {
Ok(Some(principal)) if !principal.trim().is_empty() => {
SessionPrincipal::Resolved(Some(principal))
}
Ok(Some(_)) => {
tracing::error!(
target: "mcp::session",
"session principal resolver returned an empty principal; denying the request"
);
SessionPrincipal::Invalid
}
Ok(None) => SessionPrincipal::Resolved(None),
Err(_) => {
tracing::error!(
target: "mcp::session",
"session principal resolver panicked; denying the request"
);
SessionPrincipal::Invalid
}
}
})
}
pub(super) fn resolve_request_principal(
state: &AppState,
http_extensions: &axum::http::Extensions,
) -> SessionPrincipal {
let mut extensions = crate::context::Extensions::new();
#[cfg(feature = "oauth")]
if let Some(claims) = http_extensions.get::<crate::oauth::token::TokenClaims>() {
extensions.insert(claims.clone());
}
crate::transport::extension_bridge::apply_extension_bridges(
&state.extension_bridges,
http_extensions,
&mut extensions,
);
(state.session_principal_resolver)(&extensions)
}