From 84b364544bf84ee40412219f02f420eb5d155c81 Mon Sep 17 00:00:00 2001 From: Chojan Shang Date: Tue, 3 Mar 2020 14:13:43 +0800 Subject: [PATCH] Add a basic casbin example for actix-web. (#264) * Add the casbin example for actix-web. * Update for clear description. --- Cargo.toml | 1 + casbin/Cargo.toml | 12 ++++++++ casbin/README.md | 27 +++++++++++++++++ casbin/rbac/rbac_model.conf | 14 +++++++++ casbin/rbac/rbac_policy.csv | 5 ++++ casbin/src/main.rs | 58 +++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 casbin/Cargo.toml create mode 100644 casbin/README.md create mode 100644 casbin/rbac/rbac_model.conf create mode 100644 casbin/rbac/rbac_policy.csv create mode 100644 casbin/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index b428143e..c17b4799 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "async_pg", "awc_https", "basics", + "casbin", "cookie-auth", "cookie-session", "diesel", diff --git a/casbin/Cargo.toml b/casbin/Cargo.toml new file mode 100644 index 00000000..4fe02699 --- /dev/null +++ b/casbin/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "actix-casbin-example" +version = "0.1.0" +authors = ["Chojan Shang "] +edition = "2018" +workspace = ".." + +[dependencies] +actix-web = "2.0" +actix-rt = "1.0" +casbin = "0.3" +loge = {version = "0.4", default-features = false, features = ["colored", "chrono"]} \ No newline at end of file diff --git a/casbin/README.md b/casbin/README.md new file mode 100644 index 00000000..752ef2a9 --- /dev/null +++ b/casbin/README.md @@ -0,0 +1,27 @@ +# Casbin + +Basic integration of [Casbin-RS](https://github.com/casbin/casbin-rs) with [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control) for Actix web. + +## Usage + +```sh +cd examples/casbin +``` + +Modify the files in the `rbac` directory and the code in the `src` directory as required. + +## Running Server + +```sh +cd examples/casbin +cargo run (or ``cargo watch -x run``) + +# Started http server: 127.0.0.1:8080 +``` + +In this example, you can get the the successful result at `http://localhost:8080/success` (accessible) and the failed result at `http://localhost:8080/fail` (inaccessible, `ERR_EMPTY_RESPONSE`). + +## Others + +- Original location of this demo: +- For more related examples of [Casbin-RS](https://github.com/casbin/casbin-rs): diff --git a/casbin/rbac/rbac_model.conf b/casbin/rbac/rbac_model.conf new file mode 100644 index 00000000..71159e38 --- /dev/null +++ b/casbin/rbac/rbac_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/casbin/rbac/rbac_policy.csv b/casbin/rbac/rbac_policy.csv new file mode 100644 index 00000000..f93d6df8 --- /dev/null +++ b/casbin/rbac/rbac_policy.csv @@ -0,0 +1,5 @@ +p, alice, data1, read +p, bob, data2, write +p, data2_admin, data2, read +p, data2_admin, data2, write +g, alice, data2_admin \ No newline at end of file diff --git a/casbin/src/main.rs b/casbin/src/main.rs new file mode 100644 index 00000000..3ae6bfb9 --- /dev/null +++ b/casbin/src/main.rs @@ -0,0 +1,58 @@ +use casbin::{DefaultModel, Enforcer, FileAdapter, RbacApi}; +use std::boxed::Box; +use std::io; +use std::sync::RwLock; + +use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer}; + +/// simple handle +async fn success( + enforcer: web::Data>, + req: HttpRequest, +) -> HttpResponse { + let mut e = enforcer.write().unwrap(); + println!("{:?}", req); + assert_eq!(vec!["data2_admin"], e.get_roles_for_user("alice", None)); + + HttpResponse::Ok().body("Success: alice is data2_admin.") +} + +async fn fail(enforcer: web::Data>, req: HttpRequest) -> HttpResponse { + let mut e = enforcer.write().unwrap(); + println!("{:?}", req); + assert_eq!(vec!["data1_admin"], e.get_roles_for_user("alice", None)); + + HttpResponse::Ok().body("Fail: alice is not data1_admin.") // In fact, it can't be displayed. +} + +#[actix_rt::main] +async fn main() -> io::Result<()> { + std::env::set_var("RUST_LOG", "info"); + std::env::set_var("LOGE_FORMAT", "target"); + + loge::init(); + + let model = DefaultModel::from_file("rbac/rbac_model.conf") + .await + .unwrap(); + let adapter = FileAdapter::new("rbac/rbac_policy.csv"); + + let e = Enforcer::new(Box::new(model), Box::new(adapter)) + .await + .unwrap(); + let e = web::Data::new(RwLock::new(e)); // wrap enforcer into actix-state + + //move is necessary to give closure below ownership of counter + HttpServer::new(move || { + App::new() + .app_data(e.clone()) // <- create app with shared state + // enable logger + .wrap(middleware::Logger::default()) + // register simple handler, handle all methods + .service(web::resource("/success").to(success)) + .service(web::resource("/fail").to(fail)) + }) + .bind("127.0.0.1:8080")? + .run() + .await +}