Axum authorization middleware based on Casbin

Clone this repo:
  1. 9153c50 feat: bump version to 1.3.0 by Yang Luo · 8 months ago master v1.3.0
  2. ae8c11d feat: do refactoring, simplify middleware & tests implementation (#14) by mcitem · 8 months ago
  3. 64a70e8 feat: bump version to 1.2.0 by Yang Luo · 12 months ago v1.2.0
  4. 3b43c67 feat: Deduplicate enforce check (#13) by Peter Simonsson · 12 months ago
  5. 96c5a05 feat: Update dependencies which require switch to axum-test (#12) by Peter Simonsson · 1 year ago

axum-casbin

Crates.io Docs CI codecov

Casbin access control middleware for axum framework

Requirement

Casbin only takes charge of permission control, so you need to implement an Authentication Middleware to identify user.

You should put axum_casbin::CasbinVals which contains subject(username) and domain(optional) into Extension.

For more details on implementing middleware, please refer to axum::middleware or tower::Service.

For example:

use axum::extract::Request;
use axum_casbin::CasbinVals;
use std::task::{Context, Poll};
use tower::{Layer, Service};

#[derive(Clone)]
pub struct AuthLayer;

impl<S> Layer<S> for AuthLayer {
    type Service = AuthMiddleware<S>;

    fn layer(&self, inner: S) -> Self::Service {
        AuthMiddleware { inner }
    }
}

#[derive(Clone)]
pub struct AuthMiddleware<S> {
    inner: S,
}

impl<S> Service<Request> for AuthMiddleware<S>
where
    S: Service<Request>,
{
    type Error = S::Error;
    type Future = S::Future;
    type Response = S::Response;
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }
    fn call(&mut self, mut req: Request) -> Self::Future {
        req.extensions_mut().insert(CasbinVals {
            subject: String::from("alice"),
            domain: None,
        });
        self.inner.call(req)
    }
}

Example

use axum::routing::{Router, get};
use axum_casbin::CasbinAxumLayer;
use axum_casbin::casbin::function_map::key_match2;
use axum_casbin::casbin::{CoreApi, DefaultModel, FileAdapter};

// Handler that immediately returns an empty `200 OK` response.
async fn handler() {}

#[tokio::main]
async fn main() {
    let m = DefaultModel::from_file("examples/rbac_with_pattern_model.conf")
        .await
        .unwrap();

    let a = FileAdapter::new("examples/rbac_with_pattern_policy.csv");

    let casbin_middleware = CasbinAxumLayer::new(m, a).await.unwrap();

    casbin_middleware
        .write()
        .await
        .get_role_manager()
        .write()
        .matching_fn(Some(key_match2), None);

    let app: Router = Router::new()
        .route("/", get(handler))
        .route("/pen/1", get(handler))
        .route("/pen/2", get(handler))
        .route("/book/{id}", get(handler))
        .layer(casbin_middleware)
        .layer(AuthLayer);
    axum::serve(
        tokio::net::TcpListener::bind("127.0.0.1:3000")
            .await
            .unwrap(),
        app.into_make_service(),
    )
    .await
    .unwrap();
}

License

This project is licensed under