Skip to content

Commit ac1d2fd

Browse files
authored
Merge pull request #30 from GopherJ/filtered-adapter
Filtered adapter
2 parents 1ef3511 + c338379 commit ac1d2fd

5 files changed

+135
-5
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "diesel-adapter"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
authors = ["Cheng JIANG <[email protected]>"]
55
edition = "2018"
66
license = "Apache-2.0"
@@ -9,7 +9,7 @@ homepage="https://github.com/casbin-rs/diesel-adapter"
99
readme="README.md"
1010

1111
[dependencies]
12-
casbin = { version = "0.5.1" }
12+
casbin = { version = "0.6.0" }
1313
diesel = { version = "1.4.4", features = ["r2d2"] }
1414
async-trait = "0.1.30"
1515

examples/rbac_policy.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ p, alice, data1, read
22
p, bob, data2, write
33
p, data2_admin, data2, read
44
p, data2_admin, data2, write
5-
g, alice, data2_admin
5+
g, alice, data2_admin

examples/rbac_with_domains_model.conf

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[request_definition]
2+
r = sub, dom, obj, act
3+
4+
[policy_definition]
5+
p = sub, dom, obj, act
6+
7+
[role_definition]
8+
g = _, _, _
9+
10+
[policy_effect]
11+
e = some(where (p.eft == allow))
12+
13+
[matchers]
14+
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act

examples/rbac_with_domains_policy.csv

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
p, admin,domain1,data1,read
2+
p, admin,domain1,data1,write
3+
p, admin,domain2,data2,read
4+
p, admin,domain2,data2,write
5+
g, alice,admin,domain1
6+
g, bob,admin,domain2

src/adapter.rs

+112-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_trait::async_trait;
2-
use casbin::{error::AdapterError, Adapter, Error as CasbinError, Model, Result};
2+
use casbin::{error::AdapterError, Adapter, Error as CasbinError, Filter, Model, Result};
33
use diesel::{
44
self,
55
r2d2::{ConnectionManager, Pool},
@@ -11,6 +11,7 @@ use std::time::Duration;
1111

1212
pub struct DieselAdapter {
1313
pool: Pool<ConnectionManager<adapter::Connection>>,
14+
is_filtered: bool,
1415
}
1516

1617
pub const TABLE_NAME: &str = "casbin_rules";
@@ -27,7 +28,10 @@ impl<'a> DieselAdapter {
2728
.get()
2829
.map_err(|err| CasbinError::from(AdapterError(Box::new(Error::PoolError(err)))));
2930

30-
adapter::new(conn).map(|_| Self { pool })
31+
adapter::new(conn).map(|_| Self {
32+
pool,
33+
is_filtered: false,
34+
})
3135
}
3236

3337
pub(crate) fn save_policy_line(
@@ -82,6 +86,39 @@ impl<'a> DieselAdapter {
8286
None
8387
}
8488

89+
pub(crate) fn load_filtered_policy_line(
90+
&self,
91+
casbin_rule: &CasbinRule,
92+
f: &Filter,
93+
) -> Option<Vec<String>> {
94+
if let Some(sec) = casbin_rule.ptype.chars().next() {
95+
if let Some(policy) = self.normalize_policy(casbin_rule) {
96+
let mut is_filtered = false;
97+
if sec == 'p' {
98+
for (i, rule) in f.p.iter().enumerate() {
99+
if !rule.is_empty() && rule != &policy[i] {
100+
is_filtered = true
101+
}
102+
}
103+
} else if sec == 'g' {
104+
for (i, rule) in f.g.iter().enumerate() {
105+
if !rule.is_empty() && rule != &policy[i] {
106+
is_filtered = true
107+
}
108+
}
109+
} else {
110+
return None;
111+
}
112+
113+
if !is_filtered {
114+
return Some(policy);
115+
}
116+
}
117+
}
118+
119+
None
120+
}
121+
85122
fn normalize_policy(&self, casbin_rule: &CasbinRule) -> Option<Vec<String>> {
86123
let mut result = vec![
87124
&casbin_rule.v0,
@@ -135,6 +172,33 @@ impl Adapter for DieselAdapter {
135172
Ok(())
136173
}
137174

175+
async fn load_filtered_policy(&mut self, m: &mut dyn Model, f: Filter) -> Result<()> {
176+
let conn = self
177+
.pool
178+
.get()
179+
.map_err(|err| CasbinError::from(AdapterError(Box::new(Error::PoolError(err)))))?;
180+
181+
let rules = adapter::load_policy(conn)?;
182+
183+
for casbin_rule in &rules {
184+
let rule = self.load_filtered_policy_line(casbin_rule, &f);
185+
186+
if let Some(rule) = rule {
187+
if let Some(ref sec) = casbin_rule.ptype.chars().next().map(|x| x.to_string()) {
188+
if let Some(t1) = m.get_mut_model().get_mut(sec) {
189+
if let Some(t2) = t1.get_mut(&casbin_rule.ptype) {
190+
t2.get_mut_policy().insert(rule);
191+
}
192+
}
193+
}
194+
} else {
195+
self.is_filtered = true;
196+
}
197+
}
198+
199+
Ok(())
200+
}
201+
138202
async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
139203
let conn = self
140204
.pool
@@ -241,6 +305,10 @@ impl Adapter for DieselAdapter {
241305
Ok(false)
242306
}
243307
}
308+
309+
fn is_filtered(&self) -> bool {
310+
self.is_filtered
311+
}
244312
}
245313

246314
#[cfg(test)]
@@ -411,5 +479,47 @@ mod tests {
411479
)
412480
.await
413481
.is_ok());
482+
483+
// shadow the previous enforcer
484+
let mut e = Enforcer::new(
485+
"examples/rbac_with_domains_model.conf",
486+
"examples/rbac_with_domains_policy.csv",
487+
)
488+
.await
489+
.unwrap();
490+
491+
assert!(adapter.save_policy(e.get_mut_model()).await.is_ok());
492+
e.set_adapter(adapter).await.unwrap();
493+
494+
let filter = Filter {
495+
p: vec!["", "domain1"],
496+
g: vec!["", "", "domain1"],
497+
};
498+
499+
e.load_filtered_policy(filter).await.unwrap();
500+
assert!(e
501+
.enforce(&["alice", "domain1", "data1", "read"])
502+
.await
503+
.unwrap());
504+
assert!(e
505+
.enforce(&["alice", "domain1", "data1", "write"])
506+
.await
507+
.unwrap());
508+
assert!(!e
509+
.enforce(&["alice", "domain1", "data2", "read"])
510+
.await
511+
.unwrap());
512+
assert!(!e
513+
.enforce(&["alice", "domain1", "data2", "write"])
514+
.await
515+
.unwrap());
516+
assert!(!e
517+
.enforce(&["bob", "domain2", "data2", "read"])
518+
.await
519+
.unwrap());
520+
assert!(!e
521+
.enforce(&["bob", "domain2", "data2", "write"])
522+
.await
523+
.unwrap());
414524
}
415525
}

0 commit comments

Comments
 (0)