#[macro_export]
macro_rules! require_authz {
($namespace:expr, $action:literal, $resource:literal) => {{
$crate::axum::middleware::from_fn(
|path_params: $crate::axum::extract::Path<
::std::collections::HashMap<::std::string::String, ::std::string::String>,
>,
auth_session: $crate::AuthSession,
req: $crate::axum::extract::Request,
next: $crate::axum::middleware::Next| async move {
use ::std::str::FromStr;
use ::std::sync::Arc;
use $crate::axum::response::IntoResponse;
if !auth_session.is_authenticated().await {
return $crate::axum::http::StatusCode::UNAUTHORIZED.into_response();
}
let user_id = match auth_session.user_id().await {
::std::option::Option::Some(id) => id.to_string(),
::std::option::Option::None => {
return $crate::axum::http::StatusCode::UNAUTHORIZED.into_response();
}
};
let store = match req
.extensions()
.get::<Arc<$crate::__authz::PolicyStore>>()
{
::std::option::Option::Some(s) => Arc::clone(s),
::std::option::Option::None => {
$crate::tracing::error!(
macro_name = "require_authz!",
namespace = $namespace,
action = $action,
"missing PolicyStore extension; install Extension(Arc<PolicyStore>) earlier in the middleware chain"
);
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let provider = match req
.extensions()
.get::<Arc<dyn $crate::__authz::RequestEntityProvider>>()
{
::std::option::Option::Some(p) => Arc::clone(p),
::std::option::Option::None => {
$crate::tracing::error!(
macro_name = "require_authz!",
namespace = $namespace,
action = $action,
"missing RequestEntityProvider extension; install Extension(Arc<dyn RequestEntityProvider>) earlier in the middleware chain"
);
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let resource_str = $crate::__macro_support::interpolate_path_params(
$resource, &path_params.0
);
let principal_uid = format!(
"{}::User::\"{}\"", $namespace, user_id
);
let action_uid = format!(
"{}::Action::\"{}\"", $namespace, $action
);
let (resource_type, resource_id) = match resource_str.split_once(':') {
::std::option::Option::Some(parts) => parts,
::std::option::Option::None => {
$crate::tracing::error!(
macro_name = "require_authz!",
resource_template = $resource,
resolved = %resource_str,
"resource must be 'TypeName:id' shape"
);
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let resource_uid = format!(
"{}::{}::\"{}\"", $namespace, resource_type, resource_id
);
let principal = match $crate::__cedar::EntityUid::from_str(&principal_uid) {
::std::result::Result::Ok(u) => u,
::std::result::Result::Err(e) => {
$crate::tracing::error!(uid = %principal_uid, ?e, "invalid principal UID");
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let action_uid_parsed = match $crate::__cedar::EntityUid::from_str(&action_uid) {
::std::result::Result::Ok(u) => u,
::std::result::Result::Err(e) => {
$crate::tracing::error!(uid = %action_uid, ?e, "invalid action UID");
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let resource = match $crate::__cedar::EntityUid::from_str(&resource_uid) {
::std::result::Result::Ok(u) => u,
::std::result::Result::Err(e) => {
$crate::tracing::error!(uid = %resource_uid, ?e, "invalid resource UID");
return $crate::axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
}
};
let entities = match provider
.entities_for(&auth_session, &principal, &resource, &action_uid_parsed)
.await
{
::std::result::Result::Ok(e) => e,
::std::result::Result::Err(e) => {
$crate::tracing::warn!(
?e,
principal = %principal,
resource = %resource,
action = %action_uid_parsed,
"entities_for failed; treating as deny"
);
return $crate::__authz::AuthzDenied.into_response();
}
};
let decision = $crate::__authz::PolicyEvaluator::is_authorized(
&*store,
&entities,
principal,
action_uid_parsed,
resource,
$crate::__cedar::Context::empty(),
);
match decision {
$crate::__authz::AuthzDecision::Allow => next.run(req).await,
$crate::__authz::AuthzDecision::Deny => {
$crate::__authz::AuthzDenied.into_response()
}
}
},
)
}};
}