From e7e0a91a5b1f7181f00fa1c95932d0f0ac660a3e Mon Sep 17 00:00:00 2001 From: MuZhou233 Date: Mon, 2 Sep 2024 16:29:32 +0800 Subject: [PATCH 1/4] feat: add key_get series built-in operator --- src/core_api.rs | 4 +- src/enforcer.rs | 13 +- src/model/function_map.rs | 348 ++++++++++++++++++++++++++++++++++---- 3 files changed, 325 insertions(+), 40 deletions(-) diff --git a/src/core_api.rs b/src/core_api.rs index 131f1676..ca47c186 100644 --- a/src/core_api.rs +++ b/src/core_api.rs @@ -15,7 +15,7 @@ use crate::emitter::EventData; use async_trait::async_trait; use parking_lot::RwLock; -use rhai::ImmutableString; +use rhai::{Dynamic, ImmutableString}; use std::sync::Arc; @@ -36,7 +36,7 @@ pub trait CoreApi: Send + Sync { fn add_function( &mut self, fname: &str, - f: fn(ImmutableString, ImmutableString) -> bool, + f: fn(&[ImmutableString]) -> Dynamic, ); fn get_model(&self) -> &dyn Model; fn get_mut_model(&mut self) -> &mut dyn Model; diff --git a/src/enforcer.rs b/src/enforcer.rs index ddcbd589..6dac1ce9 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -428,7 +428,7 @@ impl CoreApi for Enforcer { fn add_function( &mut self, fname: &str, - f: fn(ImmutableString, ImmutableString) -> bool, + f: fn(&[ImmutableString]) -> Dynamic, ) { self.fm.add_function(fname, f); self.engine.register_fn(fname, f); @@ -1338,10 +1338,13 @@ mod tests { let adapter1 = FileAdapter::new("examples/keymatch_policy.csv"); let mut e = Enforcer::new(m1, adapter1).await.unwrap(); - e.add_function( - "keyMatchCustom", - |s1: ImmutableString, s2: ImmutableString| key_match(&s1, &s2), - ); + e.add_function("keyMatchCustom", |args: &[ImmutableString]| { + key_match( + &args.get(0).unwrap_or(&ImmutableString::from("")), + &args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); assert_eq!( true, diff --git a/src/model/function_map.rs b/src/model/function_map.rs index 01285e0d..ef2a5654 100644 --- a/src/model/function_map.rs +++ b/src/model/function_map.rs @@ -10,52 +10,91 @@ use globset::GlobBuilder; use ip_network::IpNetwork; use once_cell::sync::Lazy; use regex::Regex; -use rhai::ImmutableString; +use rhai::{Dynamic, ImmutableString}; static MAT_B: Lazy = Lazy::new(|| Regex::new(r":[^/]*").unwrap()); static MAT_P: Lazy = Lazy::new(|| Regex::new(r"\{[^/]*\}").unwrap()); +use std::any::Any; use std::{borrow::Cow, collections::HashMap}; pub struct FunctionMap { - pub(crate) fm: - HashMap bool>, + pub(crate) fm: HashMap Dynamic>, } impl Default for FunctionMap { fn default() -> FunctionMap { - let mut fm: HashMap< - String, - fn(ImmutableString, ImmutableString) -> bool, - > = HashMap::new(); - fm.insert( - "keyMatch".to_owned(), - |s1: ImmutableString, s2: ImmutableString| key_match(&s1, &s2), - ); - fm.insert( - "keyMatch2".to_owned(), - |s1: ImmutableString, s2: ImmutableString| key_match2(&s1, &s2), - ); - fm.insert( - "keyMatch3".to_owned(), - |s1: ImmutableString, s2: ImmutableString| key_match3(&s1, &s2), - ); - fm.insert( - "regexMatch".to_owned(), - |s1: ImmutableString, s2: ImmutableString| regex_match(&s1, &s2), - ); + let mut fm: HashMap Dynamic> = + HashMap::new(); + fm.insert("keyMatch".to_owned(), |args: &[ImmutableString]| { + key_match( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("keyGet".to_owned(), |args: &[ImmutableString]| { + key_get( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("keyMatch2".to_owned(), |args: &[ImmutableString]| { + key_match2( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("keyGet2".to_owned(), |args: &[ImmutableString]| { + key_get2( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + args.get(2).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("keyMatch3".to_owned(), |args: &[ImmutableString]| { + key_match3( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("keyGet3".to_owned(), |args: &[ImmutableString]| { + key_get3( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + args.get(2).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); + fm.insert("regexMatch".to_owned(), |args: &[ImmutableString]| { + regex_match( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); #[cfg(feature = "glob")] - fm.insert( - "globMatch".to_owned(), - |s1: ImmutableString, s2: ImmutableString| glob_match(&s1, &s2), - ); + fm.insert("globMatch".to_owned(), |args: &[ImmutableString]| { + glob_match( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); #[cfg(feature = "ip")] - fm.insert( - "ipMatch".to_owned(), - |s1: ImmutableString, s2: ImmutableString| ip_match(&s1, &s2), - ); + fm.insert("ipMatch".to_owned(), |args: &[ImmutableString]| { + ip_match( + args.get(0).unwrap_or(&ImmutableString::from("")), + args.get(1).unwrap_or(&ImmutableString::from("")), + ) + .into() + }); FunctionMap { fm } } @@ -66,7 +105,7 @@ impl FunctionMap { pub fn add_function( &mut self, fname: &str, - f: fn(ImmutableString, ImmutableString) -> bool, + f: fn(&[ImmutableString]) -> Dynamic, ) { self.fm.insert(fname.to_owned(), f); } @@ -74,7 +113,7 @@ impl FunctionMap { #[inline] pub fn get_functions( &self, - ) -> impl Iterator bool)> + ) -> impl Iterator Dynamic)> { self.fm.iter() } @@ -93,6 +132,20 @@ pub fn key_match(key1: &str, key2: &str) -> bool { } } +// key_get returns the matched part +// For example, "/foo/bar/foo" matches "/foo/*" +// "bar/foo" will be returned. +pub fn key_get(key1: &str, key2: &str) -> String { + if let Some(i) = key2.find('*') { + if key1.len() > i { + if key1[..i] == key2[..i] { + return key1[i..].to_string(); + } + } + } + "".to_string() +} + // key_match2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a * // For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource" pub fn key_match2(key1: &str, key2: &str) -> bool { @@ -107,6 +160,35 @@ pub fn key_match2(key1: &str, key2: &str) -> bool { regex_match(key1, &format!("^{}$", key2)) } +// key_get2 returns value matched pattern +// For example, "/resource1" matches "/:resource" +// if the pathVar == "resource", then "resource1" will be returned. +pub fn key_get2(key1: &str, key2: &str, path_var: &str) -> String { + let key2: Cow = if key2.contains("/*") { + key2.replace("/*", "/.*").into() + } else { + key2.into() + }; + + let re = Regex::new(r":[^/]+").unwrap(); + let keys: Vec<_> = re.find_iter(&key2).collect(); + let key2 = re.replace_all(&key2, "([^/]+)").to_string(); + let key2 = format!("^{}$", key2); + + if let Ok(re2) = Regex::new(&key2) { + if let Some(caps) = re2.captures(key1) { + for (i, key) in keys.iter().enumerate() { + if path_var == &key.as_str()[1..] { + return caps + .get(i + 1) + .map_or("".to_string(), |m| m.as_str().to_string()); + } + } + } + } + "".to_string() +} + // key_match3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a * // For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}" pub fn key_match3(key1: &str, key2: &str) -> bool { @@ -121,6 +203,38 @@ pub fn key_match3(key1: &str, key2: &str) -> bool { regex_match(key1, &format!("^{}$", key2)) } +// key_get3 returns value matched pattern +// For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/" +// if the pathVar == "project", then "project1" will be returned. +pub fn key_get3(key1: &str, key2: &str, path_var: &str) -> String { + let key2: Cow = if key2.contains("/*") { + key2.replace("/*", "/.*").into() + } else { + key2.into() + }; + + let re = Regex::new(r"\{[^/]+?\}").unwrap(); + let keys: Vec<_> = re.find_iter(&key2).collect(); + let key2 = re.replace_all(&key2, "([^/]+?)").to_string(); + let key2 = Regex::new(r"\{") + .unwrap() + .replace_all(&key2, "\\{") + .to_string(); + let key2 = format!("^{}$", key2); + + let re2 = Regex::new(&key2).unwrap(); + if let Some(caps) = re2.captures(key1) { + for (i, key) in keys.iter().enumerate() { + if path_var == &key.as_str()[1..key.as_str().len() - 1] { + return caps + .get(i + 1) + .map_or("".to_string(), |m| m.as_str().to_string()); + } + } + } + "".to_string() +} + // regex_match determines whether key1 matches the pattern of key2 in regular expression. pub fn regex_match(key1: &str, key2: &str) -> bool { Regex::new(key2).unwrap().is_match(key1) @@ -184,6 +298,19 @@ mod tests { assert!(key_match("/bar", "/ba*")); } + #[test] + fn test_key_get() { + assert_eq!(key_get("/foo", "/foo"), ""); + assert_eq!(key_get("/foo", "/foo*"), ""); + assert_eq!(key_get("/foo", "/foo/*"), ""); + assert_eq!(key_get("/foo/bar", "/foo"), ""); + assert_eq!(key_get("/foo/bar", "/foo*"), "/bar"); + assert_eq!(key_get("/foo/bar", "/foo/*"), "bar"); + assert_eq!(key_get("/foobar", "/foo"), ""); + assert_eq!(key_get("/foobar", "/foo*"), "bar"); + assert_eq!(key_get("/foobar", "/foo/*"), ""); + } + #[test] fn test_key_match2() { assert!(key_match2("/foo/bar", "/foo/*")); @@ -200,6 +327,58 @@ mod tests { assert!(!key_match2("/foo/bar", "/foo/:/baz")); } + #[test] + fn test_key_get2() { + assert_eq!(key_get2("/foo", "/foo", "id"), ""); + assert_eq!(key_get2("/foo", "/foo*", "id"), ""); + assert_eq!(key_get2("/foo", "/foo/*", "id"), ""); + assert_eq!(key_get2("/foo/bar", "/foo", "id"), ""); + assert_eq!(key_get2("/foo/bar", "/foo*", "id"), ""); + assert_eq!(key_get2("/foo/bar", "/foo/*", "id"), ""); + assert_eq!(key_get2("/foobar", "/foo", "id"), ""); + assert_eq!(key_get2("/foobar", "/foo*", "id"), ""); + assert_eq!(key_get2("/foobar", "/foo/*", "id"), ""); + + assert_eq!(key_get2("/", "/:resource", "resource"), ""); + assert_eq!( + key_get2("/resource1", "/:resource", "resource"), + "resource1" + ); + assert_eq!(key_get2("/myid", "/:id/using/:resId", "id"), ""); + assert_eq!( + key_get2("/myid/using/myresid", "/:id/using/:resId", "id"), + "myid" + ); + assert_eq!( + key_get2("/myid/using/myresid", "/:id/using/:resId", "resId"), + "myresid" + ); + + assert_eq!(key_get2("/proxy/myid", "/proxy/:id/*", "id"), ""); + assert_eq!(key_get2("/proxy/myid/", "/proxy/:id/*", "id"), "myid"); + assert_eq!(key_get2("/proxy/myid/res", "/proxy/:id/*", "id"), "myid"); + assert_eq!( + key_get2("/proxy/myid/res/res2", "/proxy/:id/*", "id"), + "myid" + ); + assert_eq!( + key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/*", "id"), + "myid" + ); + assert_eq!( + key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id"), + "myid" + ); + assert_eq!(key_get2("/proxy/", "/proxy/:id/*", "id"), ""); + + assert_eq!(key_get2("/alice", "/:id", "id"), "alice"); + assert_eq!(key_get2("/alice/all", "/:id/all", "id"), "alice"); + assert_eq!(key_get2("/alice", "/:id/all", "id"), ""); + assert_eq!(key_get2("/alice/all", "/:id", "id"), ""); + + assert_eq!(key_get2("/alice/all", "/:/all", ""), ""); + } + #[test] fn test_regex_match() { assert!(regex_match("foobar", "^foo*")); @@ -222,6 +401,109 @@ mod tests { assert!(key_match3("/foo/bar/baz", "/foo/{}/baz")); } + #[test] + fn test_key_get3() { + assert_eq!(key_get3("/foo", "/foo", "id"), ""); + assert_eq!(key_get3("/foo", "/foo*", "id"), ""); + assert_eq!(key_get3("/foo", "/foo/*", "id"), ""); + assert_eq!(key_get3("/foo/bar", "/foo", "id"), ""); + assert_eq!(key_get3("/foo/bar", "/foo*", "id"), ""); + assert_eq!(key_get3("/foo/bar", "/foo/*", "id"), ""); + assert_eq!(key_get3("/foobar", "/foo", "id"), ""); + assert_eq!(key_get3("/foobar", "/foo*", "id"), ""); + assert_eq!(key_get3("/foobar", "/foo/*", "id"), ""); + + assert_eq!(key_get3("/", "/{resource}", "resource"), ""); + assert_eq!( + key_get3("/resource1", "/{resource}", "resource"), + "resource1" + ); + assert_eq!(key_get3("/myid", "/{id}/using/{resId}", "id"), ""); + assert_eq!( + key_get3("/myid/using/myresid", "/{id}/using/{resId}", "id"), + "myid" + ); + assert_eq!( + key_get3("/myid/using/myresid", "/{id}/using/{resId}", "resId"), + "myresid" + ); + + assert_eq!(key_get3("/proxy/myid", "/proxy/{id}/*", "id"), ""); + assert_eq!(key_get3("/proxy/myid/", "/proxy/{id}/*", "id"), "myid"); + assert_eq!(key_get3("/proxy/myid/res", "/proxy/{id}/*", "id"), "myid"); + assert_eq!( + key_get3("/proxy/myid/res/res2", "/proxy/{id}/*", "id"), + "myid" + ); + assert_eq!( + key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id"), + "myid" + ); + assert_eq!( + key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/res/*", "id"), + "myid" + ); + assert_eq!(key_get3("/proxy/", "/proxy/{id}/*", "id"), ""); + + assert_eq!( + key_get3( + "/api/group1_group_name/project1_admin/info", + "/api/{proj}_admin/info", + "proj" + ), + "" + ); + assert_eq!( + key_get3("/{id/using/myresid", "/{id/using/{resId}", "resId"), + "myresid" + ); + assert_eq!( + key_get3( + "/{id/using/myresid/status}", + "/{id/using/{resId}/status}", + "resId" + ), + "myresid" + ); + + assert_eq!( + key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res"), + "res3" + ); + assert_eq!( + key_get3( + "/api/project1_admin/info", + "/api/{proj}_admin/info", + "proj" + ), + "project1" + ); + assert_eq!( + key_get3( + "/api/group1_group_name/project1_admin/info", + "/api/{g}_{gn}/{proj}_admin/info", + "g" + ), + "group1" + ); + assert_eq!( + key_get3( + "/api/group1_group_name/project1_admin/info", + "/api/{g}_{gn}/{proj}_admin/info", + "gn" + ), + "group_name" + ); + assert_eq!( + key_get3( + "/api/group1_group_name/project1_admin/info", + "/api/{g}_{gn}/{proj}_admin/info", + "proj" + ), + "project1" + ); + } + #[cfg(feature = "ip")] #[test] fn test_ip_match() { From b847143bd9573b327a940cbb5c4997420a0d2a05 Mon Sep 17 00:00:00 2001 From: MuZhou233 Date: Mon, 2 Sep 2024 17:54:17 +0800 Subject: [PATCH 2/4] fix: pass test --- src/core_api.rs | 4 +- src/enforcer.rs | 32 ++++++++----- src/model/function_map.rs | 97 ++++++++++----------------------------- 3 files changed, 46 insertions(+), 87 deletions(-) diff --git a/src/core_api.rs b/src/core_api.rs index ca47c186..cd78040b 100644 --- a/src/core_api.rs +++ b/src/core_api.rs @@ -1,4 +1,5 @@ use crate::{ + model::OperatorFunction, enforcer::EnforceContext, Adapter, Effector, EnforceArgs, Event, EventEmitter, Filter, Model, Result, RoleManager, TryIntoAdapter, TryIntoModel, @@ -15,7 +16,6 @@ use crate::emitter::EventData; use async_trait::async_trait; use parking_lot::RwLock; -use rhai::{Dynamic, ImmutableString}; use std::sync::Arc; @@ -36,7 +36,7 @@ pub trait CoreApi: Send + Sync { fn add_function( &mut self, fname: &str, - f: fn(&[ImmutableString]) -> Dynamic, + f: OperatorFunction, ); fn get_model(&self) -> &dyn Model; fn get_mut_model(&mut self) -> &mut dyn Model; diff --git a/src/enforcer.rs b/src/enforcer.rs index 6dac1ce9..2e94137d 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -7,7 +7,7 @@ use crate::{ error::{ModelError, PolicyError, RequestError}, get_or_err, get_or_err_with_context, management_api::MgmtApi, - model::{FunctionMap, Model}, + model::{FunctionMap, Model, OperatorFunction}, rbac::{DefaultRoleManager, RoleManager}, register_g_function, util::{escape_assertion, escape_eval}, @@ -350,6 +350,19 @@ impl Enforcer { } })) } + + fn register_function( + engine: &mut Engine, + key: &str, + f: OperatorFunction, + ) { + match f { + OperatorFunction::Arg0(func) => {engine.register_fn(key, func);} + OperatorFunction::Arg1(func) => {engine.register_fn(key, func);} + OperatorFunction::Arg2(func) => {engine.register_fn(key, func);} + OperatorFunction::Arg3(func) => {engine.register_fn(key, func);} + } + } pub(crate) fn register_g_functions(&mut self) -> Result<()> { if let Some(ast_map) = self.model.get_model().get("g") { @@ -380,7 +393,7 @@ impl CoreApi for Enforcer { engine.register_global_module(CASBIN_PACKAGE.as_shared_module()); for (key, &func) in fm.get_functions() { - engine.register_fn(key, func); + Self::register_function(&mut engine, key, func); } let mut e = Self { @@ -428,10 +441,10 @@ impl CoreApi for Enforcer { fn add_function( &mut self, fname: &str, - f: fn(&[ImmutableString]) -> Dynamic, + f: OperatorFunction, ) { self.fm.add_function(fname, f); - self.engine.register_fn(fname, f); + Self::register_function(&mut self.engine, fname, f); } #[inline] @@ -1338,13 +1351,10 @@ mod tests { let adapter1 = FileAdapter::new("examples/keymatch_policy.csv"); let mut e = Enforcer::new(m1, adapter1).await.unwrap(); - e.add_function("keyMatchCustom", |args: &[ImmutableString]| { - key_match( - &args.get(0).unwrap_or(&ImmutableString::from("")), - &args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); + e.add_function( + "keyMatchCustom", + OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| key_match(&s1, &s2).into()), + ); assert_eq!( true, diff --git a/src/model/function_map.rs b/src/model/function_map.rs index ef2a5654..3321f1b7 100644 --- a/src/model/function_map.rs +++ b/src/model/function_map.rs @@ -15,86 +15,37 @@ use rhai::{Dynamic, ImmutableString}; static MAT_B: Lazy = Lazy::new(|| Regex::new(r":[^/]*").unwrap()); static MAT_P: Lazy = Lazy::new(|| Regex::new(r"\{[^/]*\}").unwrap()); -use std::any::Any; use std::{borrow::Cow, collections::HashMap}; +#[derive(Clone, Copy)] +pub enum OperatorFunction { + Arg0(fn() -> Dynamic), + Arg1(fn(ImmutableString) -> Dynamic), + Arg2(fn(ImmutableString, ImmutableString) -> Dynamic), + Arg3(fn(ImmutableString, ImmutableString, ImmutableString) -> Dynamic), +} + pub struct FunctionMap { - pub(crate) fm: HashMap Dynamic>, + pub(crate) fm: HashMap, } impl Default for FunctionMap { fn default() -> FunctionMap { - let mut fm: HashMap Dynamic> = + let mut fm: HashMap = HashMap::new(); - fm.insert("keyMatch".to_owned(), |args: &[ImmutableString]| { - key_match( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("keyGet".to_owned(), |args: &[ImmutableString]| { - key_get( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("keyMatch2".to_owned(), |args: &[ImmutableString]| { - key_match2( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("keyGet2".to_owned(), |args: &[ImmutableString]| { - key_get2( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - args.get(2).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("keyMatch3".to_owned(), |args: &[ImmutableString]| { - key_match3( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("keyGet3".to_owned(), |args: &[ImmutableString]| { - key_get3( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - args.get(2).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); - fm.insert("regexMatch".to_owned(), |args: &[ImmutableString]| { - regex_match( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); + fm.insert("keyMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match(&s1, &s2).into() })); + fm.insert("keyGet".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_get(&s1, &s2).into() })); + fm.insert("keyMatch2".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match2(&s1, &s2).into() })); + fm.insert("keyGet2".to_owned(), OperatorFunction::Arg3(|s1: ImmutableString, s2: ImmutableString, s3: ImmutableString| { key_get2(&s1, &s2, &s3).into() })); + fm.insert("keyMatch3".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match3(&s1, &s2).into() })); + fm.insert("keyGet3".to_owned(), OperatorFunction::Arg3(|s1: ImmutableString, s2: ImmutableString, s3: ImmutableString| { key_get3(&s1, &s2, &s3).into() })); + fm.insert("regexMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { regex_match(&s1, &s2).into() })); #[cfg(feature = "glob")] - fm.insert("globMatch".to_owned(), |args: &[ImmutableString]| { - glob_match( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); + fm.insert("globMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { glob_match(&s1, &s2).into() })); #[cfg(feature = "ip")] - fm.insert("ipMatch".to_owned(), |args: &[ImmutableString]| { - ip_match( - args.get(0).unwrap_or(&ImmutableString::from("")), - args.get(1).unwrap_or(&ImmutableString::from("")), - ) - .into() - }); + fm.insert("ipMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { ip_match(&s1, &s2).into() })); FunctionMap { fm } } @@ -105,7 +56,7 @@ impl FunctionMap { pub fn add_function( &mut self, fname: &str, - f: fn(&[ImmutableString]) -> Dynamic, + f: OperatorFunction, ) { self.fm.insert(fname.to_owned(), f); } @@ -113,7 +64,7 @@ impl FunctionMap { #[inline] pub fn get_functions( &self, - ) -> impl Iterator Dynamic)> + ) -> impl Iterator { self.fm.iter() } @@ -137,10 +88,8 @@ pub fn key_match(key1: &str, key2: &str) -> bool { // "bar/foo" will be returned. pub fn key_get(key1: &str, key2: &str) -> String { if let Some(i) = key2.find('*') { - if key1.len() > i { - if key1[..i] == key2[..i] { - return key1[i..].to_string(); - } + if key1.len() > i && key1[..i] == key2[..i] { + return key1[i..].to_string(); } } "".to_string() From 664dfdab3faf3703e4785e03a4a9407e89a69114 Mon Sep 17 00:00:00 2001 From: MuZhou233 Date: Mon, 2 Sep 2024 17:59:30 +0800 Subject: [PATCH 3/4] fix: pass test --- src/cached_enforcer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cached_enforcer.rs b/src/cached_enforcer.rs index 94442fd7..2e805924 100644 --- a/src/cached_enforcer.rs +++ b/src/cached_enforcer.rs @@ -8,7 +8,7 @@ use crate::{ emitter::{clear_cache, Event, EventData, EventEmitter}, enforcer::EnforceContext, enforcer::Enforcer, - model::Model, + model::{Model, OperatorFunction}, rbac::RoleManager, Result, }; @@ -27,7 +27,7 @@ use crate::{error::ModelError, get_or_err}; use async_trait::async_trait; use parking_lot::RwLock; -use rhai::{Dynamic, ImmutableString}; +use rhai::{Dynamic}; use std::{collections::HashMap, sync::Arc}; @@ -126,7 +126,7 @@ impl CoreApi for CachedEnforcer { fn add_function( &mut self, fname: &str, - f: fn(ImmutableString, ImmutableString) -> bool, + f: OperatorFunction, ) { self.enforcer.add_function(fname, f); } From 166a27479c03b953f1154bb67f8e403af8d8d088 Mon Sep 17 00:00:00 2001 From: MuZhou233 Date: Mon, 2 Sep 2024 18:04:06 +0800 Subject: [PATCH 4/4] fix: cargo fmt --- src/cached_enforcer.rs | 8 +--- src/core_api.rs | 13 ++---- src/enforcer.rs | 36 ++++++++------- src/model/function_map.rs | 97 +++++++++++++++++++++++++++++++-------- 4 files changed, 105 insertions(+), 49 deletions(-) diff --git a/src/cached_enforcer.rs b/src/cached_enforcer.rs index 2e805924..0da85503 100644 --- a/src/cached_enforcer.rs +++ b/src/cached_enforcer.rs @@ -27,7 +27,7 @@ use crate::{error::ModelError, get_or_err}; use async_trait::async_trait; use parking_lot::RwLock; -use rhai::{Dynamic}; +use rhai::Dynamic; use std::{collections::HashMap, sync::Arc}; @@ -123,11 +123,7 @@ impl CoreApi for CachedEnforcer { } #[inline] - fn add_function( - &mut self, - fname: &str, - f: OperatorFunction, - ) { + fn add_function(&mut self, fname: &str, f: OperatorFunction) { self.enforcer.add_function(fname, f); } diff --git a/src/core_api.rs b/src/core_api.rs index cd78040b..352bf432 100644 --- a/src/core_api.rs +++ b/src/core_api.rs @@ -1,8 +1,7 @@ use crate::{ - model::OperatorFunction, - enforcer::EnforceContext, Adapter, Effector, EnforceArgs, Event, - EventEmitter, Filter, Model, Result, RoleManager, TryIntoAdapter, - TryIntoModel, + enforcer::EnforceContext, model::OperatorFunction, Adapter, Effector, + EnforceArgs, Event, EventEmitter, Filter, Model, Result, RoleManager, + TryIntoAdapter, TryIntoModel, }; #[cfg(feature = "watcher")] @@ -33,11 +32,7 @@ pub trait CoreApi: Send + Sync { ) -> Result where Self: Sized; - fn add_function( - &mut self, - fname: &str, - f: OperatorFunction, - ); + fn add_function(&mut self, fname: &str, f: OperatorFunction); fn get_model(&self) -> &dyn Model; fn get_mut_model(&mut self) -> &mut dyn Model; fn get_adapter(&self) -> &dyn Adapter; diff --git a/src/enforcer.rs b/src/enforcer.rs index 2e94137d..7f87eae1 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -350,17 +350,21 @@ impl Enforcer { } })) } - - fn register_function( - engine: &mut Engine, - key: &str, - f: OperatorFunction, - ) { + + fn register_function(engine: &mut Engine, key: &str, f: OperatorFunction) { match f { - OperatorFunction::Arg0(func) => {engine.register_fn(key, func);} - OperatorFunction::Arg1(func) => {engine.register_fn(key, func);} - OperatorFunction::Arg2(func) => {engine.register_fn(key, func);} - OperatorFunction::Arg3(func) => {engine.register_fn(key, func);} + OperatorFunction::Arg0(func) => { + engine.register_fn(key, func); + } + OperatorFunction::Arg1(func) => { + engine.register_fn(key, func); + } + OperatorFunction::Arg2(func) => { + engine.register_fn(key, func); + } + OperatorFunction::Arg3(func) => { + engine.register_fn(key, func); + } } } @@ -438,11 +442,7 @@ impl CoreApi for Enforcer { } #[inline] - fn add_function( - &mut self, - fname: &str, - f: OperatorFunction, - ) { + fn add_function(&mut self, fname: &str, f: OperatorFunction) { self.fm.add_function(fname, f); Self::register_function(&mut self.engine, fname, f); } @@ -1353,7 +1353,11 @@ mod tests { e.add_function( "keyMatchCustom", - OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| key_match(&s1, &s2).into()), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + key_match(&s1, &s2).into() + }, + ), ); assert_eq!( diff --git a/src/model/function_map.rs b/src/model/function_map.rs index 3321f1b7..bc9faddf 100644 --- a/src/model/function_map.rs +++ b/src/model/function_map.rs @@ -31,21 +31,87 @@ pub struct FunctionMap { impl Default for FunctionMap { fn default() -> FunctionMap { - let mut fm: HashMap = - HashMap::new(); - fm.insert("keyMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match(&s1, &s2).into() })); - fm.insert("keyGet".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_get(&s1, &s2).into() })); - fm.insert("keyMatch2".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match2(&s1, &s2).into() })); - fm.insert("keyGet2".to_owned(), OperatorFunction::Arg3(|s1: ImmutableString, s2: ImmutableString, s3: ImmutableString| { key_get2(&s1, &s2, &s3).into() })); - fm.insert("keyMatch3".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { key_match3(&s1, &s2).into() })); - fm.insert("keyGet3".to_owned(), OperatorFunction::Arg3(|s1: ImmutableString, s2: ImmutableString, s3: ImmutableString| { key_get3(&s1, &s2, &s3).into() })); - fm.insert("regexMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { regex_match(&s1, &s2).into() })); + let mut fm: HashMap = HashMap::new(); + fm.insert( + "keyMatch".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + key_match(&s1, &s2).into() + }, + ), + ); + fm.insert( + "keyGet".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + key_get(&s1, &s2).into() + }, + ), + ); + fm.insert( + "keyMatch2".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + key_match2(&s1, &s2).into() + }, + ), + ); + fm.insert( + "keyGet2".to_owned(), + OperatorFunction::Arg3( + |s1: ImmutableString, + s2: ImmutableString, + s3: ImmutableString| { + key_get2(&s1, &s2, &s3).into() + }, + ), + ); + fm.insert( + "keyMatch3".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + key_match3(&s1, &s2).into() + }, + ), + ); + fm.insert( + "keyGet3".to_owned(), + OperatorFunction::Arg3( + |s1: ImmutableString, + s2: ImmutableString, + s3: ImmutableString| { + key_get3(&s1, &s2, &s3).into() + }, + ), + ); + fm.insert( + "regexMatch".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + regex_match(&s1, &s2).into() + }, + ), + ); #[cfg(feature = "glob")] - fm.insert("globMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { glob_match(&s1, &s2).into() })); + fm.insert( + "globMatch".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + glob_match(&s1, &s2).into() + }, + ), + ); #[cfg(feature = "ip")] - fm.insert("ipMatch".to_owned(), OperatorFunction::Arg2(|s1: ImmutableString, s2: ImmutableString| { ip_match(&s1, &s2).into() })); + fm.insert( + "ipMatch".to_owned(), + OperatorFunction::Arg2( + |s1: ImmutableString, s2: ImmutableString| { + ip_match(&s1, &s2).into() + }, + ), + ); FunctionMap { fm } } @@ -53,19 +119,14 @@ impl Default for FunctionMap { impl FunctionMap { #[inline] - pub fn add_function( - &mut self, - fname: &str, - f: OperatorFunction, - ) { + pub fn add_function(&mut self, fname: &str, f: OperatorFunction) { self.fm.insert(fname.to_owned(), f); } #[inline] pub fn get_functions( &self, - ) -> impl Iterator - { + ) -> impl Iterator { self.fm.iter() } }