pub trait Role: PartialEq {}
pub trait RoleCheck<R: Role> {
fn has_role(&self, role: &R) -> bool;
}
pub trait ScopeCheck {
fn has_scope(&self, scope: &str) -> bool;
}
#[macro_export]
macro_rules! require_role_middleware {
($middleware_name:ident, $claims_type:ty, $role_expr:expr, $has_role_fn:path) => {
pub fn $middleware_name(
ctx: chopin_core::http::Context,
next: chopin_core::router::BoxedHandler,
) -> chopin_core::http::Response {
let token = (0..ctx.req.header_count as usize).find_map(|i| {
let (k, v) = ctx.req.headers[i];
if k.eq_ignore_ascii_case("Authorization") {
v.strip_prefix("Bearer ")
} else {
None
}
});
let Some(token) = token else {
return chopin_core::http::Response::new(401);
};
let Some(manager) = $crate::extractor::GLOBAL_JWT_MANAGER.get() else {
return chopin_core::http::Response::server_error();
};
match manager.decode::<$claims_type>(token) {
Ok(claims) if $has_role_fn(&claims, &$role_expr) => next(ctx),
Ok(_) => chopin_core::http::Response::new(403),
Err(_) => chopin_core::http::Response::new(401),
}
}
};
}
#[macro_export]
macro_rules! require_scope_middleware {
($middleware_name:ident, $claims_type:ty, $scope:expr) => {
pub fn $middleware_name(
ctx: chopin_core::http::Context,
next: chopin_core::router::BoxedHandler,
) -> chopin_core::http::Response {
let token = (0..ctx.req.header_count as usize).find_map(|i| {
let (k, v) = ctx.req.headers[i];
if k.eq_ignore_ascii_case("Authorization") {
v.strip_prefix("Bearer ")
} else {
None
}
});
let Some(token) = token else {
return chopin_core::http::Response::new(401);
};
let Some(manager) = $crate::extractor::GLOBAL_JWT_MANAGER.get() else {
return chopin_core::http::Response::server_error();
};
match manager.decode::<$claims_type>(token) {
Ok(claims) => {
if $crate::ScopeCheck::has_scope(&claims, $scope) {
next(ctx)
} else {
chopin_core::http::Response::new(403)
}
}
Err(_) => chopin_core::http::Response::new(401),
}
}
};
}