Skip to content

Commit

Permalink
feat: add casbin_js_get_permission_for_user (#339)
Browse files Browse the repository at this point in the history
* feat: add casbin_js_get_permission_for_user

* fix: cargo fmt

* fix: pass test

* fix: pass test under linux
  • Loading branch information
MuZhou233 authored Sep 13, 2024
1 parent 368d04a commit f51f24e
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ slog-term = { version = "2.9.0", optional = true }
thiserror = "1.0.30"
tokio = { version = "1.17.0", optional = true, default-features = false }
tokio-stream = { version = "0.1.8", optional = true, default-features = false }
serde_json = "1.0.127"

[features]
default = ["runtime-tokio", "incremental"]
Expand Down
102 changes: 102 additions & 0 deletions src/frontend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::{CoreApi, Enforcer};
use std::collections::HashMap;

pub fn casbin_js_get_permission_for_user(
e: &Enforcer,
_user: &str,
) -> Result<String, Box<dyn std::error::Error>> {
let model = e.get_model();
let mut m = HashMap::new();

m.insert("m", serde_json::Value::from(model.to_text()));

let mut p_rules = Vec::new();
if let Some(assertions) = model.get_model().get("p") {
for (ptype, _assertion) in assertions {
let policies = model.get_policy("p", ptype);
for rules in policies {
let mut rule = vec![ptype.to_string()];
rule.extend(rules);
p_rules.push(rule);
}
}
}
m.insert("p", serde_json::Value::from(p_rules));

let mut g_rules = Vec::new();
if let Some(assertions) = model.get_model().get("g") {
for (ptype, _assertion) in assertions {
let policies = model.get_policy("g", ptype);
for rules in policies {
let mut rule = vec![ptype.to_string()];
rule.extend(rules);
g_rules.push(rule);
}
}
}
m.insert("g", serde_json::Value::from(g_rules));

let result = serde_json::to_string(&m)?;
Ok(result)
}

#[cfg(test)]
mod tests {
use crate::frontend::casbin_js_get_permission_for_user;
use crate::prelude::*;

#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(
all(feature = "runtime-async-std", not(target_arch = "wasm32")),
async_std::test
)]
#[cfg_attr(
all(feature = "runtime-tokio", not(target_arch = "wasm32")),
tokio::test
)]
async fn test_casbin_js_get_permission_for_user() {
use serde_json::Value;
use std::fs;
use std::io::Read;

let model_path = "examples/rbac_model.conf";
let policy_path = "examples/rbac_with_hierarchy_policy.csv";
let e = Enforcer::new(model_path, policy_path).await.unwrap();

let received_string =
casbin_js_get_permission_for_user(&e, "alice").unwrap();
let received: Value = serde_json::from_str(&received_string).unwrap();

let mut expected_model = String::new();
fs::File::open(model_path)
.unwrap()
.read_to_string(&mut expected_model)
.unwrap();
let expected_model_str =
expected_model.replace("\r\n", "\n").replace("\n\n", "\n");

assert_eq!(
received["m"].as_str().unwrap().trim(),
expected_model_str.trim()
);

let mut expected_policies = String::new();
fs::File::open(policy_path)
.unwrap()
.read_to_string(&mut expected_policies)
.unwrap();
let expected_policies_items: Vec<&str> =
expected_policies.split(&[',', '\n'][..]).collect();

let mut i = 0;
for s_arr in received["p"].as_array().unwrap() {
for s in s_arr.as_array().unwrap() {
assert_eq!(
s.as_str().unwrap().trim(),
expected_policies_items[i].trim()
);
i += 1;
}
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod util;
mod watcher;

pub mod error;
pub mod frontend;
pub mod prelude;

#[cfg(not(target_arch = "wasm32"))]
Expand Down
60 changes: 60 additions & 0 deletions src/model/default_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,66 @@ impl Model for DefaultModel {
}
(res, rules_removed)
}

fn to_text(&self) -> String {
let mut token_patterns = HashMap::new();
let p_pattern = regex::Regex::new(r"^p_").unwrap();
let r_pattern = regex::Regex::new(r"^r_").unwrap();

for ptype in ["r", "p"] {
if let Some(assertion) = self.model.get(ptype) {
for token in &assertion[ptype].tokens {
let new_token = p_pattern.replace_all(token, "p.");
let new_token = r_pattern.replace_all(&new_token, "r.");
token_patterns.insert(token.clone(), new_token.to_string());
}
}
}

if let Some(assertions) = self.model.get("e") {
if let Some(assertion) = assertions.get("e") {
if assertion.value.contains("p_eft") {
token_patterns
.insert("p_eft".to_string(), "p.eft".to_string());
}
}
}

let mut s = String::new();

let write_string = |sec: &str, s: &mut String| {
if let Some(assertions) = self.model.get(sec) {
for (_ptype, assertion) in assertions {
let mut value = assertion.value.clone();
for (token_pattern, new_token) in &token_patterns {
value = value.replace(token_pattern, new_token);
}
s.push_str(&format!("{} = {}\n", sec, value));
}
}
};

s.push_str("[request_definition]\n");
write_string("r", &mut s);
s.push_str("[policy_definition]\n");
write_string("p", &mut s);

if self.model.contains_key("g") {
s.push_str("[role_definition]\n");
if let Some(assertions) = self.model.get("g") {
for (ptype, assertion) in assertions {
s.push_str(&format!("{} = {}\n", ptype, assertion.value));
}
}
}

s.push_str("[policy_effect]\n");
write_string("e", &mut s);
s.push_str("[matchers]\n");
write_string("m", &mut s);

s
}
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ pub trait Model: Send + Sync {
field_index: usize,
field_values: Vec<String>,
) -> (bool, Vec<Vec<String>>);
fn to_text(&self) -> String;
}

0 comments on commit f51f24e

Please sign in to comment.