use rust_webx_core::auth::{IAuthorizationPolicy, IClaims, IDynamicAuthorizer};
use rust_webx_core::error::Result;
use rust_webx_core::http::IHttpContext;
use rust_webx_core::middleware::IMiddleware;
use std::collections::HashMap;
use std::ops::ControlFlow;
use std::sync::Arc;
pub struct ResourceAuthorization {
allowed_roles: HashMap<String, Vec<String>>,
required_permissions: HashMap<String, Vec<String>>,
}
impl ResourceAuthorization {
pub fn new() -> Self {
Self {
allowed_roles: HashMap::new(),
required_permissions: HashMap::new(),
}
}
pub fn allow_role(mut self, resource_key: impl Into<String>, role: impl Into<String>) -> Self {
self.allowed_roles
.entry(resource_key.into())
.or_default()
.push(role.into());
self
}
pub fn allow_permission(
mut self,
resource_key: impl Into<String>,
permission: impl Into<String>,
) -> Self {
self.required_permissions
.entry(resource_key.into())
.or_default()
.push(permission.into());
self
}
}
impl Default for ResourceAuthorization {
fn default() -> Self {
Self::new()
}
}
#[async_trait::async_trait]
impl IAuthorizationPolicy for ResourceAuthorization {
async fn authorize(
&self,
claims: &dyn IClaims,
resource_key: &str,
_method: &str,
) -> Result<()> {
if let Some(allowed) = self.allowed_roles.get(resource_key) {
if allowed.iter().any(|r| claims.roles().contains(r)) {
return Ok(());
}
}
if let Some(required) = self.required_permissions.get(resource_key) {
if required.iter().any(|p| claims.permissions().contains(p)) {
return Ok(());
}
}
Err(rust_webx_core::error::Error::Forbidden(format!(
"no policy grants access to '{} {}'",
_method, resource_key
)))
}
}
struct ResourceAuthMiddleware {
policy: Arc<dyn IAuthorizationPolicy>,
}
#[async_trait::async_trait]
impl IMiddleware for ResourceAuthMiddleware {
async fn invoke(&self, ctx: &mut dyn IHttpContext) -> Result<ControlFlow<()>> {
let route_pattern = match ctx.request().route_pattern() {
Some(p) => p.to_string(),
None => {
return Ok(ControlFlow::Continue(()));
}
};
let method = ctx.request().method().to_string();
match ctx.claims() {
Some(claims) => self.policy.authorize(claims, &route_pattern, &method).await.map(|_| ControlFlow::Continue(())),
None => Err(rust_webx_core::error::Error::Unauthorized(
"no authentication claims found".to_string(),
)),
}
}
}
pub fn resource_auth_middleware(policy: Arc<dyn IAuthorizationPolicy>) -> impl IMiddleware {
ResourceAuthMiddleware { policy }
}
#[derive(Clone)]
pub struct AuthorizerSet {
authorizers: Vec<Arc<dyn IDynamicAuthorizer>>,
}
impl AuthorizerSet {
pub fn new(authorizers: Vec<Arc<dyn IDynamicAuthorizer>>) -> Self {
Self { authorizers }
}
pub async fn authorize(
&self,
claims: &dyn IClaims,
route_pattern: &str,
method: &str,
) -> Result<()> {
for authorizer in &self.authorizers {
authorizer.authorize(claims, route_pattern, method).await?;
}
Ok(())
}
pub fn is_empty(&self) -> bool {
self.authorizers.is_empty()
}
}
pub fn collect_authorizers(
provider: &rust_dix::ServiceProvider,
) -> Option<Arc<AuthorizerSet>> {
let authorizers: Vec<Arc<dyn IDynamicAuthorizer>> = provider.get_all();
if authorizers.is_empty() {
None
} else {
Some(Arc::new(AuthorizerSet::new(authorizers)))
}
}