diff --git a/casbin/casbin.vcxproj b/casbin/casbin.vcxproj index ac39d519..d7b92c3f 100644 --- a/casbin/casbin.vcxproj +++ b/casbin/casbin.vcxproj @@ -196,7 +196,7 @@ - + @@ -267,14 +267,14 @@ - - + + diff --git a/casbin/casbin.vcxproj.filters b/casbin/casbin.vcxproj.filters index 65149081..a3936ced 100644 --- a/casbin/casbin.vcxproj.filters +++ b/casbin/casbin.vcxproj.filters @@ -222,9 +222,6 @@ Source Files\persist\file_adapter - - Source Files\persist\file_adapter - Source Files\persist @@ -255,6 +252,9 @@ Source Files\ip_parser\parser + + Source Files\persist\file_adapter + @@ -272,9 +272,6 @@ Header Files\persist - - Header Files\persist - Header Files\persist @@ -416,9 +413,6 @@ Header Files\duktape - - Header Files\persist\file_adapter - Header Files\effect @@ -467,5 +461,11 @@ Header Files\model + + Header Files\persist + + + Header Files\persist\file_adapter + \ No newline at end of file diff --git a/casbin/config/config.cpp b/casbin/config/config.cpp index 02ac90be..8ed2fe69 100644 --- a/casbin/config/config.cpp +++ b/casbin/config/config.cpp @@ -156,9 +156,10 @@ void Config :: Set(string key, string value) { if (keys.size() >= 2) { section = keys[0]; option = keys[1]; - } else { - option = keys[0]; } + else + option = keys[0]; + AddConfig(section, option, value); mtx_lock.unlock(); } diff --git a/casbin/effect/default_effector.cpp b/casbin/effect/default_effector.cpp index 8bff2093..eb99fb8d 100644 --- a/casbin/effect/default_effector.cpp +++ b/casbin/effect/default_effector.cpp @@ -33,27 +33,25 @@ DefaultEffector* DefaultEffector :: NewDefaultEffector(){ bool DefaultEffector :: MergeEffects(string expr, vector effects, vector results) { bool result; - unsigned int number_of_effects = sizeof(effects) / sizeof(effects[0]); - - if (!expr.compare("some(where (p_eft == allow))")) { + if (!expr.compare("some(where (p.eft == allow))")) { result = false; - for(unsigned int index = 0 ; index < number_of_effects ; index++){ + for(unsigned int index = 0 ; index < effects.size() ; index++){ if (effects[index] == Effect::Allow) { result = true; break; } } - } else if (!expr.compare("!some(where (p_eft == deny))")) { + } else if (!expr.compare("!some(where (p.eft == deny))")) { result = true; - for(unsigned int index = 0 ; index < number_of_effects ; index++){ + for(unsigned int index = 0 ; index < effects.size(); index++){ if (effects[index] == Effect::Deny) { result = false; break; } } - } else if (!expr.compare("some(where (p_eft == allow)) && !some(where (p_eft == deny))")) { + } else if (!expr.compare("some(where (p.eft == allow)) && !some(where (p.eft == deny))")) { result = false; - for(unsigned int index = 0 ; index < number_of_effects ; index++){ + for(unsigned int index = 0 ; index < effects.size(); index++){ if (effects[index] == Effect::Allow) { result = true; } else if (effects[index] == Effect::Deny) { @@ -61,9 +59,9 @@ bool DefaultEffector :: MergeEffects(string expr, vector effects, vector break; } } - } else if (!expr.compare("priority(p_eft) || deny")) { + } else if (!expr.compare("priority(p.eft) || deny")) { result = false; - for(unsigned int index = 0 ; index < number_of_effects ; index++){ + for(unsigned int index = 0 ; index < effects.size(); index++){ if (effects[index] != Effect::Indeterminate) { if (effects[index] == Effect::Allow) { result = true; diff --git a/casbin/enforcer.cpp b/casbin/enforcer.cpp index 7c514b23..105934c3 100644 --- a/casbin/enforcer.cpp +++ b/casbin/enforcer.cpp @@ -18,6 +18,8 @@ #include "pch.h" +#include + #include "./enforcer.h" #include "./persist/watcher_ex.h" #include "./persist/file_adapter/file_adapter.h" @@ -37,117 +39,123 @@ bool Enforcer :: enforce(string matcher, Scope scope) { // }() this->func_map.scope = scope; + this->func_map.LoadFunctionMap(); - if(this->enabled) + if(!this->enabled) return true; // for(unordered_map :: iterator it = this->fm.fmap.begin() ; it != this->fm.fmap.end() ; it++) // this->fm.AddFunction(it->first, it->second); - string expString; + string exp_string; if(matcher == "") - expString = this->model->m["m"].assertion_map["m"]->value; + exp_string = this->model->m["m"].assertion_map["m"]->value; else - expString = matcher; + exp_string = matcher; + unordered_map rm_map; bool ok = this->model->m.find("g") != this->model->m.end(); + if(ok) { for(unordered_map :: iterator it = this->model->m["g"].assertion_map.begin() ; it != this->model->m["g"].assertion_map.end() ; it++){ RoleManager* rm = it->second->rm; - int index = int(expString.find((it->first)+"(")); + int char_count = int(count(it->second->value.begin(), it->second->value.end(), '_')); + int index = int(exp_string.find((it->first)+"(")); if(index != string::npos) - expString.insert(index+(it->first+"(").length()-1, (it->first)+"_rm"); - PushPointer(this->func_map.scope, (void *)rm, (it->first)+"_rm"); - this->func_map.AddFunction(it->first, GFunction); + exp_string.insert(index+(it->first+"(").length(), "rm, "); + PushPointer(this->func_map.scope, (void *)rm, "rm"); + this->func_map.AddFunction(it->first, GFunction, char_count + 1); } } - unordered_map pIntTokens; + unordered_map p_int_tokens; for(int i = 0 ; i < this->model->m["p"].assertion_map["p"]->tokens.size() ; i++) - pIntTokens[this->model->m["p"].assertion_map["p"]->tokens[i]] = i; + p_int_tokens[this->model->m["p"].assertion_map["p"]->tokens[i]] = i; - vector pTokens = this->model->m["p"].assertion_map["p"]->tokens; + vector p_tokens = this->model->m["p"].assertion_map["p"]->tokens; - vector policyEffects; - vector matcherResults; + int policy_len = int(this->model->m["p"].assertion_map["p"]->policy.size()); - int policyLen = int(this->model->m["p"].assertion_map["p"]->policy.size()); + vector policy_effects(policy_len, Effect :: Indeterminate); + vector matcher_results; - if(policyLen != 0) { + if(policy_len != 0) { if(this->model->m["r"].assertion_map["r"]->tokens.size() != this->func_map.GetRLen()) return false; //TODO - for( int i = 0 ; i < this->model->m["p"].assertion_map["p"]->policy.size() ; i++){ + for( int i = 0 ; i < policy_len ; i++){ // log.LogPrint("Policy Rule: ", pvals) - vector pVals = this->model->m["p"].assertion_map["p"]->policy[i]; - if(this->model->m["p"].assertion_map["p"]->tokens.size() != pVals.size()) + vector p_vals = this->model->m["p"].assertion_map["p"]->policy[i]; + if(this->model->m["p"].assertion_map["p"]->tokens.size() != p_vals.size()) return false; PushObject(this->func_map.scope, "p"); - for(int j = 0 ; j < pTokens.size() ; j++){ - int index = int(pTokens[j].find("_")); - string token = pTokens[j].substr(index+1); - PushStringPropToObject(this->func_map.scope, "p", pVals[j], token); + for(int j = 0 ; j < p_tokens.size() ; j++){ + int index = int(p_tokens[j].find("_")); + string token = p_tokens[j].substr(index+1); + PushStringPropToObject(this->func_map.scope, "p", p_vals[j], token); } - this->func_map.Eval(expString); + this->func_map.Evaluate(exp_string); + //TODO // log.LogPrint("Result: ", result) - if(CheckType(this->func_map.scope) == Type :: Bool){ bool result = GetBoolean(this->func_map.scope); if(!result) { - policyEffects[i] = Effect :: Indeterminate; + policy_effects[i] = Effect :: Indeterminate; continue; } } else if(CheckType(this->func_map.scope) == Type :: Float){ bool result = GetFloat(this->func_map.scope); if(result == 0) { - policyEffects[i] = Effect :: Indeterminate; + policy_effects[i] = Effect :: Indeterminate; continue; } else - matcherResults[i] = result; + matcher_results[i] = result; } else return false; - bool ok = pIntTokens.find("p_eft") != pIntTokens.end(); - if(ok) { - int j = pIntTokens["p_eft"]; - string eft = pVals[j]; + bool is_p_eft = p_int_tokens.find("p_eft") != p_int_tokens.end(); + if(is_p_eft) { + int j = p_int_tokens["p_eft"]; + string eft = p_vals[j]; if(eft == "allow") - policyEffects[i] = Effect :: Allow; + policy_effects[i] = Effect :: Allow; else if(eft == "deny") - policyEffects[i] = Effect :: Deny; + policy_effects[i] = Effect :: Deny; else - policyEffects[i] = Effect :: Indeterminate; + policy_effects[i] = Effect :: Indeterminate; } else - policyEffects[i] = Effect :: Allow; + policy_effects[i] = Effect :: Allow; if(this->model->m["e"].assertion_map["e"]->value == "priority(p_eft) || deny") break; } } else { - this->func_map.Eval(expString); + bool isValid = this->func_map.Evaluate(exp_string); + if(!isValid) + return false; bool result = this->func_map.GetBooleanResult(); + //TODO // log.LogPrint("Result: ", result) - if(result) - policyEffects[0] = Effect::Allow; + policy_effects.push_back(Effect::Allow); else - policyEffects[0] = Effect::Indeterminate; + policy_effects.push_back(Effect::Indeterminate); } //TODO // log.LogPrint("Rule Results: ", policyEffects) - bool result = this->eft->MergeEffects(this->model->m["e"].assertion_map["e"]->value, policyEffects, matcherResults); - + bool result = this->eft->MergeEffects(this->model->m["e"].assertion_map["e"]->value, policy_effects, matcher_results); + return result; } @@ -198,7 +206,7 @@ Enforcer* Enforcer :: NewEnforcer(Model* m, Adapter* adapter) { e->Initialize(); - if (e->adapter != NULL) { + if (e->adapter->file_path != "") { e->LoadPolicy(); } return e; @@ -344,7 +352,6 @@ void Enforcer :: ClearPolicy() { void Enforcer :: LoadPolicy() { this->model->ClearPolicy(); this->adapter->LoadPolicy(this->model); - this->model->PrintPolicy(); if(this->auto_build_role_links) { diff --git a/casbin/enforcer.h b/casbin/enforcer.h index 7234622a..9f3ef161 100644 --- a/casbin/enforcer.h +++ b/casbin/enforcer.h @@ -20,7 +20,7 @@ #include "./rbac/role_manager.h" #include "./model/function.h" #include "./enforcer_interface.h" -#include "./persist/adapter_filtered.h" +#include "./persist/filtered_adapter.h" // Enforcer is the main interface for authorization enforcement and policy management. class Enforcer : public IEnforcer{ @@ -33,7 +33,6 @@ class Enforcer : public IEnforcer{ Adapter* adapter; Watcher* watcher; - RoleManager* rm; bool enabled; bool auto_save; @@ -45,6 +44,8 @@ class Enforcer : public IEnforcer{ public: + RoleManager* rm; + /** * Enforcer is the default constructor. */ @@ -190,7 +191,7 @@ class Enforcer : public IEnforcer{ bool RemoveNamedGroupingPolicy(string ptype, vector params); bool RemoveNamedGroupingPolicies(string p_type, vector> rules); bool RemoveFilteredNamedGroupingPolicy(string ptype, int field_index, vector field_values); - void AddFunction(string name, Function); + void AddFunction(string name, Function function, Index nargs); /*RBAC API member functions.*/ vector GetRolesForUser(string name); diff --git a/casbin/enforcer_interface.h b/casbin/enforcer_interface.h index 7b1424fe..0175a6ba 100644 --- a/casbin/enforcer_interface.h +++ b/casbin/enforcer_interface.h @@ -119,7 +119,7 @@ class IEnforcer { virtual bool RemoveNamedGroupingPolicy(string ptype, vector params) = 0; virtual bool RemoveNamedGroupingPolicies(string p_type, vector> rules) = 0; virtual bool RemoveFilteredNamedGroupingPolicy(string ptype, int fieldIndex, vector fieldValues) = 0; - virtual void AddFunction(string name, Function) = 0; + virtual void AddFunction(string name, Function function, Index nargs) = 0; /* Internal API member functions */ virtual bool addPolicy(string sec, string ptype, vector rule) = 0; diff --git a/casbin/internal_api.cpp b/casbin/internal_api.cpp index 33597955..efb67b44 100644 --- a/casbin/internal_api.cpp +++ b/casbin/internal_api.cpp @@ -22,6 +22,7 @@ #include "./persist/batch_adapter.h" #include "./util/util.h" #include "./persist/watcher_ex.h" +#include "./exception/unsupported_operation_exception.h" // addPolicy adds a rule to the current policy. bool Enforcer :: addPolicy(string sec, string p_type, vector rule) { @@ -34,8 +35,13 @@ bool Enforcer :: addPolicy(string sec, string p_type, vector rule) { this->BuildIncrementalRoleLinks(policy_add, p_type, rules); } - if (this->adapter != NULL && this->auto_save) - this->adapter->AddPolicy(sec, p_type, rule); + if (this->adapter != NULL && this->auto_save) { + try { + this->adapter->AddPolicy(sec, p_type, rule); + } + catch(UnsupportedOperationException e) { + } + } if (this->watcher != NULL && this->auto_notify_watcher) { if (IsInstanceOf(this->watcher)) { @@ -79,9 +85,14 @@ bool Enforcer :: removePolicy(string sec, string p_type, vector rule) { vector> rules{rule}; this->BuildIncrementalRoleLinks(policy_add, p_type, rules); } - - if(this->adapter != NULL && this->auto_save) - this->adapter->RemovePolicy(sec, p_type, rule); + + if (this->adapter != NULL && this->auto_save) { + try { + this->adapter->RemovePolicy(sec, p_type, rule); + } + catch (UnsupportedOperationException e) { + } + } if(this->watcher !=NULL && this->auto_notify_watcher){ if (IsInstanceOf(this->watcher)) { @@ -127,8 +138,13 @@ bool Enforcer :: removeFilteredPolicy(string sec, string p_type, int field_index if (sec == "g") this->BuildIncrementalRoleLinks(policy_remove, p_type, effects); - if(this->adapter != NULL && this->auto_save) - this->adapter->RemoveFilteredPolicy(sec, p_type, field_index, field_values); + if (this->adapter != NULL && this->auto_save) { + try { + this->adapter->RemoveFilteredPolicy(sec, p_type, field_index, field_values); \ + } + catch (UnsupportedOperationException e) { + } + } if (this->watcher !=NULL && this->auto_notify_watcher) { if (IsInstanceOf(this->watcher)) { diff --git a/casbin/management_api.cpp b/casbin/management_api.cpp index f2dae877..00833475 100644 --- a/casbin/management_api.cpp +++ b/casbin/management_api.cpp @@ -304,6 +304,6 @@ bool Enforcer :: RemoveFilteredNamedGroupingPolicy(string p_type, int field_inde } // AddFunction adds a customized function. -void Enforcer :: AddFunction(string name, Function function) { - this->func_map.AddFunction(name, function); +void Enforcer :: AddFunction(string name, Function function, Index nargs) { + this->func_map.AddFunction(name, function, nargs); } \ No newline at end of file diff --git a/casbin/model/Function.h b/casbin/model/Function.h index 6a80fcb8..1903a646 100644 --- a/casbin/model/Function.h +++ b/casbin/model/Function.h @@ -30,16 +30,18 @@ class FunctionMap { FunctionMap(); + void ProcessFunctions(string expression); + int GetRLen(); - void Eval(string expression); + bool Evaluate(string expression); bool GetBooleanResult(); // AddFunction adds an expression function. - void AddFunction(string func_name, Function f, Index nargs = VARARGS); + void AddFunction(string func_name, Function f, Index nargs); - void AddFunctionPropToR(string identifier, Function func, unsigned int nargs = VARARGS); + void AddFunctionPropToR(string identifier, Function func, Index nargs); void AddBooleanPropToR(string identifier, bool val); @@ -60,7 +62,7 @@ class FunctionMap { void AddObjectPropToR(string identifier); // LoadFunctionMap loads an initial function map. - static FunctionMap LoadFunctionMap(); + void LoadFunctionMap(); }; diff --git a/casbin/model/function.cpp b/casbin/model/function.cpp index 606652d8..55856fb5 100644 --- a/casbin/model/function.cpp +++ b/casbin/model/function.cpp @@ -19,9 +19,37 @@ #include "pch.h" #include "./function.h" +#include "../util/util.h" FunctionMap :: FunctionMap(){ - scope = duk_create_heap_default(); + scope = InitializeScope(); +} + +void FunctionMap :: ProcessFunctions(string expression){ + for(unordered_map :: iterator it = this->func_map.begin() ; it != this->func_map.end() ; it++){ + int index = int(expression.find((it->first)+"(")); + + if(index != string::npos){ + int close_index = int(expression.find(")", index)); + int start = index + int(((it->first)+"(").length()); + + string function_params = expression.substr(start, close_index-start); + FetchIdentifier(this->scope, it->first); + vector params = Split(function_params, ","); + + for(int i=0;iscope, Trim(params[i])); + else{ + params[i] = params[i].replace(quote_index, 1, "'"); + int second_quote_index = int(params[i].find("\"", quote_index+1)); + params[i] = params[i].replace(second_quote_index, 1, "'"); + Get(this->scope, Trim(params[i])); + } + } + } + } } int FunctionMap :: GetRLen(){ @@ -31,8 +59,9 @@ int FunctionMap :: GetRLen(){ return -1; } -void FunctionMap :: Eval(string expression){ - duk_eval_string(scope, expression.c_str()); +bool FunctionMap :: Evaluate(string expression){ + ProcessFunctions(expression); + return Eval(scope, expression); } bool FunctionMap :: GetBooleanResult(){ @@ -42,11 +71,11 @@ bool FunctionMap :: GetBooleanResult(){ // AddFunction adds an expression function. void FunctionMap :: AddFunction(string func_name, Function f, Index nargs) { func_map[func_name] = f; - PushFunction(this->scope, f, nargs, func_name); + PushFunction(this->scope, f, func_name, nargs); } -void FunctionMap :: AddFunctionPropToR(string identifier, Function func, unsigned int nargs){ - PushFunctionPropToObject(scope, "r", func, nargs, identifier); +void FunctionMap :: AddFunctionPropToR(string identifier, Function func, Index nargs){ + PushFunctionPropToObject(scope, "r", func, identifier, nargs); } void FunctionMap :: AddBooleanPropToR(string identifier, bool val){ @@ -86,14 +115,10 @@ void FunctionMap :: AddObjectPropToR(string identifier){ } // LoadFunctionMap loads an initial function map. -FunctionMap FunctionMap :: LoadFunctionMap() { - FunctionMap func_map; - - func_map.AddFunction("keyMatch", KeyMatch); - func_map.AddFunction("keyMatch2", KeyMatch2); - func_map.AddFunction("keyMatch3", KeyMatch3); - func_map.AddFunction("regexMatch", RegexMatch); - func_map.AddFunction("ipMatch", IPMatch); - - return func_map; +void FunctionMap :: LoadFunctionMap() { + AddFunction("keyMatch", KeyMatch, 2); + AddFunction("keyMatch2", KeyMatch2, 2); + AddFunction("keyMatch3", KeyMatch3, 2); + AddFunction("regexMatch", RegexMatch, 2); + AddFunction("ipMatch", IPMatch, 2); } \ No newline at end of file diff --git a/casbin/model/model.cpp b/casbin/model/model.cpp index 080111a9..e2c8e604 100644 --- a/casbin/model/model.cpp +++ b/casbin/model/model.cpp @@ -57,8 +57,9 @@ bool Model :: HasSection(string sec) { void Model :: LoadSection(Model* model, ConfigInterface* cfg, string sec) { int i = 1; while(true) { - if (!LoadAssertion(model, cfg, sec, sec+GetKeySuffix(i))) + if (!LoadAssertion(model, cfg, sec, sec+GetKeySuffix(i))){ break; + } else i++; } @@ -93,11 +94,12 @@ bool Model :: AddDef(string sec, string key, string value) { ast->tokens[i] = key + "_" + Trim(ast->tokens[i]); } else - ast->value = RemoveComments(EscapeAssertion(ast->value)); + ast->value = RemoveComments(ast->value); - if (m.find(sec) != m.end()) + if (m.find(sec) == m.end()) m[sec] = AssertionMap(); ast->policy = vector>{}; + m[sec].assertion_map[key] = ast; return true; @@ -160,9 +162,8 @@ void Model :: BuildIncrementalRoleLinks(RoleManager* rm, policy_op op, string se // BuildRoleLinks initializes the roles in RBAC. void Model :: BuildRoleLinks(RoleManager* rm) { - for (unordered_map :: iterator it = this->m["g"].assertion_map.begin() ; it != this->m["g"].assertion_map.end() ; it++) { + for (unordered_map :: iterator it = this->m["g"].assertion_map.begin() ; it != this->m["g"].assertion_map.end() ; it++) (it->second)->BuildRoleLinks(rm); - } } // PrintPolicy prints the policy to log. @@ -186,13 +187,15 @@ void Model :: PrintPolicy() { // ClearPolicy clears all current policy. void Model :: ClearPolicy() { - for (unordered_map :: iterator it = this->m["p"].assertion_map.begin() ; it != this->m["p"].assertion_map.end() ; it++) + for (unordered_map :: iterator it = this->m["p"].assertion_map.begin() ; it != this->m["p"].assertion_map.end() ; it++){ if((it->second)->policy.size() > 0) (it->second)->policy.clear(); + } - for (unordered_map :: iterator it = this->m["g"].assertion_map.begin() ; it != this->m["g"].assertion_map.end() ; it++) + for (unordered_map :: iterator it = this->m["g"].assertion_map.begin() ; it != this->m["g"].assertion_map.end() ; it++){ if((it->second)->policy.size() > 0) (it->second)->policy.clear(); + } } // GetPolicy gets all rules in a policy. @@ -221,7 +224,7 @@ vector> Model :: GetFilteredPolicy(string sec, string p_type, int // HasPolicy determines whether a model has the specified policy rule. bool Model :: HasPolicy(string sec, string p_type, vector rule) { - vector> policy(m[sec].assertion_map[p_type]->policy); + vector> policy = m[sec].assertion_map[p_type]->policy; for(int i=0 ; i < policy.size() ; i++) if (ArrayEquals(rule, policy[i])) return true; @@ -235,6 +238,7 @@ bool Model :: AddPolicy(string sec, string p_type, vector rule) { m[sec].assertion_map[p_type]->policy.push_back(rule); return true; } + return false; } @@ -265,17 +269,19 @@ bool Model :: RemovePolicy(string sec, string p_type, vector rule) { // RemovePolicies removes policy rules from the model. bool Model :: RemovePolicies(string sec, string p_type, vector> rules) { OUTER: for (int j = 0; j < rules.size(); j++) { - for (int i = 0; i < this->m[sec].assertion_map[p_type]->policy.size(); i++) - if (ArrayEquals(rules[j], this->m[sec].assertion_map[p_type]->policy[i])) { + for (int i = 0; i < this->m[sec].assertion_map[p_type]->policy.size(); i++){ + if (ArrayEquals(rules[j], this->m[sec].assertion_map[p_type]->policy[i])) goto OUTER; } return false; } - for (int j = 0; j < rules.size(); j++) - for (int i = 0; i < this->m[sec].assertion_map[p_type]->policy.size(); i++) + for (int j = 0; j < rules.size(); j++){ + for (int i = 0; i < this->m[sec].assertion_map[p_type]->policy.size(); i++){ if (ArrayEquals(rules[j], this->m[sec].assertion_map[p_type]->policy[i])) this->m[sec].assertion_map[p_type]->policy.erase(this->m[sec].assertion_map[p_type]->policy.begin() + i); + } + } return true; } @@ -288,8 +294,8 @@ pair>> Model :: RemoveFilteredPolicy(string sec, str bool res = false; for(int i = 0 ; i < policy.size() ; i++){ bool matched = true; - for (int i = 0 ; i < field_values.size() ; i++) { - if (field_values[i] != "" && (policy[i])[field_index+i] != field_values[i]) { + for (int j = 0 ; j < field_values.size() ; j++) { + if (field_values[j] != "" && (policy[i])[field_index+j] != field_values[j]) { matched = false; break; } diff --git a/casbin/model/scope_config.cpp b/casbin/model/scope_config.cpp index 1df22cf7..712c99ac 100644 --- a/casbin/model/scope_config.cpp +++ b/casbin/model/scope_config.cpp @@ -64,7 +64,7 @@ void PushObjectValue(Scope scope){ duk_push_global_object(scope); } -void PushFunction(Scope scope, Function f, int nargs, string fname) { +void PushFunction(Scope scope, Function f, string fname, int nargs) { duk_push_c_function(scope, f, (Index)nargs); duk_put_global_string(scope, fname.c_str()); } @@ -110,13 +110,13 @@ void PushPointer(Scope scope, void * ptr, string identifier){ } void PushObject(Scope scope, string identifier){ - duk_push_global_object(scope); + duk_push_object(scope); duk_put_global_string(scope, identifier.c_str()); duk_push_int(scope, 0); duk_put_global_string(scope, (identifier+"len").c_str()); } -void PushFunctionPropToObject(Scope scope, string obj, Function f, int nargs, string fname) { +void PushFunctionPropToObject(Scope scope, string obj, Function f, string fname, int nargs) { duk_get_global_string(scope, obj.c_str()); duk_push_c_function(scope, f, nargs); duk_put_prop_string(scope, -2, fname.c_str()); @@ -223,4 +223,17 @@ string GetString(Scope scope, int id){ void* GetPointer(Scope scope, int id){ return (void *)duk_to_pointer(scope, (Index)id); +} + +void Get(Scope scope, string identifier){ + Eval(scope, identifier); +} + +bool Eval(Scope scope, string expression){ + PushStringValue(scope, expression); + return duk_peval(scope)==0; +} + +void EvalNoResult(Scope scope, string expression){ + duk_eval_string_noresult(scope, expression.c_str()); } \ No newline at end of file diff --git a/casbin/model/scope_config.h b/casbin/model/scope_config.h index 3ffd9a7b..7552f8e3 100644 --- a/casbin/model/scope_config.h +++ b/casbin/model/scope_config.h @@ -50,7 +50,7 @@ void PushDoubleValue(Scope scope, double d); void PushStringValue(Scope scope, string s); void PushPointerValue(Scope scope, void * ptr); void PushObjectValue(Scope scope); -void PushFunction(Scope scope, Function f, int nargs, string fname); +void PushFunction(Scope scope, Function f, string fname, int nargs); void PushBoolean(Scope scope, bool expression, string identifier); void PushTrue(Scope scope, string identifier); void PushFalse(Scope scope, string identifier); @@ -60,7 +60,7 @@ void PushDouble(Scope scope, double d, string identifier); void PushString(Scope scope, string s, string identifier); void PushPointer(Scope scope, void * ptr, string identifier); void PushObject(Scope scope, string identifier = "r"); -void PushFunctionPropToObject(Scope scope, string obj, Function f, int nargs, string fname); +void PushFunctionPropToObject(Scope scope, string obj, Function f, string fname, int nargs); void PushBooleanPropToObject(Scope scope, string obj, bool expression, string identifier); void PushTruePropToObject(Scope scope, string obj, string identifier); void PushFalsePropToObject(Scope scope, string obj, string identifier); @@ -79,5 +79,8 @@ float GetFloat(Scope scope, int id = -1); double GetDouble(Scope scope, int id = -1); string GetString(Scope scope, int id = -1); void* GetPointer(Scope scope, int id = -1); +void Get(Scope scope, string identifier); +bool Eval(Scope scope, string expression); +void EvalNoResult(Scope scope, string expression); #endif \ No newline at end of file diff --git a/casbin/persist.h b/casbin/persist.h index 6f3bb043..1a6e8f97 100644 --- a/casbin/persist.h +++ b/casbin/persist.h @@ -18,7 +18,7 @@ #define CASBIN_CPP_PERSIST #include "./persist/adapter.h" -#include "./persist/adapter_filtered.h" +#include "./persist/filtered_adapter.h" #include "./persist/batch_adapter.h" #include "./persist/default_watcher.h" #include "./persist/default_watcher_ex.h" @@ -27,6 +27,6 @@ #include "./persist/file_adapter/batch_file_adapter.h" #include "./persist/file_adapter/file_adapter.h" -#include "./persist/file_adapter/filtered_adapter.h" +#include "./persist/file_adapter/filtered_file_adapter.h" #endif \ No newline at end of file diff --git a/casbin/persist/adapter.cpp b/casbin/persist/adapter.cpp index 9363572a..16c7e265 100644 --- a/casbin/persist/adapter.cpp +++ b/casbin/persist/adapter.cpp @@ -23,18 +23,19 @@ // LoadPolicyLine loads a text line as a policy rule to model. void LoadPolicyLine(string line, Model* model) { - if(line == "" || line.find("#")==0) { + if(line == "" || line.find("#")==0) return; - } vector tokens = Split(line, ",", -1); - for (int i = 0; i < tokens.size(); i++) { + for (int i = 0; i < tokens.size(); i++) tokens[i] = Trim(tokens[i]); - } string key = tokens[0]; string sec = key.substr(0,1); vector new_tokens(tokens.begin()+1, tokens.end()); - + + if (model->m.find(sec) == model->m.end()) + model->m[sec] = AssertionMap(); + (model->m[sec].assertion_map[key]->policy).push_back(new_tokens); } \ No newline at end of file diff --git a/casbin/persist/file_adapter/file_adapter.cpp b/casbin/persist/file_adapter/file_adapter.cpp index b918d4be..21c4811f 100644 --- a/casbin/persist/file_adapter/file_adapter.cpp +++ b/casbin/persist/file_adapter/file_adapter.cpp @@ -20,9 +20,8 @@ FileAdapter* FileAdapter :: NewAdapter(string file_path) { // LoadPolicy loads all policy rules from the storage. void FileAdapter :: LoadPolicy(Model* model) { - if (this->file_path == "") { + if (this->file_path == "") throw CasbinAdapterException("Invalid file path, file path cannot be empty"); - } this->LoadPolicyFile(model, LoadPolicyLine); } diff --git a/casbin/persist/file_adapter/filtered_adapter.cpp b/casbin/persist/file_adapter/filtered_file_adapter.cpp similarity index 75% rename from casbin/persist/file_adapter/filtered_adapter.cpp rename to casbin/persist/file_adapter/filtered_file_adapter.cpp index 65745327..8f123c68 100644 --- a/casbin/persist/file_adapter/filtered_adapter.cpp +++ b/casbin/persist/file_adapter/filtered_file_adapter.cpp @@ -4,14 +4,14 @@ #include -#include "./filtered_adapter.h" +#include "./filtered_file_adapter.h" #include "../../exception/io_exception.h" #include "../../exception/casbin_adapter_exception.h" #include "../../util/util.h" using namespace std; -bool FilteredAdapter :: filterLine(string line, Filter* filter) { +bool FilteredFileAdapter :: filterLine(string line, Filter* filter) { if (filter == NULL) return false; @@ -29,7 +29,7 @@ bool FilteredAdapter :: filterLine(string line, Filter* filter) { return filterWords(p, filter_slice); } -bool FilteredAdapter :: filterWords(vector line, vector filter) { +bool FilteredFileAdapter :: filterWords(vector line, vector filter) { if (line.size() < filter.size()+1) return true; @@ -44,7 +44,7 @@ bool FilteredAdapter :: filterWords(vector line, vector filter) return skip_line; } -void FilteredAdapter :: loadFilteredPolicyFile(Model* model, Filter* filter, void (*handler)(string, Model*)) { +void FilteredFileAdapter :: loadFilteredPolicyFile(Model* model, Filter* filter, void (*handler)(string, Model*)) { ifstream out_file; try { out_file.open(this->file_path); @@ -66,21 +66,21 @@ void FilteredAdapter :: loadFilteredPolicyFile(Model* model, Filter* filter, voi } // NewFilteredAdapter is the constructor for FilteredAdapter. -FilteredAdapter* FilteredAdapter :: NewFilteredAdapter(string file_path) { - FilteredAdapter* a = new FilteredAdapter; +FilteredFileAdapter* FilteredFileAdapter :: NewFilteredAdapter(string file_path) { + FilteredFileAdapter* a = new FilteredFileAdapter; a->filtered = true; a->file_path = file_path; return a; } // LoadPolicy loads all policy rules from the storage. -void FilteredAdapter :: LoadPolicy(Model* model) { +void FilteredFileAdapter :: LoadPolicy(Model* model) { this->filtered = false; this->FileAdapter::LoadPolicy(model); } // LoadFilteredPolicy loads only policy rules that match the filter. -void FilteredAdapter :: LoadFilteredPolicy(Model* model, Filter* filter) { +void FilteredFileAdapter :: LoadFilteredPolicy(Model* model, Filter* filter) { if (filter == NULL) { this->LoadPolicy(model); } @@ -94,12 +94,12 @@ void FilteredAdapter :: LoadFilteredPolicy(Model* model, Filter* filter) { } // IsFiltered returns true if the loaded policy has been filtered. -bool FilteredAdapter :: IsFiltered() { +bool FilteredFileAdapter :: IsFiltered() { return this->filtered; } // SavePolicy saves all policy rules to the storage. -void FilteredAdapter :: SavePolicy(Model* model) { +void FilteredFileAdapter :: SavePolicy(Model* model) { if (this->filtered) { throw CasbinAdapterException("Cannot save a filtered policy"); } diff --git a/casbin/persist/file_adapter/filtered_adapter.h b/casbin/persist/file_adapter/filtered_file_adapter.h similarity index 73% rename from casbin/persist/file_adapter/filtered_adapter.h rename to casbin/persist/file_adapter/filtered_file_adapter.h index a2e6d193..28f6487a 100644 --- a/casbin/persist/file_adapter/filtered_adapter.h +++ b/casbin/persist/file_adapter/filtered_file_adapter.h @@ -2,16 +2,9 @@ #define CASBIN_CPP_PERSIST_FILE_ADAPTER_FILTERED_ADAPTER #include "file_adapter.h" +#include "../filtered_adapter.h" -// Filter defines the filtering rules for a FilteredAdapter's policy. Empty values -// are ignored, but all others must match the filter. -class Filter{ - public: - vector P; - vector G; -}; - -class FilteredAdapter : public FileAdapter { +class FilteredFileAdapter : public FileAdapter, public FilteredAdapter { private: static bool filterLine(string line, Filter* filter); @@ -23,7 +16,7 @@ class FilteredAdapter : public FileAdapter { public: // NewFilteredAdapter is the constructor for FilteredAdapter. - static FilteredAdapter* NewFilteredAdapter(string file_path); + static FilteredFileAdapter* NewFilteredAdapter(string file_path); // LoadPolicy loads all policy rules from the storage. void LoadPolicy(Model* model); diff --git a/casbin/persist/Adapter_Filtered.h b/casbin/persist/filtered_adapter.h similarity index 75% rename from casbin/persist/Adapter_Filtered.h rename to casbin/persist/filtered_adapter.h index 5d7023e7..70f29e18 100644 --- a/casbin/persist/Adapter_Filtered.h +++ b/casbin/persist/filtered_adapter.h @@ -19,13 +19,20 @@ #include "./adapter.h" +// Filter defines the filtering rules for a FilteredAdapter's policy. Empty values +// are ignored, but all others must match the filter. +class Filter{ + public: + vector P; + vector G; +}; + // FilteredAdapter is the interface for Casbin adapters supporting filtered policies. -class FilteredAdapter : public Adapter { +class FilteredAdapter : virtual public Adapter { public: // LoadFilteredPolicy loads only policy rules that match the filter. - template - void LoadFilteredPolicy(Model model, Filter filter); + void LoadFilteredPolicy(Model* model, Filter* filter); // IsFiltered returns true if the loaded policy has been filtered. virtual bool IsFiltered() = 0; }; diff --git a/casbin/rbac/default_role_manager.cpp b/casbin/rbac/default_role_manager.cpp index 029f4faf..6f09536d 100644 --- a/casbin/rbac/default_role_manager.cpp +++ b/casbin/rbac/default_role_manager.cpp @@ -38,13 +38,13 @@ void Role :: AddRole(Role* role) { void Role :: DeleteRole(Role* role) { for (int i = 0; i < roles.size();i++) { - if (!roles[i]->name.compare(role->name)) + if (roles[i]->name == role->name) roles.erase(roles.begin()+i); } } bool Role :: HasRole(string name, int hierarchy_level) { - if (!this->name.compare(name)) + if (this->name == name) return true; if (hierarchy_level <= 0) @@ -60,7 +60,7 @@ bool Role :: HasRole(string name, int hierarchy_level) { bool Role :: HasDirectRole(string name) { for(int i = 0 ; i < roles.size() ; i++){ - if (!roles[i]->name.compare(name)) + if (roles[i]->name == name) return true; } @@ -68,15 +68,24 @@ bool Role :: HasDirectRole(string name) { } string Role :: ToString() { + if(this->roles.size()==0) + return ""; + string names = ""; + if(this->roles.size() != 1) + names += "("; + for (int i = 0; i < roles.size(); i ++) { Role* role = roles[i]; - if (i == 0) { - names.append(role->name); - } else { - names.append(", " + role->name); - } + if (i == 0) + names += role->name; + else + names += ", " + role->name; } + + if(this->roles.size() != 1) + names += ")"; + return name + " < " + names; } @@ -116,7 +125,7 @@ Role* DefaultRoleManager :: CreateRole(string name) { if (this->matching_func(name, it->first) && name!=it->first) { Role* role1; bool ok1 = this->all_roles.find(it->first) != this->all_roles.end(); - if (!ok) { + if (!ok1) { all_roles[it->first] = Role :: NewRole(it->first); role1 = all_roles[it->first]; } else @@ -145,12 +154,11 @@ DefaultRoleManager* DefaultRoleManager :: NewRoleManager(int max_hierarchy_level // e.BuildRoleLinks must be called after AddMatchingFunc(). // // example: e.GetRoleManager().(*defaultrolemanager.RoleManager).AddMatchingFunc('matcher', util.KeyMatch) -void DefaultRoleManager :: AddMatchingFunc(string name, MatchingFunc fn) { +void DefaultRoleManager :: AddMatchingFunc(MatchingFunc fn) { this->has_pattern = true; this->matching_func = fn; } - /** * clear clears all stored data and resets the role manager to the initial state. */ @@ -208,7 +216,6 @@ bool DefaultRoleManager :: HasLink(string name1, string name2, vector do if (!name1.compare(name2)) return true; - if (!HasRole(name1) || !HasRole(name2)) return false; diff --git a/casbin/rbac/default_role_manager.h b/casbin/rbac/default_role_manager.h index ecb0571b..b3c838c7 100644 --- a/casbin/rbac/default_role_manager.h +++ b/casbin/rbac/default_role_manager.h @@ -75,7 +75,7 @@ class DefaultRoleManager : public RoleManager { // e.BuildRoleLinks must be called after AddMatchingFunc(). // // example: e.GetRoleManager().(*defaultrolemanager.RoleManager).AddMatchingFunc('matcher', util.KeyMatch) - void AddMatchingFunc(string name, MatchingFunc fn); + void AddMatchingFunc(MatchingFunc fn); /** * clear clears all stored data and resets the role manager to the initial state. diff --git a/casbin/util/built_in_functions.cpp b/casbin/util/built_in_functions.cpp index 9cd05e05..82da4eb8 100644 --- a/casbin/util/built_in_functions.cpp +++ b/casbin/util/built_in_functions.cpp @@ -20,6 +20,7 @@ #include +#include "../model/function.h" #include "./built_in_functions.h" #include "../rbac/role_manager.h" #include "./util.h" @@ -37,20 +38,20 @@ ReturnType KeyMatch(Scope scope) { string key1 = GetString(scope, 0); string key2 = GetString(scope, 1); + PushBooleanValue(scope, KeyMatch(key1, key2)); + return RETURN_RESULT; +} + +bool KeyMatch(string key1, string key2) { size_t pos = key2.find("*"); - if (pos == string :: npos) { - PushBooleanValue(scope, key1 == key2); - return RETURN_RESULT; - } + if (pos == string :: npos) + return key1 == key2; - if (key1.length() > pos) { - PushBooleanValue(scope, key1.substr(0, pos) == key2.substr(0, pos)); - return RETURN_RESULT; - } + if (key1.length() > pos) + return key1.substr(0, pos) == key2.substr(0, pos); - PushBooleanValue(scope, key1 == key2.substr(0, pos)); - return RETURN_RESULT; + return key1 == key2.substr(0, pos); } // KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. @@ -59,6 +60,11 @@ ReturnType KeyMatch2(Scope scope) { string key1 = GetString(scope, 0); string key2 = GetString(scope, 1); + PushBooleanValue(scope, KeyMatch2(key1, key2)); + return RETURN_RESULT; +} + +bool KeyMatch2(string key1, string key2) { vector key1_arr = Split(key1, "/"); vector key2_arr = Split(key2, "/"); @@ -99,8 +105,7 @@ ReturnType KeyMatch2(Scope scope) { if(key2_arr[key2_arr.size()-1] != "*") res = false; - PushBooleanValue(scope, res); - return RETURN_RESULT; + return res; } // KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. @@ -109,6 +114,11 @@ ReturnType KeyMatch3(Scope scope) { string key1 = GetString(scope, 0); string key2 = GetString(scope, 1); + PushBooleanValue(scope, KeyMatch3(key1, key2)); + return RETURN_RESULT; +} + +bool KeyMatch3(string key1, string key2) { vector key1_arr = Split(key1, "/"); vector key2_arr = Split(key2, "/"); @@ -150,8 +160,7 @@ ReturnType KeyMatch3(Scope scope) { if(key2_arr[key2_arr.size()-1] != "*") res = false; - PushBooleanValue(scope, res); - return RETURN_RESULT; + return res; } // RegexMatch determines whether key1 matches the pattern of key2 in regular expression. @@ -159,17 +168,26 @@ ReturnType RegexMatch(Scope scope) { string key1 = GetString(scope, 0); string key2 = GetString(scope, 1); - regex regex_s(key2); - PushBooleanValue(scope, regex_match(key1, regex_s)); + PushBooleanValue(scope, RegexMatch(key1, key2)); return RETURN_RESULT; } +bool RegexMatch(string key1, string key2) { + regex regex_s(key2); + return regex_match(key1, regex_s); +} + // IPMatch determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR pattern. // For example, "192.168.2.123" matches "192.168.2.0/24" ReturnType IPMatch(Scope scope) { string ip1 = GetString(scope, 0); string ip2 = GetString(scope, 1); + PushBooleanValue(scope, IPMatch(ip1, ip2)); + return RETURN_RESULT; +} + +bool IPMatch(string ip1, string ip2) { IP objIP1 = parseIP(ip1); if (objIP1.isLegal == false) throw IllegalArgumentException("invalid argument: ip1 in IPMatch() function is not an IP address."); @@ -180,17 +198,15 @@ ReturnType IPMatch(Scope scope) { if (objIP2.isLegal == false) throw IllegalArgumentException("invalid argument: ip1 in IPMatch() function is not an IP address."); - PushBooleanValue(scope, objIP1.Equal(objIP2)); - return RETURN_RESULT; + return objIP1.Equal(objIP2); } - PushBooleanValue(scope, objCIDR.net.contains(objIP1)); - return RETURN_RESULT; + return objCIDR.net.contains(objIP1); } // GFunction is the method of the g(_, _) function. ReturnType GFunction(Scope scope) { - RoleManager *rm; + RoleManager* rm; rm = (RoleManager*)GetPointer(scope, 0); string name1 = GetString(scope, 1); string name2 = GetString(scope, 2); @@ -199,12 +215,12 @@ ReturnType GFunction(Scope scope) { if(rm == NULL) PushBooleanValue(scope, name1 == name2); - else if (len == 2) { + else if (len == 3) { vector domain; bool res = rm->HasLink(name1, name2, domain); PushBooleanValue(scope, res); } else { - vector domain{GetString(scope, 2)}; + vector domain{GetString(scope, 3)}; bool res = rm->HasLink(name1, name2, domain); PushBooleanValue(scope, res); } diff --git a/casbin/util/built_in_functions.h b/casbin/util/built_in_functions.h index c8fcbb1d..fc405b66 100644 --- a/casbin/util/built_in_functions.h +++ b/casbin/util/built_in_functions.h @@ -22,21 +22,26 @@ // KeyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *. // For example, "/foo/bar" matches "/foo/*" ReturnType KeyMatch(Scope scope); +bool KeyMatch(string key1, string key2); // KeyMatch2 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" ReturnType KeyMatch2(Scope scope); +bool KeyMatch2(string key1, string key2); // KeyMatch3 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}" ReturnType KeyMatch3(Scope scope); +bool KeyMatch3(string key1, string key2); // RegexMatch determines whether key1 matches the pattern of key2 in regular expression. ReturnType RegexMatch(Scope scope); +bool RegexMatch(string key1, string key2); // IPMatch determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR pattern. // For example, "192.168.2.123" matches "192.168.2.0/24" ReturnType IPMatch(Scope scope); +bool IPMatch(string ip1, string ip2); // GFunction is the method of the g(_, _) function. ReturnType GFunction(Scope scope); diff --git a/examples/abac_rule_model.conf b/examples/abac_rule_model.conf new file mode 100644 index 00000000..591dd3a6 --- /dev/null +++ b/examples/abac_rule_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub_rule, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = eval(p.sub_rule) && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/abac_rule_policy.csv b/examples/abac_rule_policy.csv new file mode 100644 index 00000000..e3dbc833 --- /dev/null +++ b/examples/abac_rule_policy.csv @@ -0,0 +1,2 @@ +p, r.sub.Age > 18, /data1, read +p, r.sub.Age < 60, /data2, write \ No newline at end of file diff --git a/examples/basic_model_without_spaces.conf b/examples/basic_model_without_spaces.conf new file mode 100644 index 00000000..5452f954 --- /dev/null +++ b/examples/basic_model_without_spaces.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub,obj,act + +[policy_definition] +p = sub,obj,act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/basic_with_root_model.conf b/examples/basic_with_root_model.conf index 8f13907e..d3ec95b5 100644 --- a/examples/basic_with_root_model.conf +++ b/examples/basic_with_root_model.conf @@ -8,4 +8,4 @@ p = sub, obj, act e = some(where (p.eft == allow)) [matchers] -m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root" \ No newline at end of file +m = r.sub == "root" || r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/comment_model.conf b/examples/comment_model.conf new file mode 100644 index 00000000..a4200ebf --- /dev/null +++ b/examples/comment_model.conf @@ -0,0 +1,12 @@ +[request_definition] +r = sub, obj, act ; Request definition + +[policy_definition] +p = sub, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) # This is policy effect. + +# Matchers +[matchers] +m = r.sub == p.sub && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/glob_model.conf b/examples/glob_model.conf new file mode 100644 index 00000000..b16cad49 --- /dev/null +++ b/examples/glob_model.conf @@ -0,0 +1,11 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = r.sub == p.sub && globMatch(r.obj, p.obj) && r.act == p.act \ No newline at end of file diff --git a/examples/glob_policy.csv b/examples/glob_policy.csv new file mode 100644 index 00000000..86c03b07 --- /dev/null +++ b/examples/glob_policy.csv @@ -0,0 +1,4 @@ +p, u1, /foo/*, read +p, u2, /foo*, read +p, u3, /*/foo/*, read +p, u4, *, read \ No newline at end of file diff --git a/examples/rbac_with_all_pattern_model.conf b/examples/rbac_with_all_pattern_model.conf new file mode 100644 index 00000000..045bfa57 --- /dev/null +++ b/examples/rbac_with_all_pattern_model.conf @@ -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 = r.sub == p.sub && g(r.obj, p.obj, r.dom) && r.dom == p.dom && r.act == p.act \ No newline at end of file diff --git a/examples/rbac_with_all_pattern_policy.csv b/examples/rbac_with_all_pattern_policy.csv new file mode 100644 index 00000000..8097be8a --- /dev/null +++ b/examples/rbac_with_all_pattern_policy.csv @@ -0,0 +1,4 @@ +p, alice, domain1, book_group, read +p, alice, domain2, book_group, write + +g, /book/:id, book_group, * \ No newline at end of file diff --git a/examples/rbac_with_domain_pattern_model.conf b/examples/rbac_with_domain_pattern_model.conf new file mode 100644 index 00000000..774e4418 --- /dev/null +++ b/examples/rbac_with_domain_pattern_model.conf @@ -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 \ No newline at end of file diff --git a/examples/rbac_with_domain_pattern_policy.csv b/examples/rbac_with_domain_pattern_policy.csv new file mode 100644 index 00000000..c8abd35e --- /dev/null +++ b/examples/rbac_with_domain_pattern_policy.csv @@ -0,0 +1,7 @@ +p, admin, domain1, data1, read +p, admin, domain1, data1, write +p, admin, domain2, data2, read +p, admin, domain2, data2, write + +g, alice, admin, * +g, bob, admin, domain2 \ No newline at end of file diff --git a/examples/rbac_with_pattern_policy.csv b/examples/rbac_with_pattern_policy.csv index a8c06c74..31316e90 100644 --- a/examples/rbac_with_pattern_policy.csv +++ b/examples/rbac_with_pattern_policy.csv @@ -6,10 +6,11 @@ p, pen_admin, pen_group, GET g, alice, book_admin g, bob, pen_admin -g, /book/*, book_group g, cathy, /book/1/2/3/4/5 g, cathy, pen_admin +g2, /book/*, book_group + g2, /book/:id, book_group g2, /pen/:id, pen_group diff --git a/test/test.vcxproj b/test/test.vcxproj index cd613c9b..e4b0b3f8 100644 --- a/test/test.vcxproj +++ b/test/test.vcxproj @@ -168,6 +168,7 @@ + diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters index 01e9d08f..fc1fde8c 100644 --- a/test/test.vcxproj.filters +++ b/test/test.vcxproj.filters @@ -33,6 +33,9 @@ Source Files + + Source Files + diff --git a/test/test_built_in_functions.cpp b/test/test_built_in_functions.cpp index 32ead58e..5985d106 100644 --- a/test/test_built_in_functions.cpp +++ b/test/test_built_in_functions.cpp @@ -11,6 +11,7 @@ namespace test_built_in_functions TEST_CLASS(TestBuiltInFunctions) { public: + void TestKeyMatch(string key1, string key2, bool res) { Scope scope = InitializeScope(); PushStringValue(scope, key1); diff --git a/test/test_model_enforcer.cpp b/test/test_model_enforcer.cpp new file mode 100644 index 00000000..c280cbf7 --- /dev/null +++ b/test/test_model_enforcer.cpp @@ -0,0 +1,816 @@ +#pragma once + +#include "pch.h" + +#include +#include + +#include +#include +#include +#include + +using namespace std; + +namespace test_model_enforcer +{ + TEST_CLASS(TestModelEnforcer) + { + public: + + string filePath(string filepath) { + char* root = _getcwd(NULL, 0); + string rootStr = string(root); + + vector directories = Split(rootStr, "\\", -1); + vector::iterator it = find(directories.begin(), directories.end(), "x64"); + vector left{ *(it - 1) }; + it = find_end(directories.begin(), directories.end(), left.begin(), left.end()); + int index = int(directories.size() + (it - directories.end())); + + vector finalDirectories(directories.begin(), directories.begin() + index + 1); + + vector userD = Split(filepath, "/", -1); + for (int i = 1; i < userD.size(); i++) + finalDirectories.push_back(userD[i]); + + string filepath1 = finalDirectories[0]; + for (int i = 1; i < finalDirectories.size(); i++) + filepath1 = filepath1 + "/" + finalDirectories[i]; + return filepath1; + } + + Scope InitializeParams(string sub, string obj, string act) { + Scope scope = InitializeScope(); + PushObject(scope, "r"); + PushStringPropToObject(scope, "r", sub, "sub"); + PushStringPropToObject(scope, "r", obj, "obj"); + PushStringPropToObject(scope, "r", act, "act"); + + return scope; + } + + Scope InitializeParamsWithoutUsers(string obj, string act) { + Scope scope = InitializeScope(); + PushObject(scope, "r"); + PushStringPropToObject(scope, "r", obj, "obj"); + PushStringPropToObject(scope, "r", act, "act"); + return scope; + } + + Scope InitializeParamsWithoutResources(string sub, string act) { + Scope scope = InitializeScope(); + PushObject(scope, "r"); + PushStringPropToObject(scope, "r", sub, "sub"); + PushStringPropToObject(scope, "r", act, "act"); + + return scope; + } + + Scope InitializeParamsWithDomains(string sub, string domain, string obj, string act) { + Scope scope = InitializeScope(); + PushObject(scope, "r"); + PushStringPropToObject(scope, "r", sub, "sub"); + PushStringPropToObject(scope, "r", domain, "dom"); + PushStringPropToObject(scope, "r", obj, "obj"); + PushStringPropToObject(scope, "r", act, "act"); + + return scope; + } + + void TestEnforce(Enforcer* e, Scope scope, bool res) { + Assert::AreEqual(res, e->Enforce(scope)); + } + + TEST_METHOD(TestBasicModel) { + string model = filePath("../examples/basic_model.conf"); + string policy = filePath("../examples/basic_policy.csv"); + Enforcer* e = Enforcer :: NewEnforcer(model, policy); + + Scope scope; + + scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestBasicModelWithoutSpaces) { + string model = filePath("../examples/basic_model_without_spaces.conf"); + string policy = filePath("../examples/basic_policy.csv"); + Enforcer* e = Enforcer :: NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestBasicModelNoPolicy) { + string model = filePath("../examples/basic_model.conf"); + Enforcer* e = Enforcer :: NewEnforcer(model); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, false); + } + + TEST_METHOD(TestBasicModelWithRoot) { + string model = filePath("../examples/basic_with_root_model.conf"); + string policy = filePath("../examples/basic_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data1", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestBasicModelWithRootNoPolicy) { + string model = filePath("../examples/basic_with_root_model.conf"); + Enforcer* e = Enforcer::NewEnforcer(model); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("root", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data1", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("root", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestBasicModelWithoutUsers) { + string model = filePath("../examples/basic_without_users_model.conf"); + string policy = filePath("../examples/basic_without_users_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParamsWithoutUsers("data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithoutUsers("data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithoutUsers("data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithoutUsers("data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestBasicModelWithoutResources) { + string model = filePath("../examples/basic_without_resources_model.conf"); + string policy = filePath("../examples/basic_without_resources_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParamsWithoutResources("alice", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithoutResources("alice", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithoutResources("bob", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithoutResources("bob", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModel) { + string model = filePath("../examples/rbac_model.conf"); + string policy = filePath("../examples/rbac_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithResourceRoles) { + string model = filePath("../examples/rbac_with_resource_roles_model.conf"); + string policy = filePath("../examples/rbac_with_resource_roles_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithDomains) { + string model = filePath("../examples/rbac_with_domains_model.conf"); + string policy = filePath("../examples/rbac_with_domains_policy.csv"); + Enforcer* e = Enforcer :: NewEnforcer(model, policy); + + Scope scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "write"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithDomainsAtRuntime) { + string model = filePath("../examples/rbac_with_domains_model.conf"); + Enforcer* e = Enforcer::NewEnforcer(model); + + vector params{ "admin", "domain1", "data1", "read" }; + e->AddPolicy(params); + params = vector{ "admin", "domain1", "data1", "write" }; + e->AddPolicy(params); + params = vector{ "admin", "domain2", "data2", "read" }; + e->AddPolicy(params); + params = vector{ "admin", "domain2", "data2", "write" }; + e->AddPolicy(params); + + params = vector{ "alice", "admin", "domain1" }; + e->AddGroupingPolicy(params); + params = vector{ "bob", "admin", "domain2" }; + e->AddGroupingPolicy(params); + + Scope scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "write"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "write"); + TestEnforce(e, scope, true); + + // Remove all policy rules related to domain1 and data1. + params = vector{ "domain1", "data1" }; + e->RemoveFilteredPolicy(1, params); + + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "write"); + TestEnforce(e, scope, true); + + // Remove the specified policy rule. + params = vector{ "admin", "domain2", "data2", "read" }; + e->RemovePolicy(params); + + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("alice", "domain1", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithDomainsAtRuntimeMockAdapter) { + string model = filePath("../examples/rbac_with_domains_model.conf"); + string policy = filePath("../examples/rbac_with_domains_policy.csv"); + Adapter* adapter = FileAdapter :: NewAdapter(policy); + Enforcer* e = Enforcer :: NewEnforcer(model, adapter); + + vector params{ "admin", "domain3", "data1", "read" }; + e->AddPolicy(params); + params = vector{ "alice", "admin", "domain3" }; + e->AddGroupingPolicy(params); + + Scope scope = InitializeParamsWithDomains("alice", "domain3", "data1", "read"); + TestEnforce(e, scope, true); + + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, true); + params = vector{ "domain1", "data1" }; + e->RemoveFilteredPolicy(1, params); + scope = InitializeParamsWithDomains("alice", "domain1", "data1", "read"); + TestEnforce(e, scope, false); + + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, true); + params = vector{ "admin", "domain2", "data2", "read" }; + e->RemovePolicy(params); + scope = InitializeParamsWithDomains("bob", "domain2", "data2", "read"); + TestEnforce(e, scope, false); + } + + TEST_METHOD(TestRBACModelWithDeny) { + string model = filePath("../examples/rbac_with_deny_model.conf"); + string policy = filePath("../examples/rbac_with_deny_policy.csv"); + Enforcer* e = Enforcer :: NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithOnlyDeny) { + string model = filePath("../examples/rbac_with_not_deny_model.conf"); + string policy = filePath("../examples/rbac_with_deny_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + Scope scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, false); + } + + TEST_METHOD(TestRBACModelWithCustomData) { + string model = filePath("../examples/rbac_model.conf"); + string policy = filePath("../examples/rbac_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + // You can add custom data to a grouping policy, Casbin will ignore it. It is only meaningful to the caller. + // This feature can be used to store information like whether "bob" is an end user (so no subject will inherit "bob") + // For Casbin, it is equivalent to: e.AddGroupingPolicy("bob", "data2_admin") + vector params{ "bob", "data2_admin", "custom_data" }; + e->AddGroupingPolicy(params); + + Scope scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + + // You should also take the custom data as a parameter when deleting a grouping policy. + // e.RemoveGroupingPolicy("bob", "data2_admin") won't work. + // Or you can remove it by using RemoveFilteredGroupingPolicy(). + params = vector{ "bob", "data2_admin", "custom_data" }; + e->RemoveGroupingPolicy(params); + + scope = InitializeParams("alice", "data1", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("alice", "data2", "read"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "data2", "write"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "data1", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data1", "write"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "read"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "data2", "write"); + TestEnforce(e, scope, true); + } + + TEST_METHOD(TestRBACModelWithPattern) { + string model = filePath("../examples/rbac_with_pattern_model.conf"); + string policy = filePath("../examples/rbac_with_pattern_policy.csv"); + Enforcer* e = Enforcer::NewEnforcer(model, policy); + + // Here's a little confusing: the matching function here is not the custom function used in matcher. + // It is the matching function used by "g" (and "g2", "g3" if any..) + // You can see in policy that: "g2, /book/:id, book_group", so in "g2()" function in the matcher, instead + // of checking whether "/book/:id" equals the obj: "/book/1", it checks whether the pattern matches. + // You can see it as normal RBAC: "/book/:id" == "/book/1" becomes KeyMatch2("/book/:id", "/book/1") + DefaultRoleManager* rm_tmp = (DefaultRoleManager*)e->rm; + rm_tmp->AddMatchingFunc(KeyMatch2); + Scope scope = InitializeParams("alice", "/book/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/book/2", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/pen/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/pen/2", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/book/1", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/book/2", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/pen/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "/pen/2", "GET"); + TestEnforce(e, scope, true); + + // AddMatchingFunc() is actually setting a function because only one function is allowed, + // so when we set "KeyMatch3", we are actually replacing "KeyMatch2" with "KeyMatch3". + rm_tmp->AddMatchingFunc(KeyMatch3); + scope = InitializeParams("alice", "/book2/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/book2/2", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/pen2/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("alice", "/pen2/2", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/book2/1", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/book2/2", "GET"); + TestEnforce(e, scope, false); + scope = InitializeParams("bob", "/pen2/1", "GET"); + TestEnforce(e, scope, true); + scope = InitializeParams("bob", "/pen2/2", "GET"); + TestEnforce(e, scope, true); + } + /* + type testCustomRoleManager struct {} + + func NewRoleManager() rbac.RoleManager{ + return &testCustomRoleManager{} + } + func(rm* testCustomRoleManager) Clear() error { return nil } + func(rm* testCustomRoleManager) AddLink(name1 string, name2 string, domain ...string) error { + return nil + } + func(rm* testCustomRoleManager) DeleteLink(name1 string, name2 string, domain ...string) error { + return nil + } + func(rm* testCustomRoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) { + if name1 == "alice" && name2 == "alice" { + return true, nil + } + else if name1 == "alice" && name2 == "data2_admin" { + return true, nil + } + else if name1 == "bob" && name2 == "bob" { + return true, nil + } + return false, nil + } + func(rm* testCustomRoleManager) GetRoles(name string, domain ...string) ([]string, error) { + return[]string{}, nil + } + func(rm* testCustomRoleManager) GetUsers(name string, domain ...string) ([]string, error) { + return[]string{}, nil + } + func(rm* testCustomRoleManager) PrintRoles() error { return nil } + + func TestRBACModelWithCustomRoleManager(t* testing.T) { + e, _ : = NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv") + e.SetRoleManager(NewRoleManager()) + e.LoadModel() + _ = e.LoadPolicy() + + TestEnforce(e, "alice", "data1", "read", true) + TestEnforce(e, "alice", "data1", "write", false) + TestEnforce(e, "alice", "data2", "read", true) + TestEnforce(e, "alice", "data2", "write", true) + TestEnforce(e, "bob", "data1", "read", false) + TestEnforce(e, "bob", "data1", "write", false) + TestEnforce(e, "bob", "data2", "read", false) + TestEnforce(e, "bob", "data2", "write", true) + } + + type testResource struct { + Name string + Owner string + } + + func newTestResource(name string, owner string) testResource { + r: = testResource{} + r.Name = name + r.Owner = owner + return r + } + + func TestABACModel(t* testing.T) { + e, _ : = NewEnforcer("examples/abac_model.conf") + + data1 : = newTestResource("data1", "alice") + data2 : = newTestResource("data2", "bob") + + TestEnforce(e, "alice", data1, "read", true) + TestEnforce(e, "alice", data1, "write", true) + TestEnforce(e, "alice", data2, "read", false) + TestEnforce(e, "alice", data2, "write", false) + TestEnforce(e, "bob", data1, "read", false) + TestEnforce(e, "bob", data1, "write", false) + TestEnforce(e, "bob", data2, "read", true) + TestEnforce(e, "bob", data2, "write", true) + } + + func TestKeyMatchModel(t* testing.T) { + e, _ : = NewEnforcer("examples/keymatch_model.conf", "examples/keymatch_policy.csv") + + TestEnforce(e, "alice", "/alice_data/resource1", "GET", true) + TestEnforce(e, "alice", "/alice_data/resource1", "POST", true) + TestEnforce(e, "alice", "/alice_data/resource2", "GET", true) + TestEnforce(e, "alice", "/alice_data/resource2", "POST", false) + TestEnforce(e, "alice", "/bob_data/resource1", "GET", false) + TestEnforce(e, "alice", "/bob_data/resource1", "POST", false) + TestEnforce(e, "alice", "/bob_data/resource2", "GET", false) + TestEnforce(e, "alice", "/bob_data/resource2", "POST", false) + + TestEnforce(e, "bob", "/alice_data/resource1", "GET", false) + TestEnforce(e, "bob", "/alice_data/resource1", "POST", false) + TestEnforce(e, "bob", "/alice_data/resource2", "GET", true) + TestEnforce(e, "bob", "/alice_data/resource2", "POST", false) + TestEnforce(e, "bob", "/bob_data/resource1", "GET", false) + TestEnforce(e, "bob", "/bob_data/resource1", "POST", true) + TestEnforce(e, "bob", "/bob_data/resource2", "GET", false) + TestEnforce(e, "bob", "/bob_data/resource2", "POST", true) + + TestEnforce(e, "cathy", "/cathy_data", "GET", true) + TestEnforce(e, "cathy", "/cathy_data", "POST", true) + TestEnforce(e, "cathy", "/cathy_data", "DELETE", false) + } + + func TestKeyMatch2Model(t* testing.T) { + e, _ : = NewEnforcer("examples/keymatch2_model.conf", "examples/keymatch2_policy.csv") + + TestEnforce(e, "alice", "/alice_data", "GET", false) + TestEnforce(e, "alice", "/alice_data/resource1", "GET", true) + TestEnforce(e, "alice", "/alice_data2/myid", "GET", false) + TestEnforce(e, "alice", "/alice_data2/myid/using/res_id", "GET", true) + } + + func CustomFunction(key1 string, key2 string) bool{ + if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data/:resource" { + return true + } + else if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data2/:id/using/:resId" { + return true + } + else { + return false + } + } + + func CustomFunctionWrapper(args ...interface {}) (interface {}, error) { + key1: = args[0].(string) + key2 : = args[1].(string) + + return bool(CustomFunction(key1, key2)), nil + } + + func TestKeyMatchCustomModel(t* testing.T) { + e, _ : = NewEnforcer("examples/keymatch_custom_model.conf", "examples/keymatch2_policy.csv") + + e.AddFunction("keyMatchCustom", CustomFunctionWrapper) + + TestEnforce(e, "alice", "/alice_data2/myid", "GET", false) + TestEnforce(e, "alice", "/alice_data2/myid/using/res_id", "GET", true) + } + + func TestIPMatchModel(t* testing.T) { + e, _ : = NewEnforcer("examples/ipmatch_model.conf", "examples/ipmatch_policy.csv") + + TestEnforce(e, "192.168.2.123", "data1", "read", true) + TestEnforce(e, "192.168.2.123", "data1", "write", false) + TestEnforce(e, "192.168.2.123", "data2", "read", false) + TestEnforce(e, "192.168.2.123", "data2", "write", false) + + TestEnforce(e, "192.168.0.123", "data1", "read", false) + TestEnforce(e, "192.168.0.123", "data1", "write", false) + TestEnforce(e, "192.168.0.123", "data2", "read", false) + TestEnforce(e, "192.168.0.123", "data2", "write", false) + + TestEnforce(e, "10.0.0.5", "data1", "read", false) + TestEnforce(e, "10.0.0.5", "data1", "write", false) + TestEnforce(e, "10.0.0.5", "data2", "read", false) + TestEnforce(e, "10.0.0.5", "data2", "write", true) + + TestEnforce(e, "192.168.0.1", "data1", "read", false) + TestEnforce(e, "192.168.0.1", "data1", "write", false) + TestEnforce(e, "192.168.0.1", "data2", "read", false) + TestEnforce(e, "192.168.0.1", "data2", "write", false) + } + + func TestGlobMatchModel(t* testing.T) { + e, _ : = NewEnforcer("examples/glob_model.conf", "examples/glob_policy.csv") + TestEnforce(e, "u1", "/foo/", "read", true) + TestEnforce(e, "u1", "/foo", "read", false) + TestEnforce(e, "u1", "/foo/subprefix", "read", true) + TestEnforce(e, "u1", "foo", "read", false) + + TestEnforce(e, "u2", "/foosubprefix", "read", true) + TestEnforce(e, "u2", "/foo/subprefix", "read", false) + TestEnforce(e, "u2", "foo", "read", false) + + TestEnforce(e, "u3", "/prefix/foo/subprefix", "read", true) + TestEnforce(e, "u3", "/prefix/foo/", "read", true) + TestEnforce(e, "u3", "/prefix/foo", "read", false) + + TestEnforce(e, "u4", "/foo", "read", false) + TestEnforce(e, "u4", "foo", "read", true) + } + + func TestPriorityModel(t* testing.T) { + e, _ : = NewEnforcer("examples/priority_model.conf", "examples/priority_policy.csv") + + TestEnforce(e, "alice", "data1", "read", true) + TestEnforce(e, "alice", "data1", "write", false) + TestEnforce(e, "alice", "data2", "read", false) + TestEnforce(e, "alice", "data2", "write", false) + TestEnforce(e, "bob", "data1", "read", false) + TestEnforce(e, "bob", "data1", "write", false) + TestEnforce(e, "bob", "data2", "read", true) + TestEnforce(e, "bob", "data2", "write", false) + } + + func TestPriorityModelIndeterminate(t* testing.T) { + e, _ : = NewEnforcer("examples/priority_model.conf", "examples/priority_indeterminate_policy.csv") + + TestEnforce(e, "alice", "data1", "read", false) + } + + func TestRBACModelInMultiLines(t* testing.T) { + e, _ : = NewEnforcer("examples/rbac_model_in_multi_line.conf", "examples/rbac_policy.csv") + + TestEnforce(e, "alice", "data1", "read", true) + TestEnforce(e, "alice", "data1", "write", false) + TestEnforce(e, "alice", "data2", "read", true) + TestEnforce(e, "alice", "data2", "write", true) + TestEnforce(e, "bob", "data1", "read", false) + TestEnforce(e, "bob", "data1", "write", false) + TestEnforce(e, "bob", "data2", "read", false) + TestEnforce(e, "bob", "data2", "write", true) + } + + type testSub struct { + Name string + Age int + } + + func newTestSubject(name string, age int) testSub { + s: = testSub{} + s.Name = name + s.Age = age + return s + } + + func TestABACPolicy(t* testing.T) { + e, _ : = NewEnforcer("examples/abac_rule_model.conf", "examples/abac_rule_policy.csv") + sub1 : = newTestSubject("alice", 16) + sub2 : = newTestSubject("alice", 20) + sub3 : = newTestSubject("alice", 65) + + TestEnforce(e, sub1, "/data1", "read", false) + TestEnforce(e, sub1, "/data2", "read", false) + TestEnforce(e, sub1, "/data1", "write", false) + TestEnforce(e, sub1, "/data2", "write", true) + TestEnforce(e, sub2, "/data1", "read", true) + TestEnforce(e, sub2, "/data2", "read", false) + TestEnforce(e, sub2, "/data1", "write", false) + TestEnforce(e, sub2, "/data2", "write", true) + TestEnforce(e, sub3, "/data1", "read", true) + TestEnforce(e, sub3, "/data2", "read", false) + TestEnforce(e, sub3, "/data1", "write", false) + TestEnforce(e, sub3, "/data2", "write", false) + } + + func TestCommentModel(t* testing.T) { + e, _ : = NewEnforcer("examples/comment_model.conf", "examples/basic_policy.csv") + + TestEnforce(e, "alice", "data1", "read", true) + TestEnforce(e, "alice", "data1", "write", false) + TestEnforce(e, "alice", "data2", "read", false) + TestEnforce(e, "alice", "data2", "write", false) + TestEnforce(e, "bob", "data1", "read", false) + TestEnforce(e, "bob", "data1", "write", false) + TestEnforce(e, "bob", "data2", "read", false) + TestEnforce(e, "bob", "data2", "write", true) + } + */ + }; +} \ No newline at end of file diff --git a/test/test_role_manager.cpp b/test/test_role_manager.cpp index 5dfafa5e..36a29308 100644 --- a/test/test_role_manager.cpp +++ b/test/test_role_manager.cpp @@ -11,173 +11,174 @@ namespace test_role_manager TEST_CLASS(TestRoleManager) { public: - void TestRole(RoleManager* rm, string name1, string name2, bool res) { - bool my_res = rm->HasLink(name1, name2); - - Assert::AreEqual(res, my_res); - } - - void TestDomainRole(RoleManager* rm, string name1, string name2, vector domain, bool res) { - bool my_res = rm->HasLink(name1, name2, domain); - - Assert::AreEqual(res, my_res); - } - - TEST_METHOD(TestRole) { - RoleManager* rm = DefaultRoleManager :: NewRoleManager(3); - rm->AddLink("u1", "g1"); - rm->AddLink("u2", "g1"); - rm->AddLink("u3", "g2"); - rm->AddLink("u4", "g2"); - rm->AddLink("u4", "g3"); - rm->AddLink("g1", "g3"); - - // Current role inheritance tree: - // g3 g2 - // / \ / \ - // g1 u4 u3 - // / \ - // u1 u2 - - TestRole(rm, "u1", "g1", true); - TestRole(rm, "u1", "g2", false); - TestRole(rm, "u1", "g3", true); - TestRole(rm, "u2", "g1", true); - TestRole(rm, "u2", "g2", false); - TestRole(rm, "u2", "g3", true); - TestRole(rm, "u3", "g1", false); - TestRole(rm, "u3", "g2", true); - TestRole(rm, "u3", "g3", false); - TestRole(rm, "u4", "g1", false); - TestRole(rm, "u4", "g2", true); - TestRole(rm, "u4", "g3", true); - - rm->DeleteLink("g1", "g3"); - rm->DeleteLink("u4", "g2"); - - // Current role inheritance tree after deleting the links: - // g3 g2 - // \ \ - // g1 u4 u3 - // / \ - // u1 u2 - - TestRole(rm, "u1", "g1", true); - TestRole(rm, "u1", "g2", false); - TestRole(rm, "u1", "g3", false); - TestRole(rm, "u2", "g1", true); - TestRole(rm, "u2", "g2", false); - TestRole(rm, "u2", "g3", false); - TestRole(rm, "u3", "g1", false); - TestRole(rm, "u3", "g2", true); - TestRole(rm, "u3", "g3", false); - TestRole(rm, "u4", "g1", false); - TestRole(rm, "u4", "g2", false); - TestRole(rm, "u4", "g3", true); - } - - TEST_METHOD(TestDomainRole) { - RoleManager* rm = DefaultRoleManager :: NewRoleManager(3); - vector domain1{ "domain1" }; - vector domain2{ "domain2" }; - rm->AddLink("u1", "g1", domain1); - rm->AddLink("u2", "g1", domain1); - rm->AddLink("u3", "admin", domain2); - rm->AddLink("u4", "admin", domain2); - rm->AddLink("u4", "admin", domain1); - rm->AddLink("g1", "admin", domain1); - - // Current role inheritance tree: - // domain1:admin domain2:admin - // / \ / \ - // domain1:g1 u4 u3 - // / \ - // u1 u2 - - TestDomainRole(rm, "u1", "g1", domain1, true); - TestDomainRole(rm, "u1", "g1", domain2, false); - TestDomainRole(rm, "u1", "admin", domain1, true); - TestDomainRole(rm, "u1", "admin", domain2, false); - - TestDomainRole(rm, "u2", "g1", domain1, true); - TestDomainRole(rm, "u2", "g1", domain2, false); - TestDomainRole(rm, "u2", "admin", domain1, true); - TestDomainRole(rm, "u2", "admin", domain2, false); - - TestDomainRole(rm, "u3", "g1", domain1, false); - TestDomainRole(rm, "u3", "g1", domain2, false); - TestDomainRole(rm, "u3", "admin", domain1, false); - TestDomainRole(rm, "u3", "admin", domain2, true); - - TestDomainRole(rm, "u4", "g1", domain1, false); - TestDomainRole(rm, "u4", "g1", domain2, false); - TestDomainRole(rm, "u4", "admin", domain1, true); - TestDomainRole(rm, "u4", "admin", domain2, true); - - rm->DeleteLink("g1", "admin", domain1); - rm->DeleteLink("u4", "admin", domain2); - - // Current role inheritance tree after deleting the links: - // domain1:admin domain2:admin - // \ \ - // domain1:g1 u4 u3 - // / \ - // u1 u2 - - TestDomainRole(rm, "u1", "g1", domain1, true); - TestDomainRole(rm, "u1", "g1", domain2, false); - TestDomainRole(rm, "u1", "admin", domain1, false); - TestDomainRole(rm, "u1", "admin", domain2, false); - - TestDomainRole(rm, "u2", "g1", domain1, true); - TestDomainRole(rm, "u2", "g1", domain2, false); - TestDomainRole(rm, "u2", "admin", domain1, false); - TestDomainRole(rm, "u2", "admin", domain2, false); - - TestDomainRole(rm, "u3", "g1", domain1, false); - TestDomainRole(rm, "u3", "g1", domain2, false); - TestDomainRole(rm, "u3", "admin", domain1, false); - TestDomainRole(rm, "u3", "admin", domain2, true); - - TestDomainRole(rm, "u4", "g1", domain1, false); - TestDomainRole(rm, "u4", "g1", domain2, false); - TestDomainRole(rm, "u4", "admin", domain1, true); - TestDomainRole(rm, "u4", "admin", domain2, false); - } - - TEST_METHOD(TestClear) { - RoleManager* rm = DefaultRoleManager::NewRoleManager(3); - rm->AddLink("u1", "g1"); - rm->AddLink("u2", "g1"); - rm->AddLink("u3", "g2"); - rm->AddLink("u4", "g2"); - rm->AddLink("u4", "g3"); - rm->AddLink("g1", "g3"); - - // Current role inheritance tree: - // g3 g2 - // / \ / \ - // g1 u4 u3 - // / \ - // u1 u2 - - rm->Clear(); - - // All data is cleared. - // No role inheritance now. - - TestRole(rm, "u1", "g1", false); - TestRole(rm, "u1", "g2", false); - TestRole(rm, "u1", "g3", false); - TestRole(rm, "u2", "g1", false); - TestRole(rm, "u2", "g2", false); - TestRole(rm, "u2", "g3", false); - TestRole(rm, "u3", "g1", false); - TestRole(rm, "u3", "g2", false); - TestRole(rm, "u3", "g3", false); - TestRole(rm, "u4", "g1", false); - TestRole(rm, "u4", "g2", false); - TestRole(rm, "u4", "g3", false); - } + + void TestRole(RoleManager* rm, string name1, string name2, bool res) { + bool my_res = rm->HasLink(name1, name2); + + Assert::AreEqual(res, my_res); + } + + void TestDomainRole(RoleManager* rm, string name1, string name2, vector domain, bool res) { + bool my_res = rm->HasLink(name1, name2, domain); + + Assert::AreEqual(res, my_res); + } + + TEST_METHOD(TestRole) { + RoleManager* rm = DefaultRoleManager :: NewRoleManager(3); + rm->AddLink("u1", "g1"); + rm->AddLink("u2", "g1"); + rm->AddLink("u3", "g2"); + rm->AddLink("u4", "g2"); + rm->AddLink("u4", "g3"); + rm->AddLink("g1", "g3"); + + // Current role inheritance tree: + // g3 g2 + // / \ / \ + // g1 u4 u3 + // / \ + // u1 u2 + + TestRole(rm, "u1", "g1", true); + TestRole(rm, "u1", "g2", false); + TestRole(rm, "u1", "g3", true); + TestRole(rm, "u2", "g1", true); + TestRole(rm, "u2", "g2", false); + TestRole(rm, "u2", "g3", true); + TestRole(rm, "u3", "g1", false); + TestRole(rm, "u3", "g2", true); + TestRole(rm, "u3", "g3", false); + TestRole(rm, "u4", "g1", false); + TestRole(rm, "u4", "g2", true); + TestRole(rm, "u4", "g3", true); + + rm->DeleteLink("g1", "g3"); + rm->DeleteLink("u4", "g2"); + + // Current role inheritance tree after deleting the links: + // g3 g2 + // \ \ + // g1 u4 u3 + // / \ + // u1 u2 + + TestRole(rm, "u1", "g1", true); + TestRole(rm, "u1", "g2", false); + TestRole(rm, "u1", "g3", false); + TestRole(rm, "u2", "g1", true); + TestRole(rm, "u2", "g2", false); + TestRole(rm, "u2", "g3", false); + TestRole(rm, "u3", "g1", false); + TestRole(rm, "u3", "g2", true); + TestRole(rm, "u3", "g3", false); + TestRole(rm, "u4", "g1", false); + TestRole(rm, "u4", "g2", false); + TestRole(rm, "u4", "g3", true); + } + + TEST_METHOD(TestDomainRole) { + RoleManager* rm = DefaultRoleManager :: NewRoleManager(3); + vector domain1{ "domain1" }; + vector domain2{ "domain2" }; + rm->AddLink("u1", "g1", domain1); + rm->AddLink("u2", "g1", domain1); + rm->AddLink("u3", "admin", domain2); + rm->AddLink("u4", "admin", domain2); + rm->AddLink("u4", "admin", domain1); + rm->AddLink("g1", "admin", domain1); + + // Current role inheritance tree: + // domain1:admin domain2:admin + // / \ / \ + // domain1:g1 u4 u3 + // / \ + // u1 u2 + + TestDomainRole(rm, "u1", "g1", domain1, true); + TestDomainRole(rm, "u1", "g1", domain2, false); + TestDomainRole(rm, "u1", "admin", domain1, true); + TestDomainRole(rm, "u1", "admin", domain2, false); + + TestDomainRole(rm, "u2", "g1", domain1, true); + TestDomainRole(rm, "u2", "g1", domain2, false); + TestDomainRole(rm, "u2", "admin", domain1, true); + TestDomainRole(rm, "u2", "admin", domain2, false); + + TestDomainRole(rm, "u3", "g1", domain1, false); + TestDomainRole(rm, "u3", "g1", domain2, false); + TestDomainRole(rm, "u3", "admin", domain1, false); + TestDomainRole(rm, "u3", "admin", domain2, true); + + TestDomainRole(rm, "u4", "g1", domain1, false); + TestDomainRole(rm, "u4", "g1", domain2, false); + TestDomainRole(rm, "u4", "admin", domain1, true); + TestDomainRole(rm, "u4", "admin", domain2, true); + + rm->DeleteLink("g1", "admin", domain1); + rm->DeleteLink("u4", "admin", domain2); + + // Current role inheritance tree after deleting the links: + // domain1:admin domain2:admin + // \ \ + // domain1:g1 u4 u3 + // / \ + // u1 u2 + + TestDomainRole(rm, "u1", "g1", domain1, true); + TestDomainRole(rm, "u1", "g1", domain2, false); + TestDomainRole(rm, "u1", "admin", domain1, false); + TestDomainRole(rm, "u1", "admin", domain2, false); + + TestDomainRole(rm, "u2", "g1", domain1, true); + TestDomainRole(rm, "u2", "g1", domain2, false); + TestDomainRole(rm, "u2", "admin", domain1, false); + TestDomainRole(rm, "u2", "admin", domain2, false); + + TestDomainRole(rm, "u3", "g1", domain1, false); + TestDomainRole(rm, "u3", "g1", domain2, false); + TestDomainRole(rm, "u3", "admin", domain1, false); + TestDomainRole(rm, "u3", "admin", domain2, true); + + TestDomainRole(rm, "u4", "g1", domain1, false); + TestDomainRole(rm, "u4", "g1", domain2, false); + TestDomainRole(rm, "u4", "admin", domain1, true); + TestDomainRole(rm, "u4", "admin", domain2, false); + } + + TEST_METHOD(TestClear) { + RoleManager* rm = DefaultRoleManager::NewRoleManager(3); + rm->AddLink("u1", "g1"); + rm->AddLink("u2", "g1"); + rm->AddLink("u3", "g2"); + rm->AddLink("u4", "g2"); + rm->AddLink("u4", "g3"); + rm->AddLink("g1", "g3"); + + // Current role inheritance tree: + // g3 g2 + // / \ / \ + // g1 u4 u3 + // / \ + // u1 u2 + + rm->Clear(); + + // All data is cleared. + // No role inheritance now. + + TestRole(rm, "u1", "g1", false); + TestRole(rm, "u1", "g2", false); + TestRole(rm, "u1", "g3", false); + TestRole(rm, "u2", "g1", false); + TestRole(rm, "u2", "g2", false); + TestRole(rm, "u2", "g3", false); + TestRole(rm, "u3", "g1", false); + TestRole(rm, "u3", "g2", false); + TestRole(rm, "u3", "g3", false); + TestRole(rm, "u4", "g1", false); + TestRole(rm, "u4", "g2", false); + TestRole(rm, "u4", "g3", false); + } }; } \ No newline at end of file