Skip to content

Commit

Permalink
Merge pull request #13 from hackerchai/master
Browse files Browse the repository at this point in the history
Feat: implement filtered adapter & bump version
  • Loading branch information
GopherJ authored Apr 18, 2020
2 parents ab594dd + 98334de commit 5bc464f
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 6 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sqlx-adapter"
version = "0.1.2"
version = "0.2.0"
authors = ["Eason Chai <[email protected]>","Cheng JIANG <[email protected]>"]
edition = "2018"
license = "Apache-2.0"
Expand All @@ -9,7 +9,7 @@ homepage="https://github.com/casbin-rs/sqlx-adapter"
readme="README.md"

[dependencies]
casbin = { version = "0.5.1" }
casbin = { version = "0.6.0" }
sqlx = {version = "0.3.4", features = [ "macros" ]}
async-trait = "0.1.30"
dotenv = { version = "0.15.0", default-features = false}
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Based on [Sqlx](https://github.com/launchbadge/sqlx), The current supported data
Add it to `Cargo.toml`

```rust
casbin = { version = "0.5.1" }
sqlx-adapter = { version = "0.1.2", features = ["postgres"] }
casbin = { version = "0.6.0" }
sqlx-adapter = { version = "0.2.0", features = ["postgres"] }
async-std = "1.5.0"
```

Expand Down
14 changes: 14 additions & 0 deletions examples/rbac_with_domains_model.conf
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions examples/rbac_with_domains_policy.csv
Original file line number Diff line number Diff line change
@@ -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
110 changes: 108 additions & 2 deletions src/adapter.rs
Original file line number Diff line number Diff line change
@@ -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 sqlx::pool::Pool;

use crate::{error::*, models::*};
Expand All @@ -8,6 +8,7 @@ use crate::actions as adapter;

pub struct SqlxAdapter {
pool: Pool<adapter::Connection>,
is_filtered: bool,
}

//pub const TABLE_NAME: &str = "casbin_rules";
Expand All @@ -28,7 +29,10 @@ impl<'a> SqlxAdapter {
.await
.map_err(|err| CasbinError::from(AdapterError(Box::new(Error::SqlxError(err)))))?;

adapter::new(&pool).await.map(|_| Self { pool })
adapter::new(&pool).await.map(|_| Self {
pool,
is_filtered: false,
})
}

pub(crate) fn save_policy_line(
Expand Down Expand Up @@ -83,6 +87,39 @@ impl<'a> SqlxAdapter {
None
}

pub(crate) fn load_filtered_policy_line(
&self,
casbin_rule: &CasbinRule,
f: &Filter,
) -> Option<Vec<String>> {
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
}
}
} 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);
}
}
}

None
}

fn normalize_policy(&self, casbin_rule: &CasbinRule) -> Option<Vec<String>> {
let mut result = vec![
&casbin_rule.v0,
Expand Down Expand Up @@ -131,6 +168,29 @@ impl Adapter for SqlxAdapter {
Ok(())
}

async fn load_filtered_policy(&mut self, m: &mut dyn Model, f: Filter) -> Result<()> {

let rules = adapter::load_policy(&self.pool).await?;

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);
}
}
}
} else {
self.is_filtered = true;
}
}

Ok(())
}

async fn save_policy(&mut self, m: &mut dyn Model) -> Result<()> {
let mut rules = vec![];

Expand Down Expand Up @@ -206,6 +266,10 @@ impl Adapter for SqlxAdapter {
Ok(false)
}
}

fn is_filtered(&self) -> bool {
self.is_filtered
}
}

#[cfg(test)]
Expand Down Expand Up @@ -388,5 +452,47 @@ 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();

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"],
};

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());
}
}

0 comments on commit 5bc464f

Please sign in to comment.