From b6dd6fbd179ee3bdfbbeca3ff60e5b2cd289d950 Mon Sep 17 00:00:00 2001 From: Cheng JIANG Date: Sat, 18 Apr 2020 12:55:23 +0200 Subject: [PATCH 1/3] bump casbin version & implement filtered adapter --- Cargo.toml | 4 +- examples/rbac_policy.csv | 2 +- examples/rbac_with_domains_model.conf | 14 ++++ examples/rbac_with_domains_policy.csv | 6 ++ src/adapter.rs | 107 +++++++++++++++++++++++++- 5 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 examples/rbac_with_domains_model.conf create mode 100644 examples/rbac_with_domains_policy.csv diff --git a/Cargo.toml b/Cargo.toml index 8c34c24..6cdf1be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diesel-adapter" -version = "0.5.0" +version = "0.6.0" authors = ["Cheng JIANG "] edition = "2018" license = "Apache-2.0" @@ -9,7 +9,7 @@ homepage="https://github.com/casbin-rs/diesel-adapter" readme="README.md" [dependencies] -casbin = { version = "0.5.1" } +casbin = { version = "0.6.0" } diesel = { version = "1.4.4", features = ["r2d2"] } async-trait = "0.1.30" diff --git a/examples/rbac_policy.csv b/examples/rbac_policy.csv index f93d6df..8479f3c 100644 --- a/examples/rbac_policy.csv +++ b/examples/rbac_policy.csv @@ -2,4 +2,4 @@ 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 +g, alice, data2_admin diff --git a/examples/rbac_with_domains_model.conf b/examples/rbac_with_domains_model.conf new file mode 100644 index 0000000..57c3721 --- /dev/null +++ b/examples/rbac_with_domains_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, dom, obj, act + +[policy_definition] +p = sub, dom, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/rbac_with_domains_policy.csv b/examples/rbac_with_domains_policy.csv new file mode 100644 index 0000000..a20ccd0 --- /dev/null +++ b/examples/rbac_with_domains_policy.csv @@ -0,0 +1,6 @@ +p, admin,domain1,data1,read +p, admin,domain1,data1,write +p, admin,domain2,data2,read +p, admin,domain2,data2,write +g, alice,admin,domain1 +g, bob,admin,domain2 diff --git a/src/adapter.rs b/src/adapter.rs index 826da00..c191a43 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use casbin::{error::AdapterError, Adapter, Error as CasbinError, Model, Result}; +use casbin::{error::AdapterError, Adapter, Error as CasbinError, Filter, Model, Result}; use diesel::{ self, r2d2::{ConnectionManager, Pool}, @@ -11,6 +11,7 @@ use std::time::Duration; pub struct DieselAdapter { pool: Pool>, + is_filtered: bool, } pub const TABLE_NAME: &str = "casbin_rules"; @@ -27,7 +28,10 @@ impl<'a> DieselAdapter { .get() .map_err(|err| CasbinError::from(AdapterError(Box::new(Error::PoolError(err))))); - adapter::new(conn).map(|_| Self { pool }) + adapter::new(conn).map(|_| Self { + pool, + is_filtered: false, + }) } pub(crate) fn save_policy_line( @@ -82,6 +86,37 @@ impl<'a> DieselAdapter { None } + pub(crate) fn load_filtered_policy_line( + &self, + casbin_rule: &CasbinRule, + f: &Filter, + ) -> Option> { + if let Some(sec) = casbin_rule.ptype.chars().next() { + if let Some(policy) = self.normalize_policy(casbin_rule) { + let mut is_filtered = false; + if sec == 'p' { + for (i, rule) in f.p.iter().enumerate() { + if !rule.is_empty() && rule != &policy[i] { + is_filtered = true + } + } + } + if sec == 'g' { + for (i, rule) in f.g.iter().enumerate() { + if !rule.is_empty() && rule != &policy[i] { + is_filtered = true + } + } + } + if !is_filtered { + return Some(policy); + } + } + } + + None + } + fn normalize_policy(&self, casbin_rule: &CasbinRule) -> Option> { let mut result = vec![ &casbin_rule.v0, @@ -135,6 +170,31 @@ impl Adapter for DieselAdapter { Ok(()) } + async fn load_filtered_policy(&mut self, m: &mut dyn Model, f: Filter) -> Result<()> { + let conn = self + .pool + .get() + .map_err(|err| CasbinError::from(AdapterError(Box::new(Error::PoolError(err)))))?; + + let rules = adapter::load_policy(conn)?; + + for casbin_rule in &rules { + let rule = self.load_filtered_policy_line(casbin_rule, &f); + + if let Some(rule) = rule { + if let Some(ref sec) = casbin_rule.ptype.chars().next().map(|x| x.to_string()) { + if let Some(t1) = m.get_mut_model().get_mut(sec) { + if let Some(t2) = t1.get_mut(&casbin_rule.ptype) { + t2.get_mut_policy().insert(rule); + } + } + } + } + } + + Ok(()) + } + async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> { let conn = self .pool @@ -241,6 +301,10 @@ impl Adapter for DieselAdapter { Ok(false) } } + + fn is_filtered(&self) -> bool { + self.is_filtered + } } #[cfg(test)] @@ -411,5 +475,44 @@ mod tests { ) .await .is_ok()); + + // shadow the previous enforcer + let mut e = Enforcer::new( + "examples/rbac_with_domains_model.conf", + "examples/rbac_with_domains_policy.csv", + ) + .await + .unwrap(); + + let filter = Filter { + p: vec!["", "domain1"], + g: vec!["", "", "domain1"], + }; + + e.load_filtered_policy(filter).await.unwrap(); + assert!(e + .enforce(&["alice", "domain1", "data1", "read"]) + .await + .unwrap()); + assert!(e + .enforce(&["alice", "domain1", "data1", "write"]) + .await + .unwrap()); + assert!(!e + .enforce(&["alice", "domain1", "data2", "read"]) + .await + .unwrap()); + assert!(!e + .enforce(&["alice", "domain1", "data2", "write"]) + .await + .unwrap()); + assert!(!e + .enforce(&["bob", "domain2", "data2", "read"]) + .await + .unwrap()); + assert!(!e + .enforce(&["bob", "domain2", "data2", "write"]) + .await + .unwrap()); } } From 260627093a8ea7989dcf00008bc5546dc304a9c9 Mon Sep 17 00:00:00 2001 From: Cheng JIANG Date: Sat, 18 Apr 2020 13:00:15 +0200 Subject: [PATCH 2/3] change self.is_filtered if returned policy is none --- src/adapter.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/adapter.rs b/src/adapter.rs index c191a43..a058f17 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -189,6 +189,8 @@ impl Adapter for DieselAdapter { } } } + } else { + self.is_filtered = true; } } @@ -484,6 +486,9 @@ mod tests { .await .unwrap(); + assert!(adapter.save_policy(e.get_mut_model()).await.is_ok()); + e.set_adapter(adapter).await.unwrap(); + let filter = Filter { p: vec!["", "domain1"], g: vec!["", "", "domain1"], From c3383791109653ba0a84ac71d8544354fc913532 Mon Sep 17 00:00:00 2001 From: Cheng JIANG Date: Sat, 18 Apr 2020 13:11:09 +0200 Subject: [PATCH 3/3] return None when policy in db isn't p*, g* --- src/adapter.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/adapter.rs b/src/adapter.rs index a058f17..0d38718 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -100,14 +100,16 @@ impl<'a> DieselAdapter { is_filtered = true } } - } - if sec == 'g' { + } else if sec == 'g' { for (i, rule) in f.g.iter().enumerate() { if !rule.is_empty() && rule != &policy[i] { is_filtered = true } } + } else { + return None; } + if !is_filtered { return Some(policy); }