dipper 0.5.3

An out-of-the-box modular dependency injection web application framework.
Documentation
//! An authorization library that supports access control models like ACL, RBAC,
//! ABAC. Home: https://casbin.org/

mod logger;

use std::{
    ops::{Deref, DerefMut},
    sync::Arc,
};

pub use casbin::{self, EnforceArgs, function_map::OperatorFunction, prelude::*};
pub use logger::TraceLogger;
use salvo::prelude::*;
pub use salvo_casbin::{CasbinHoop, CasbinVals};
use sea_orm::DatabaseConnection;
use sea_orm_adapter::SeaOrmAdapter;
use tokio::sync::RwLock;

pub struct Authz<F>(CasbinHoop<CachedEnforcer, F>);

#[handler]
impl<F> Authz<F>
where
    F: Fn(&mut Request, &mut Depot) -> std::result::Result<Option<CasbinVals>, StatusError> + Send + Sync + 'static,
{
    /// Create a recommended enforcer that includes functions such as `tracing`
    /// logger and `mini_moka` cache.
    pub async fn new(db_conn: DatabaseConnection, model_str: &str, get_casbin_vals: F) -> anyhow::Result<Self> {
        let m = DefaultModel::from_str(model_str).await?;
        let a = SeaOrmAdapter::new(db_conn).await?;
        let mut e = CachedEnforcer::new(m, a).await?;
        e.set_logger(Box::new(logger::TraceLogger::default()));
        e.enable_log(true);
        Ok(Self(CasbinHoop::new(e, false, get_casbin_vals)))
    }

    pub fn get_enforcer(&self) -> Arc<RwLock<CachedEnforcer>> {
        self.0.get_enforcer()
    }

    #[inline(always)]
    pub async fn handle(&self, req: &mut Request, depot: &mut Depot, res: &mut Response, ctrl: &mut FlowCtrl) {
        self.0.handle(req, depot, res, ctrl).await
    }
}

impl<F> Deref for Authz<F> {
    type Target = Arc<RwLock<CachedEnforcer>>;

    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}

impl<F> DerefMut for Authz<F> {
    #[inline(always)]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.0.deref_mut()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[tokio::test]
    #[should_panic]
    async fn test_optimal_enforcer() {
        let e = Authz::new(Default::default(), Default::default(), |_req, _depot| {
            Ok(Some(CasbinVals {
                subject: String::from("alice"),
                domain: None,
            }))
        })
        .await
        .unwrap();
        let _ = salvo::Router::with_hoop(e);
    }
}