use crate::{
config::Config,
error::ModelError,
model::{Assertion, AssertionMap, Model},
rbac::RoleManager,
util::*,
Result,
};
#[cfg(feature = "incremental")]
use crate::emitter::EventData;
use hashlink::{LinkedHashMap, LinkedHashSet};
use parking_lot::RwLock;
#[cfg(all(feature = "runtime-async-std", not(target_arch = "wasm32")))]
use async_std::path::Path as ioPath;
#[cfg(feature = "runtime-tokio")]
use std::path::Path as ioPath;
use std::{collections::HashMap, sync::Arc};
#[derive(Clone, Default)]
pub struct DefaultModel {
pub(crate) model: HashMap<String, AssertionMap>,
}
impl DefaultModel {
#[cfg(not(target_arch = "wasm32"))]
pub async fn from_file<P: AsRef<ioPath>>(p: P) -> Result<DefaultModel> {
let cfg = Config::from_file(p).await?;
let mut model = DefaultModel::default();
model.load_section(&cfg, "r")?;
model.load_section(&cfg, "p")?;
model.load_section(&cfg, "e")?;
model.load_section(&cfg, "m")?;
model.load_section(&cfg, "g")?;
Ok(model)
}
#[allow(clippy::should_implement_trait)]
pub async fn from_str(s: &str) -> Result<DefaultModel> {
let cfg = Config::from_str(s).await?;
let mut model = DefaultModel::default();
model.load_section(&cfg, "r")?;
model.load_section(&cfg, "p")?;
model.load_section(&cfg, "e")?;
model.load_section(&cfg, "m")?;
model.load_section(&cfg, "g")?;
Ok(model)
}
fn load_section(&mut self, cfg: &Config, sec: &str) -> Result<()> {
let mut i = 1;
loop {
if !self.load_assertion(
cfg,
sec,
&format!("{}{}", sec, self.get_key_suffix(i)),
)? {
break Ok(());
} else {
i += 1;
}
}
}
fn load_assertion(
&mut self,
cfg: &Config,
sec: &str,
key: &str,
) -> Result<bool> {
let sec_name = match sec {
"r" => "request_definition",
"p" => "policy_definition",
"g" => "role_definition",
"e" => "policy_effect",
"m" => "matchers",
_ => {
return Err(ModelError::Other(format!(
"Unknown section: `{}`",
sec
))
.into());
}
};
if let Some(val) = cfg.get_str(&format!("{}::{}", sec_name, key)) {
Ok(self.add_def(sec, key, val))
} else {
Ok(false)
}
}
fn get_key_suffix(&self, i: u64) -> String {
if i == 1 {
"".to_owned()
} else {
i.to_string()
}
}
}
impl Model for DefaultModel {
fn add_def(&mut self, sec: &str, key: &str, value: &str) -> bool {
let mut ast = Assertion {
key: key.to_owned(),
value: remove_comment(value),
..Default::default()
};
if ast.value.is_empty() {
return false;
}
if sec == "r" || sec == "p" {
ast.tokens = ast
.value
.split(',')
.map(|x| format!("{}_{}", key, x.trim()))
.collect();
} else {
ast.value = escape_assertion(&ast.value);
}
if let Some(new_model) = self.model.get_mut(sec) {
new_model.insert(key.to_owned(), ast);
} else {
let mut new_ast_map = LinkedHashMap::new();
new_ast_map.insert(key.to_owned(), ast);
self.model.insert(sec.to_owned(), new_ast_map);
}
true
}
#[inline]
fn get_model(&self) -> &HashMap<String, AssertionMap> {
&self.model
}
#[inline]
fn get_mut_model(&mut self) -> &mut HashMap<String, AssertionMap> {
&mut self.model
}
fn build_role_links(
&mut self,
rm: Arc<RwLock<dyn RoleManager>>,
) -> Result<()> {
if let Some(asts) = self.model.get_mut("g") {
for ast in asts.values_mut() {
ast.build_role_links(Arc::clone(&rm))?;
}
}
Ok(())
}
#[cfg(feature = "incremental")]
fn build_incremental_role_links(
&mut self,
rm: Arc<RwLock<dyn RoleManager>>,
d: EventData,
) -> Result<()> {
let ast = match d {
EventData::AddPolicy(ref sec, ref ptype, _)
| EventData::AddPolicies(ref sec, ref ptype, _)
| EventData::RemovePolicy(ref sec, ref ptype, _)
| EventData::RemovePolicies(ref sec, ref ptype, _)
| EventData::RemoveFilteredPolicy(ref sec, ref ptype, _)
if sec == "g" =>
{
self.model
.get_mut(sec)
.and_then(|ast_map| ast_map.get_mut(ptype))
}
_ => None,
};
if let Some(ast) = ast {
ast.build_incremental_role_links(rm, d)?;
}
Ok(())
}
fn add_policy(
&mut self,
sec: &str,
ptype: &str,
rule: Vec<String>,
) -> bool {
if let Some(ast_map) = self.model.get_mut(sec) {
if let Some(ast) = ast_map.get_mut(ptype) {
return ast.policy.insert(rule);
}
}
false
}
fn add_policies(
&mut self,
sec: &str,
ptype: &str,
rules: Vec<Vec<String>>,
) -> bool {
let mut all_added = true;
if let Some(ast_map) = self.model.get_mut(sec) {
if let Some(ast) = ast_map.get_mut(ptype) {
for rule in &rules {
if ast.policy.contains(rule) {
all_added = false;
return all_added;
}
}
ast.policy.extend(rules);
}
}
all_added
}
fn get_policy(&self, sec: &str, ptype: &str) -> Vec<Vec<String>> {
if let Some(t1) = self.model.get(sec) {
if let Some(t2) = t1.get(ptype) {
return t2.policy.iter().map(|x| x.to_owned()).collect();
}
}
vec![]
}
fn get_filtered_policy(
&self,
sec: &str,
ptype: &str,
field_index: usize,
field_values: Vec<String>,
) -> Vec<Vec<String>> {
let mut res = vec![];
if let Some(t1) = self.model.get(sec) {
if let Some(t2) = t1.get(ptype) {
for rule in t2.policy.iter() {
let mut matched = true;
for (i, field_value) in field_values.iter().enumerate() {
if !field_value.is_empty()
&& &rule[field_index + i] != field_value
{
matched = false;
break;
}
}
if matched {
res.push(rule.iter().map(String::from).collect());
}
}
}
}
res
}
fn has_policy(&self, sec: &str, ptype: &str, rule: Vec<String>) -> bool {
let policy = self.get_policy(sec, ptype);
for r in policy {
if r == rule {
return true;
}
}
false
}
fn get_values_for_field_in_policy(
&self,
sec: &str,
ptype: &str,
field_index: usize,
) -> Vec<String> {
self.get_policy(sec, ptype)
.into_iter()
.fold(LinkedHashSet::new(), |mut acc, x| {
acc.insert(x[field_index].clone());
acc
})
.into_iter()
.collect()
}
fn remove_policy(
&mut self,
sec: &str,
ptype: &str,
rule: Vec<String>,
) -> bool {
if let Some(ast_map) = self.model.get_mut(sec) {
if let Some(ast) = ast_map.get_mut(ptype) {
return ast.policy.remove(&rule);
}
}
false
}
fn remove_policies(
&mut self,
sec: &str,
ptype: &str,
rules: Vec<Vec<String>>,
) -> bool {
let mut all_removed = true;
if let Some(ast_map) = self.model.get_mut(sec) {
if let Some(ast) = ast_map.get_mut(ptype) {
for rule in &rules {
if !ast.policy.contains(rule) {
all_removed = false;
return all_removed;
}
}
for rule in &rules {
ast.policy.remove(rule);
}
}
}
all_removed
}
fn clear_policy(&mut self) {
if let Some(model_p) = self.model.get_mut("p") {
for ast in model_p.values_mut() {
ast.policy.clear();
}
}
if let Some(model_g) = self.model.get_mut("g") {
for ast in model_g.values_mut() {
ast.policy.clear();
}
}
}
fn remove_filtered_policy(
&mut self,
sec: &str,
ptype: &str,
field_index: usize,
field_values: Vec<String>,
) -> (bool, Vec<Vec<String>>) {
if field_values.is_empty() {
return (false, vec![]);
}
let mut res = false;
let mut rules_removed: Vec<Vec<String>> = vec![];
if let Some(ast_map) = self.model.get_mut(sec) {
if let Some(ast) = ast_map.get_mut(ptype) {
for rule in ast.policy.iter() {
let mut matched = true;
for (i, field_value) in field_values.iter().enumerate() {
if !field_value.is_empty()
&& &rule[field_index + i] != field_value
{
matched = false;
break;
}
}
if matched {
res = true;
rules_removed.push(rule.clone());
}
}
if res && !rules_removed.is_empty() {
for rule in rules_removed.iter() {
ast.policy.remove(rule);
}
}
}
}
(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)]
mod tests {
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_basic_model() {
let m = DefaultModel::from_file("examples/basic_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/basic_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(e.enforce(("alice", "data1", "read")).unwrap());
assert!(!e.enforce(("alice", "data1", "write")).unwrap());
assert!(!e.enforce(("alice", "data2", "read")).unwrap());
assert!(!e.enforce(("alice", "data2", "write")).unwrap());
assert!(!e.enforce(("bob", "data1", "read")).unwrap());
assert!(!e.enforce(("bob", "data1", "write")).unwrap());
assert!(!e.enforce(("bob", "data2", "read")).unwrap());
assert!(e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_basic_model_no_policy() {
let m = DefaultModel::from_file("examples/basic_model.conf")
.await
.unwrap();
let adapter = MemoryAdapter::default();
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(!e.enforce(("alice", "data1", "read")).unwrap());
assert!(!e.enforce(("alice", "data1", "write")).unwrap());
assert!(!e.enforce(("alice", "data2", "read")).unwrap());
assert!(!e.enforce(("alice", "data2", "write")).unwrap());
assert!(!e.enforce(("bob", "data1", "read")).unwrap());
assert!(!e.enforce(("bob", "data1", "write")).unwrap());
assert!(!e.enforce(("bob", "data2", "read")).unwrap());
assert!(!e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_basic_model_with_root() {
let m = DefaultModel::from_file("examples/basic_with_root_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/basic_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(e.enforce(("alice", "data1", "read")).unwrap());
assert!(e.enforce(("bob", "data2", "write")).unwrap());
assert!(e.enforce(("root", "data1", "read")).unwrap());
assert!(e.enforce(("root", "data1", "write")).unwrap());
assert!(e.enforce(("root", "data2", "read")).unwrap());
assert!(e.enforce(("root", "data2", "write")).unwrap());
assert!(!e.enforce(("alice", "data1", "write")).unwrap());
assert!(!e.enforce(("alice", "data2", "read")).unwrap());
assert!(!e.enforce(("alice", "data2", "write")).unwrap());
assert!(!e.enforce(("bob", "data1", "read")).unwrap());
assert!(!e.enforce(("bob", "data1", "write")).unwrap());
assert!(!e.enforce(("bob", "data2", "read")).unwrap());
}
#[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_basic_model_with_root_no_policy() {
let m = DefaultModel::from_file("examples/basic_with_root_model.conf")
.await
.unwrap();
let adapter = MemoryAdapter::default();
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(!e.enforce(("alice", "data1", "read")).unwrap());
assert!(!e.enforce(("bob", "data2", "write")).unwrap());
assert!(e.enforce(("root", "data1", "read")).unwrap());
assert!(e.enforce(("root", "data1", "write")).unwrap());
assert!(e.enforce(("root", "data2", "read")).unwrap());
assert!(e.enforce(("root", "data2", "write")).unwrap());
assert!(!e.enforce(("alice", "data1", "write")).unwrap());
assert!(!e.enforce(("alice", "data2", "read")).unwrap());
assert!(!e.enforce(("alice", "data2", "write")).unwrap());
assert!(!e.enforce(("bob", "data1", "read")).unwrap());
assert!(!e.enforce(("bob", "data1", "write")).unwrap());
assert!(!e.enforce(("bob", "data2", "read")).unwrap());
}
#[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_basic_model_without_users() {
let m =
DefaultModel::from_file("examples/basic_without_users_model.conf")
.await
.unwrap();
let adapter =
FileAdapter::new("examples/basic_without_users_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(e.enforce(("data1", "read")).unwrap());
assert!(!e.enforce(("data1", "write")).unwrap());
assert!(!e.enforce(("data2", "read")).unwrap());
assert!(e.enforce(("data2", "write")).unwrap());
}
#[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_basic_model_without_resources() {
let m = DefaultModel::from_file(
"examples/basic_without_resources_model.conf",
)
.await
.unwrap();
let adapter =
FileAdapter::new("examples/basic_without_resources_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert!(e.enforce(("alice", "read")).unwrap());
assert!(e.enforce(("bob", "write")).unwrap());
assert!(!e.enforce(("alice", "write")).unwrap());
assert!(!e.enforce(("bob", "read")).unwrap());
}
#[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_rbac_model() {
let m = DefaultModel::from_file("examples/rbac_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_rbac_model_with_resource_roles() {
let m = DefaultModel::from_file(
"examples/rbac_with_resource_roles_model.conf",
)
.await
.unwrap();
let adapter =
FileAdapter::new("examples/rbac_with_resource_roles_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(false, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_rbac_model_with_domains() {
let m =
DefaultModel::from_file("examples/rbac_with_domains_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_with_domains_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(
true,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("alice", "domain1", "data1", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "write")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "write")).unwrap()
);
}
#[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_rbac_model_with_domains_runtime() {
let m =
DefaultModel::from_file("examples/rbac_with_domains_model.conf")
.await
.unwrap();
let adapter = MemoryAdapter::default();
let mut e = Enforcer::new(m, adapter).await.unwrap();
e.add_policy(
vec!["admin", "domain1", "data1", "read"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_policy(
vec!["admin", "domain1", "data1", "write"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_policy(
vec!["admin", "domain2", "data2", "read"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_policy(
vec!["admin", "domain2", "data2", "write"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_grouping_policy(
vec!["alice", "admin", "domain1"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_grouping_policy(
vec!["bob", "admin", "domain2"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(
true,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("alice", "domain1", "data1", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "write")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "write")).unwrap()
);
assert_eq!(
true,
e.remove_filtered_policy(
1,
vec!["domain1", "data1"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data1", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "write")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "write")).unwrap()
);
assert_eq!(
true,
e.remove_policy(
vec!["admin", "domain2", "data2", "read"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data1", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("alice", "domain1", "data2", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "read")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data1", "write")).unwrap()
);
assert_eq!(
false,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "write")).unwrap()
);
}
#[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_rbac_model_with_domains_at_runtime_mock_adapter() {
let m =
DefaultModel::from_file("examples/rbac_with_domains_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_with_domains_policy.csv");
let mut e = Enforcer::new(m, adapter).await.unwrap();
e.add_policy(
vec!["admin", "domain3", "data1", "read"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
e.add_grouping_policy(
vec!["alice", "admin", "domain3"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(
true,
e.enforce(("alice", "domain3", "data1", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
e.remove_filtered_policy(
1,
vec!["domain1", "data1"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(
false,
e.enforce(("alice", "domain1", "data1", "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
e.remove_policy(
vec!["admin", "domain2", "data2", "read"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(
false,
e.enforce(("bob", "domain2", "data2", "read")).unwrap()
);
}
#[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_rbac_model_with_deny() {
let m = DefaultModel::from_file("examples/rbac_with_deny_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_with_deny_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_rbac_model_with_not_deny() {
let m =
DefaultModel::from_file("examples/rbac_with_not_deny_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_with_deny_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(false, e.enforce(("alice", "data2", "write")).unwrap());
}
#[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_rbac_model_with_custom_data() {
let m = DefaultModel::from_file("examples/rbac_model.conf")
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_policy.csv");
let mut e = Enforcer::new(m, adapter).await.unwrap();
e.add_grouping_policy(
vec!["bob", "data2_admin", "custom_data"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
e.remove_grouping_policy(
vec!["bob", "data2_admin", "custom_data"]
.iter()
.map(|s| s.to_string())
.collect(),
)
.await
.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("bob", "data1", "write")).unwrap());
assert_eq!(false, e.enforce(("bob", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
}
#[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_rbac_model_using_in_op() {
let m = DefaultModel::from_file(
"examples/rbac_model_matcher_using_in_op.conf",
)
.await
.unwrap();
let adapter = FileAdapter::new("examples/rbac_policy.csv");
let e = Enforcer::new(m, adapter).await.unwrap();
assert_eq!(true, e.enforce(("alice", "data1", "read")).unwrap());
assert_eq!(false, e.enforce(("alice", "data1", "write")).unwrap());
assert_eq!(true, e.enforce(("bob", "data2", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "write")).unwrap());
assert_eq!(true, e.enforce(("alice", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("guest", "data2", "read")).unwrap());
assert_eq!(true, e.enforce(("alice", "data3", "read")).unwrap());
assert_eq!(true, e.enforce(("bob", "data3", "read")).unwrap());
}
#[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_abac() {
use serde::Serialize;
let m = DefaultModel::from_file("examples/abac_model.conf")
.await
.unwrap();
let adapter = MemoryAdapter::default();
let e = Enforcer::new(m, adapter).await.unwrap();
#[derive(Serialize, Hash)]
pub struct Book<'a> {
owner: &'a str,
}
assert_eq!(
false,
e.enforce(("alice", Book { owner: "bob" }, "read")).unwrap()
);
assert_eq!(
true,
e.enforce(("alice", Book { owner: "alice" }, "read"))
.unwrap()
);
}
}