Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 24, 2022 05:07 am GMT

A Tutorial on How to Integrate Casbin with Poem Web Services

Introduction

Casbin-rs is an authorization library that supports access control models like ACL, RBAC, ABAC written in Rust.

Poem is a full-featured and easy-to-use web framework with the Rust programming language.

In this tutorial, we will integrate casbin-rs with poem web services using poem-casbin middleware.

GitHub logo casbin-rs / poem-casbin

Casbin Poem access control middleware

Poem Casbin Middleware

Casbin access control middleware for poem framework

Install

Add it to Cargo.toml

poem = "1.3.31"poem-casbin-auth = "0.x.x"tokio = { version = "1.17.0", features = ["rt-multi-thread", "macros"] }

Requirement

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

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

For example:

use poem::{    Endpoint, EndpointExt, Middleware, Request, Result,};use poem_casbin_auth::CasbinVals;pub struct FakeAuth;pub struct FakeAuthMiddleware<E> {    ep: E,}impl<E: Endpoint> Middleware<E> for FakeAuth {    type Output = FakeAuthMiddleware<E>;    fn transform(&self, ep: E)

Write a hello-world service with poem

First, create a cargo crate, then add following dependencies in Cargo.toml:

tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }poem = "1.3.35"

Add following code to main.rs

use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};use std::env;#[handler]fn pen1() -> String {    String::from("I'm pen 1")}#[handler]fn pen2() -> String {    String::from("I'm pen 2")}#[handler]fn book(Path(id): Path<String>) -> String {    format!("I'm book {}", id)}#[tokio::main]async fn main() -> Result<(), std::io::Error> {    if env::var_os("RUST_LOG").is_none() {        env::set_var("RUST_LOG", "poem=debug");    }    let app = Route::new()        .at("/pen/1", get(pen1))        .at("/pen/2", get(pen2))        .at("/book/:id", get(book));    Server::new(TcpListener::bind("127.0.0.1:3000"))        .name("poem-casbin-demo")        .run(app)        .await}

There are 3 endpoints, /pen/1, /pen/2, and /book/:id. Its quite simple, right? Lets run our service, enter cargo run and our service will be available at 127.0.0.1:3000.

Lets use curl to test our service:

Send requests to poem endpoints

Integrate with basic auth middleware

Note that casbin-poem is an authorization middleware, not an authentication middleware. Casbin only takes charge of permission control, so we need to implement anauthentication middlewareto identify user.

In this part, we will integrate a basic auth middleware with our service.

To start with, add following dependency to Cargo.toml:

poem-casbin-auth = { git = "https://github.com/casbin-rs/poem-casbin.git" }

Then create a file named auth.rs and add following code to it:

use poem::{    http::StatusCode,    web::{        headers,        headers::{authorization::Basic, HeaderMapExt},    },    Endpoint, Error, Middleware, Request, Result,};use poem_casbin_auth::CasbinVals;pub struct BasicAuth;impl<E: Endpoint> Middleware<E> for BasicAuth {    type Output = BasicAuthEndpoint<E>;    fn transform(&self, ep: E) -> Self::Output {        BasicAuthEndpoint { ep }    }}pub struct BasicAuthEndpoint<E> {    ep: E,}#[poem::async_trait]impl<E: Endpoint> Endpoint for BasicAuthEndpoint<E> {    type Output = E::Output;    async fn call(&self, mut req: Request) -> Result<Self::Output> {        if let Some(auth) = req.headers().typed_get::<headers::Authorization<Basic>>() {            let vals = CasbinVals {                subject: String::from(auth.username()),                domain: None,            };            req.extensions_mut().insert(vals);            self.ep.call(req).await        } else {            Err(Error::from_status(StatusCode::UNAUTHORIZED))        }    }}

In this mod, we implement a basic auth middleware, for simplicity, here we dont verify username and password, instead we just insert CasbinVals with provided username into Extension, so that poem-casbin middleware can extract identity information. If the request doesnt have basic auth, then the middleware will return 401 Unauthorized.

Then lets integrate this basic auth middleware with our service. Firstly, add following code to main.rs:

mod auth;use poem_casbin_auth::CasbinVals;

Then add a new handler to confirm that our auth middleware insert identity information correctly:

#[handler]fn user(data: Data<&CasbinVals>) -> String {    format!("Hello, {}", &data.subject)}

Lastly, rewrite main function to add an endpoint /user and wrap all endpoints with basic auth middleware, now it looks like:

let app = Route::new()        .at("/pen/1", get(pen1))        .at("/pen/2", get(pen2))        .at("/book/:id", get(book))        .at("/user", get(user))        .with(casbin_middleware)        .with(auth::BasicAuth);

Now, lets use curl again to test our service.

Get 401 Unauthorized response

Now as you can see, if we dont provide basic auth when accessing our service, we will get 401 Unauthorized. Our request is aborted by basic auth middleware. Lets send requests with basic auth:

curl -u alice:123 localhost:3000/book/1

Send requests with basic auth

Now we can get response as normal. It seems that our basic auth middleware works well.

Integrate with poem-casbin middleware

In the last part, we will integrate poem-casbin middleware with our service.

First, we need to provide conf and policy files under the project root directory.

rbac_with_pattern_model.conf looks like:

[request_definition]r = sub, obj, act[policy_definition]p = sub, obj, act[role_definition]g = _, _g2 = _, _[policy_effect]e = some(where (p.eft == allow))[matchers]m = g(r.sub, p.sub) && g2(r.obj, p.obj) && regexMatch(r.act, p.act)

rbac_with_pattern_policy.csv looks like:

p, alice, /pen/1, GETp, book_admin, book_group, GETp, pen_admin, pen_group, GET,,,g, alice, book_admin,g, bob, pen_admin,g2, /book/:id, book_group,g2, /pen/:id, pen_group,

These policy means:

  • For alice:
    • can access /pen/1
    • is book_admin, thus can access /book/:id
  • For bob:
    • is pen_admin, thus can access /pen/:id

Now lets focus on main.rs, first add following code to it:

use poem_casbin_auth::casbin::function_map::key_match2;use poem_casbin_auth::casbin::{CoreApi, DefaultModel, FileAdapter};use poem_casbin_auth::{CasbinService, CasbinVals};

Then rewrite main function to wrap our service with poem-casbin middleware:

let m = DefaultModel::from_file("rbac_with_pattern_model.conf")        .await        .unwrap();let a = FileAdapter::new("rbac_with_pattern_policy.csv");let casbin_middleware = CasbinService::new(m, a).await.unwrap();casbin_middleware        .write()        .await        .get_role_manager()        .write()        .matching_fn(Some(key_match2), None);let app = Route::new()        .at("/pen/1", get(pen1))        .at("/pen/2", get(pen2))        .at("/book/:id", get(book))        .at("/user", get(user))        .with(casbin_middleware)        .with(auth::BasicAuth);

Here we first read conf and policy, then create casbin_middleware and change matching_fn to key_match to match wildcard path (like /:id). Lastly, we wrap all endpoints with casbin_middleware.

Thats all the work we have to do to integrate poem-casbin middleware with our service, quite simple, right?

Again, lets use curl to test our service:

If alice wants to access /pen/2, she will get 403 Forbidden, because she is not allowed to access this endpoint.

Alice can't access /pen/2

Likewise, bob cant access /book/2:

Bob can't access /book/2

Everything is fine when both users send requests to the endpoints that they can access:

Send requests to accessible endpoints

Summary

In this tutorial, we write a hello-world web service using poem, then integrate a basic auth and casbin-poem middleware into it. Its a quite simple project with only ~100 LOC, its code can be found at this repository:

GitHub logo greenhandatsjtu / poem-casbin-demo

Demo to integrate casbin-rs with poem web services using poem-casbin middleware

poem-casbin-demo

Demo to integrate casbin-rs with poem web services using poem-casbin middleware

Introduction

Casbin-rs is an authorization library that supports access control models like ACL, RBAC, ABAC written in Rust.

Poem is a full-featured and easy-to-use web framework with the Rust programming language.

In this tutorial, we will integrate casbin-rs with poem web services using poem-casbin middleware.

Write a hello-world service with poem

First, create a cargo crate, then add following dependencies in Cargo.toml:

tokio = { version = "1.20.0", features = ["rt-multi-thread", "macros"] }poem = "1.3.35"

Add following code to main.rs

use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};use std::env;#[handler]fn pen1() -> String {    String::from("I'm pen 1"

Original Link: https://dev.to/greenhandatsjtu/a-tutorial-on-how-to-integrate-casbin-with-poem-web-services-4hje

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To