From 9182f026a797b3581c1f0a25bcc8548937d3af7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B1=BC=E7=AB=BF=E9=92=93=E9=B1=BC=E5=B9=B2?= <46661603+PokIsemaine@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:01:22 +0800 Subject: [PATCH] fix: Multithread usage of SyncedEnforcer::Enforce (#227) --- casbin/enforcer_synced.cpp | 36 +++-- include/casbin/enforcer.h | 208 ++++++++++++------------ include/casbin/enforcer_synced.h | 114 +++++++------- tests/enforcer_synced_test.cpp | 261 +++++++++++++++++++++++++++++++ 4 files changed, 448 insertions(+), 171 deletions(-) diff --git a/casbin/enforcer_synced.cpp b/casbin/enforcer_synced.cpp index 12710eb6..b09c3313 100644 --- a/casbin/enforcer_synced.cpp +++ b/casbin/enforcer_synced.cpp @@ -170,19 +170,19 @@ void SyncedEnforcer ::LoadFilteredPolicy(Filter f) { // SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. void SyncedEnforcer ::SavePolicy() { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); Enforcer::SavePolicy(); } // BuildRoleLinks manually rebuild the role inheritance relations. void SyncedEnforcer ::BuildRoleLinks() { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); Enforcer::BuildRoleLinks(); } // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). bool SyncedEnforcer ::Enforce(std::shared_ptr evalator) { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); return Enforcer::Enforce(evalator); } @@ -190,7 +190,7 @@ bool SyncedEnforcer ::Enforce(std::shared_ptr evalator) { // "object" with the operation "action", input parameters are usually: (sub, // obj, act). bool SyncedEnforcer::Enforce(const DataVector& params) { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); return Enforcer::Enforce(params); } @@ -198,27 +198,43 @@ bool SyncedEnforcer::Enforce(const DataVector& params) { // "object" with the operation "action", input parameters are usually: (sub, // obj, act). bool SyncedEnforcer ::Enforce(const DataList& params) { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); return Enforcer::Enforce(params); } // Enforce with a map param,decides whether a "subject" can access a "object" // with the operation "action", input parameters are usually: (sub, obj, act). bool SyncedEnforcer ::Enforce(const DataMap& params) { - std::shared_lock lock(policyMutex); + std::unique_lock lock(policyMutex); return Enforcer::Enforce(params); } // BatchEnforce enforce in batches std::vector SyncedEnforcer ::BatchEnforce(const std::initializer_list& requests) { - std::shared_lock lock(policyMutex); - return Enforcer::BatchEnforce(requests); + std::unique_lock lock(policyMutex); + + // note: why not return Enforcer::BatchEnforce(requests) ? + // Inside Enforcer::BatchEnforce, this->Enforce will be executed + // but now 'this' is SyncedEnforcer, which means it will call SyncedEnforcer::Enforce + // This will cause a deadlock + + std::vector results; + results.reserve(requests.size()); + for (const auto& request : requests) { + results.push_back(Enforcer::Enforce(request)); + } + return results; } // BatchEnforceWithMatcher enforce with matcher in batches std::vector SyncedEnforcer::BatchEnforceWithMatcher(const std::string& matcher, const std::initializer_list& requests) { - std::shared_lock lock(policyMutex); - return Enforcer::BatchEnforceWithMatcher(matcher, requests); + std::unique_lock lock(policyMutex); + std::vector results; + results.reserve(requests.size()); + for (const auto& request : requests) { + results.push_back(Enforcer::EnforceWithMatcher(matcher, request)); + } + return results; } // GetAllSubjects gets the list of subjects that show up in the current policy. diff --git a/include/casbin/enforcer.h b/include/casbin/enforcer.h index 3ceee2b2..527aa925 100644 --- a/include/casbin/enforcer.h +++ b/include/casbin/enforcer.h @@ -102,71 +102,71 @@ class Enforcer : public IEnforcer { // Destructor of Enforcer. ~Enforcer(); // InitWithFile initializes an enforcer with a model file and a policy file. - void InitWithFile(const std::string& model_path, const std::string& policy_path); + void InitWithFile(const std::string& model_path, const std::string& policy_path) override; // InitWithAdapter initializes an enforcer with a database adapter. - void InitWithAdapter(const std::string& model_path, std::shared_ptr adapter); + void InitWithAdapter(const std::string& model_path, std::shared_ptr adapter) override; // InitWithModelAndAdapter initializes an enforcer with a model and a database adapter. - void InitWithModelAndAdapter(const std::shared_ptr& m, std::shared_ptr adapter); - void Initialize(); + void InitWithModelAndAdapter(const std::shared_ptr& m, std::shared_ptr adapter) override; + void Initialize() override; // LoadModel reloads the model from the model CONF file. // Because the policy is attached to a model, so the policy is invalidated and // needs to be reloaded by calling LoadPolicy(). - void LoadModel(); + void LoadModel() override; // GetModel gets the current model. - std::shared_ptr GetModel(); + std::shared_ptr GetModel() override; // SetModel sets the current model. - void SetModel(const std::shared_ptr& m); + void SetModel(const std::shared_ptr& m) override; // GetAdapter gets the current adapter. - std::shared_ptr GetAdapter(); + std::shared_ptr GetAdapter() override; // SetAdapter sets the current adapter. - void SetAdapter(std::shared_ptr adapter); + void SetAdapter(std::shared_ptr adapter) override; // SetWatcher sets the current watcher. - void SetWatcher(std::shared_ptr watcher); + void SetWatcher(std::shared_ptr watcher) override; // SetWatcher sets the current watcher. void SetEvaluator(std::shared_ptr evaluator); // GetRoleManager gets the current role manager. - std::shared_ptr GetRoleManager(); + std::shared_ptr GetRoleManager() override; // SetRoleManager sets the current role manager. - void SetRoleManager(std::shared_ptr& rm); + void SetRoleManager(std::shared_ptr& rm) override; // SetEffector sets the current effector. - void SetEffector(std::shared_ptr eft); + void SetEffector(std::shared_ptr eft) override; // ClearPolicy clears all policy. - void ClearPolicy(); + void ClearPolicy() override; // LoadPolicy reloads the policy from file/database. - void LoadPolicy(); + void LoadPolicy() override; // LoadFilteredPolicy reloads a filtered policy from file/database. template void LoadFilteredPolicy(Filter filter); // IsFiltered returns true if the loaded policy has been filtered. - bool IsFiltered(); + bool IsFiltered() override; // SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. - void SavePolicy(); + void SavePolicy() override; // EnableEnforce changes the enforcing state of Casbin, when Casbin is disabled, all access will be allowed by the Enforce() function. - void EnableEnforce(bool enable); + void EnableEnforce(bool enable) override; // EnableLog changes whether Casbin will log messages to the Logger. void EnableLog(bool enable); // EnableAutoNotifyWatcher controls whether to save a policy rule automatically notify the Watcher when it is added or removed. - void EnableAutoNotifyWatcher(bool enable); + void EnableAutoNotifyWatcher(bool enable) override; // EnableAutoSave controls whether to save a policy rule automatically to the adapter when it is added or removed. - void EnableAutoSave(bool auto_save); + void EnableAutoSave(bool auto_save) override; // EnableAutoBuildRoleLinks controls whether to rebuild the role inheritance relations when a role is added or deleted. - void EnableAutoBuildRoleLinks(bool auto_build_role_links); + void EnableAutoBuildRoleLinks(bool auto_build_role_links) override; // BuildRoleLinks manually rebuild the role inheritance relations. - void BuildRoleLinks(); + void BuildRoleLinks() override; // BuildIncrementalRoleLinks provides incremental build the role inheritance relations. void BuildIncrementalRoleLinks(policy_op op, const std::string& p_type, const std::vector>& rules); // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(std::shared_ptr evalator); + bool Enforce(std::shared_ptr evalator) override; // Enforce with a list param, decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(const DataList& params); + virtual bool Enforce(const DataList& params); // Enforce with a vector param, decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(const DataVector& params); + virtual bool Enforce(const DataVector& params); // Enforce with a map param,decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(const DataMap& params); + virtual bool Enforce(const DataMap& params); // EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model // matcher by default when matcher is "". - bool EnforceWithMatcher(const std::string& matcher, std::shared_ptr evalator); + bool EnforceWithMatcher(const std::string& matcher, std::shared_ptr evalator) override; // EnforceWithMatcher use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model // matcher by default when matcher is "". bool EnforceWithMatcher(const std::string& matcher, const DataList& params); @@ -193,89 +193,89 @@ class Enforcer : public IEnforcer { std::vector BatchEnforceWithMatcher(const std::string& matcher, const std::initializer_list& requests) override; /*Management API member functions.*/ - std::vector GetAllSubjects(); - std::vector GetAllNamedSubjects(const std::string& p_type); - std::vector GetAllObjects(); - std::vector GetAllNamedObjects(const std::string& p_type); - std::vector GetAllActions(); - std::vector GetAllNamedActions(const std::string& p_type); - std::vector GetAllRoles(); - std::vector GetAllNamedRoles(const std::string& p_type); - std::vector> GetPolicy(); - std::vector> GetFilteredPolicy(int field_index, const std::vector& field_values); - std::vector> GetNamedPolicy(const std::string& p_type); - std::vector> GetFilteredNamedPolicy(const std::string& p_type, int field_index, const std::vector& field_values); - std::vector> GetGroupingPolicy(); - std::vector> GetFilteredGroupingPolicy(int field_index, const std::vector& field_values); - std::vector> GetNamedGroupingPolicy(const std::string& p_type); - std::vector> GetFilteredNamedGroupingPolicy(const std::string& p_type, int field_index, const std::vector& field_values); - bool HasPolicy(const std::vector& params); - bool HasNamedPolicy(const std::string& p_type, const std::vector& params); - bool AddPolicy(const std::vector& params); - bool AddPolicies(const std::vector>& rules); - bool AddNamedPolicy(const std::string& p_type, const std::vector& params); - bool AddNamedPolicies(const std::string& p_type, const std::vector>& rules); - bool RemovePolicy(const std::vector& params); - bool RemovePolicies(const std::vector>& rules); - bool RemoveFilteredPolicy(int field_index, const std::vector& field_values); - bool RemoveNamedPolicy(const std::string& p_type, const std::vector& params); - bool RemoveNamedPolicies(const std::string& p_type, const std::vector>& rules); - bool RemoveFilteredNamedPolicy(const std::string& p_type, int field_index, const std::vector& field_values); - bool HasGroupingPolicy(const std::vector& params); - bool HasNamedGroupingPolicy(const std::string& p_type, const std::vector& params); - bool AddGroupingPolicy(const std::vector& params); - bool AddGroupingPolicies(const std::vector>& rules); - bool AddNamedGroupingPolicy(const std::string& p_type, const std::vector& params); - bool AddNamedGroupingPolicies(const std::string& p_type, const std::vector>& rules); - bool RemoveGroupingPolicy(const std::vector& params); - bool RemoveGroupingPolicies(const std::vector>& rules); - bool RemoveFilteredGroupingPolicy(int field_index, const std::vector& field_values); - bool RemoveNamedGroupingPolicy(const std::string& p_type, const std::vector& params); - bool RemoveNamedGroupingPolicies(const std::string& p_type, const std::vector>& rules); - bool RemoveFilteredNamedGroupingPolicy(const std::string& p_type, int field_index, const std::vector& field_values); - bool UpdateGroupingPolicy(const std::vector& oldRule, const std::vector& newRule); - bool UpdateNamedGroupingPolicy(const std::string& ptype, const std::vector& oldRule, const std::vector& newRule); - bool UpdatePolicy(const std::vector& oldPolicy, const std::vector& newPolicy); - bool UpdateNamedPolicy(const std::string& ptype, const std::vector& p1, const std::vector& p2); - bool UpdatePolicies(const std::vector>& oldPolices, const std::vector>& newPolicies); - bool UpdateNamedPolicies(const std::string& ptype, const std::vector>& p1, const std::vector>& p2); - bool AddNamedMatchingFunc(const std::string& ptype, const std::string& name, casbin::MatchingFunc func); + std::vector GetAllSubjects() override; + std::vector GetAllNamedSubjects(const std::string& p_type) override; + std::vector GetAllObjects() override; + std::vector GetAllNamedObjects(const std::string& p_type) override; + std::vector GetAllActions() override; + std::vector GetAllNamedActions(const std::string& p_type) override; + std::vector GetAllRoles() override; + std::vector GetAllNamedRoles(const std::string& p_type) override; + std::vector> GetPolicy() override; + std::vector> GetFilteredPolicy(int field_index, const std::vector& field_values) override; + std::vector> GetNamedPolicy(const std::string& p_type) override; + std::vector> GetFilteredNamedPolicy(const std::string& p_type, int field_index, const std::vector& field_values) override; + std::vector> GetGroupingPolicy() override; + std::vector> GetFilteredGroupingPolicy(int field_index, const std::vector& field_values) override; + std::vector> GetNamedGroupingPolicy(const std::string& p_type) override; + std::vector> GetFilteredNamedGroupingPolicy(const std::string& p_type, int field_index, const std::vector& field_values) override; + bool HasPolicy(const std::vector& params) override; + bool HasNamedPolicy(const std::string& p_type, const std::vector& params) override; + bool AddPolicy(const std::vector& params) override; + bool AddPolicies(const std::vector>& rules) override; + bool AddNamedPolicy(const std::string& p_type, const std::vector& params) override; + bool AddNamedPolicies(const std::string& p_type, const std::vector>& rules) override; + bool RemovePolicy(const std::vector& params) override; + bool RemovePolicies(const std::vector>& rules) override; + bool RemoveFilteredPolicy(int field_index, const std::vector& field_values) override; + bool RemoveNamedPolicy(const std::string& p_type, const std::vector& params) override; + bool RemoveNamedPolicies(const std::string& p_type, const std::vector>& rules) override; + bool RemoveFilteredNamedPolicy(const std::string& p_type, int field_index, const std::vector& field_values) override; + bool HasGroupingPolicy(const std::vector& params) override; + bool HasNamedGroupingPolicy(const std::string& p_type, const std::vector& params) override; + bool AddGroupingPolicy(const std::vector& params) override; + bool AddGroupingPolicies(const std::vector>& rules) override; + bool AddNamedGroupingPolicy(const std::string& p_type, const std::vector& params) override; + bool AddNamedGroupingPolicies(const std::string& p_type, const std::vector>& rules) override; + bool RemoveGroupingPolicy(const std::vector& params) override; + bool RemoveGroupingPolicies(const std::vector>& rules) override; + bool RemoveFilteredGroupingPolicy(int field_index, const std::vector& field_values) override; + bool RemoveNamedGroupingPolicy(const std::string& p_type, const std::vector& params) override; + bool RemoveNamedGroupingPolicies(const std::string& p_type, const std::vector>& rules) override; + bool RemoveFilteredNamedGroupingPolicy(const std::string& p_type, int field_index, const std::vector& field_values) override; + bool UpdateGroupingPolicy(const std::vector& oldRule, const std::vector& newRule) override; + bool UpdateNamedGroupingPolicy(const std::string& ptype, const std::vector& oldRule, const std::vector& newRule) override; + bool UpdatePolicy(const std::vector& oldPolicy, const std::vector& newPolicy) override; + bool UpdateNamedPolicy(const std::string& ptype, const std::vector& p1, const std::vector& p2) override; + bool UpdatePolicies(const std::vector>& oldPolices, const std::vector>& newPolicies) override; + bool UpdateNamedPolicies(const std::string& ptype, const std::vector>& p1, const std::vector>& p2) override; + bool AddNamedMatchingFunc(const std::string& ptype, const std::string& name, casbin::MatchingFunc func) override; /*RBAC API member functions.*/ - std::vector GetRolesForUser(const std::string& name, const std::vector& domain = {}); - std::vector GetUsersForRole(const std::string& name, const std::vector& domain = {}); - bool HasRoleForUser(const std::string& name, const std::string& role); - bool AddRoleForUser(const std::string& user, const std::string& role); - bool AddRolesForUser(const std::string& user, const std::vector& roles); - bool AddPermissionForUser(const std::string& user, const std::vector& permission); - bool DeletePermissionForUser(const std::string& user, const std::vector& permission); - bool DeletePermissionsForUser(const std::string& user); - std::vector> GetPermissionsForUser(const std::string& user); - bool HasPermissionForUser(const std::string& user, const std::vector& permission); - std::vector GetImplicitRolesForUser(const std::string& name, const std::vector& domain = {}); - std::vector> GetImplicitPermissionsForUser(const std::string& user, const std::vector& domain = {}); - std::vector GetImplicitUsersForPermission(const std::vector& permission); - bool DeleteRoleForUser(const std::string& user, const std::string& role); - bool DeleteRolesForUser(const std::string& user); - bool DeleteUser(const std::string& user); - bool DeleteRole(const std::string& role); - bool DeletePermission(const std::vector& permission); + std::vector GetRolesForUser(const std::string& name, const std::vector& domain = {}) override; + std::vector GetUsersForRole(const std::string& name, const std::vector& domain = {}) override; + bool HasRoleForUser(const std::string& name, const std::string& role) override; + bool AddRoleForUser(const std::string& user, const std::string& role) override; + bool AddRolesForUser(const std::string& user, const std::vector& roles) override; + bool AddPermissionForUser(const std::string& user, const std::vector& permission) override; + bool DeletePermissionForUser(const std::string& user, const std::vector& permission) override; + bool DeletePermissionsForUser(const std::string& user) override; + std::vector> GetPermissionsForUser(const std::string& user) override; + bool HasPermissionForUser(const std::string& user, const std::vector& permission) override; + std::vector GetImplicitRolesForUser(const std::string& name, const std::vector& domain = {}) override; + std::vector> GetImplicitPermissionsForUser(const std::string& user, const std::vector& domain = {}) override; + std::vector GetImplicitUsersForPermission(const std::vector& permission) override; + bool DeleteRoleForUser(const std::string& user, const std::string& role) override; + bool DeleteRolesForUser(const std::string& user) override; + bool DeleteUser(const std::string& user) override; + bool DeleteRole(const std::string& role) override; + bool DeletePermission(const std::vector& permission) override; /* Internal API member functions */ - bool addPolicy(const std::string& sec, const std::string& p_type, const std::vector& rule); - bool addPolicies(const std::string& sec, const std::string& p_type, const std::vector>& rules); - bool removePolicy(const std::string& sec, const std::string& p_type, const std::vector& rule); - bool removePolicies(const std::string& sec, const std::string& p_type, const std::vector>& rules); - bool removeFilteredPolicy(const std::string& sec, const std::string& p_type, int field_index, const std::vector& field_values); - bool updatePolicy(const std::string& sec, const std::string& p_type, const std::vector& oldRule, const std::vector& newRule); - bool updatePolicies(const std::string& sec, const std::string& p_type, const std::vector>& p1, const std::vector>& p2); + bool addPolicy(const std::string& sec, const std::string& p_type, const std::vector& rule) override; + bool addPolicies(const std::string& sec, const std::string& p_type, const std::vector>& rules) override; + bool removePolicy(const std::string& sec, const std::string& p_type, const std::vector& rule) override; + bool removePolicies(const std::string& sec, const std::string& p_type, const std::vector>& rules) override; + bool removeFilteredPolicy(const std::string& sec, const std::string& p_type, int field_index, const std::vector& field_values) override; + bool updatePolicy(const std::string& sec, const std::string& p_type, const std::vector& oldRule, const std::vector& newRule) override; + bool updatePolicies(const std::string& sec, const std::string& p_type, const std::vector>& p1, const std::vector>& p2) override; /* RBAC API with domains.*/ - std::vector GetUsersForRoleInDomain(const std::string& name, const std::string& domain = {}); - std::vector GetRolesForUserInDomain(const std::string& name, const std::string& domain = {}); - std::vector> GetPermissionsForUserInDomain(const std::string& user, const std::string& domain = {}); - bool AddRoleForUserInDomain(const std::string& user, const std::string& role, const std::string& domain = {}); - bool DeleteRoleForUserInDomain(const std::string& user, const std::string& role, const std::string& domain = {}); + std::vector GetUsersForRoleInDomain(const std::string& name, const std::string& domain = {}) override; + std::vector GetRolesForUserInDomain(const std::string& name, const std::string& domain = {}) override; + std::vector> GetPermissionsForUserInDomain(const std::string& user, const std::string& domain = {}) override; + bool AddRoleForUserInDomain(const std::string& user, const std::string& role, const std::string& domain = {}) override; + bool DeleteRoleForUserInDomain(const std::string& user, const std::string& role, const std::string& domain = {}) override; }; } // namespace casbin diff --git a/include/casbin/enforcer_synced.h b/include/casbin/enforcer_synced.h index a3e6fe79..17916a07 100644 --- a/include/casbin/enforcer_synced.h +++ b/include/casbin/enforcer_synced.h @@ -100,16 +100,16 @@ class SyncedEnforcer : public Enforcer { std::string UpdateWrapper(); // SetWatcher sets the current watcher. - void SetWatcher(std::shared_ptr w); + void SetWatcher(std::shared_ptr w) override; // LoadModel reloads the model from the model CONF file. - void LoadModel(); + void LoadModel() override; // ClearPolicy clears all policy. - void ClearPolicy(); + void ClearPolicy() override; // LoadPolicy reloads the policy from file/database. - void LoadPolicy(); + void LoadPolicy() override; void LoadPolicyWrapper(); @@ -121,179 +121,179 @@ class SyncedEnforcer : public Enforcer { void LoadIncrementalFilteredPolicy(Filter); // SavePolicy saves the current policy (usually after changed with Casbin API) back to file/database. - void SavePolicy(); + void SavePolicy() override; // BuildRoleLinks manually rebuild the role inheritance relations. - void BuildRoleLinks(); + void BuildRoleLinks() override; // Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(std::shared_ptr); + bool Enforce(std::shared_ptr) override; // Enforce with a vector param,decides whether a "subject" can access a // "object" with the operation "action", input parameters are usually: (sub, // obj, act). - bool Enforce(const DataVector& params); + bool Enforce(const DataVector& params) override; // Enforce with a vector param,decides whether a "subject" can access a // "object" with the operation "action", input parameters are usually: (sub, // obj, act). - bool Enforce(const DataList& params); + bool Enforce(const DataList& params) override; // Enforce with a map param,decides whether a "subject" can access a "object" // with the operation "action", input parameters are usually: (sub, obj, act). - bool Enforce(const DataMap& params); + bool Enforce(const DataMap& params) override; // BatchEnforce enforce in batches - std::vector BatchEnforce(const std::initializer_list& requests); + std::vector BatchEnforce(const std::initializer_list& requests) override; // BatchEnforceWithMatcher enforce with matcher in batches - std::vector BatchEnforceWithMatcher(const std::string& matcher, const std::initializer_list& requests); + std::vector BatchEnforceWithMatcher(const std::string& matcher, const std::initializer_list& requests) override; // GetAllSubjects gets the list of subjects that show up in the current policy. - std::vector GetAllSubjects(); + std::vector GetAllSubjects() override; // GetAllNamedSubjects gets the list of subjects that show up in the current named policy. - std::vector GetAllNamedSubjects(const std::string& ptype); + std::vector GetAllNamedSubjects(const std::string& ptype) override; // GetAllObjects gets the list of objects that show up in the current policy. - std::vector GetAllObjects(); + std::vector GetAllObjects() override; // GetAllNamedObjects gets the list of objects that show up in the current named policy. - std::vector GetAllNamedObjects(const std::string& ptype); + std::vector GetAllNamedObjects(const std::string& ptype) override; // GetAllNamedActions gets the list of actions that show up in the current named policy. - std::vector GetAllNamedActions(const std::string& ptype); + std::vector GetAllNamedActions(const std::string& ptype) override; // GetAllRoles gets the list of roles that show up in the current policy. - std::vector GetAllRoles(); + std::vector GetAllRoles() override; // GetAllNamedRoles gets the list of roles that show up in the current named policy. - std::vector GetAllNamedRoles(const std::string& ptype); + std::vector GetAllNamedRoles(const std::string& ptype) override; // GetPolicy gets all the authorization rules in the policy. - std::vector> GetPolicy(); + std::vector> GetPolicy() override; // GetNamedPolicy gets all the authorization rules in the named policy. - std::vector> GetNamedPolicy(const std::string& ptype); + std::vector> GetNamedPolicy(const std::string& ptype) override; // GetFilteredNamedPolicy gets all the authorization rules in the named policy, field filters can be specified. - std::vector> GetFilteredNamedPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues); + std::vector> GetFilteredNamedPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues) override; // GetGroupingPolicy gets all the role inheritance rules in the policy. - std::vector> GetGroupingPolicy(); + std::vector> GetGroupingPolicy() override; // GetFilteredGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. - std::vector> GetFilteredGroupingPolicy(int fieldIndex, const std::vector& fieldValues); + std::vector> GetFilteredGroupingPolicy(int fieldIndex, const std::vector& fieldValues) override; // GetNamedGroupingPolicy gets all the role inheritance rules in the policy. - std::vector> GetNamedGroupingPolicy(const std::string& ptype); + std::vector> GetNamedGroupingPolicy(const std::string& ptype) override; // GetFilteredNamedGroupingPolicy gets all the role inheritance rules in the policy, field filters can be specified. - std::vector> GetFilteredNamedGroupingPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues); + std::vector> GetFilteredNamedGroupingPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues) override; // HasPolicy determines whether an authorization rule exists. - bool HasPolicy(const std::vector& params); + bool HasPolicy(const std::vector& params) override; // HasNamedPolicy determines whether a named authorization rule exists. - bool HasNamedPolicy(const std::string& ptype, const std::vector& params); + bool HasNamedPolicy(const std::string& ptype, const std::vector& params) override; // AddPolicy adds an authorization rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. - bool AddPolicy(const std::vector& params); + bool AddPolicy(const std::vector& params) override; // AddPolicies adds authorization rules to the current policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding rule by adding the new rule. - bool AddPolicies(const std::vector>& rules); + bool AddPolicies(const std::vector>& rules) override; // AddNamedPolicy adds an authorization rule to the current named policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. - bool AddNamedPolicy(const std::string& ptype, const std::vector& params); + bool AddNamedPolicy(const std::string& ptype, const std::vector& params) override; // AddNamedPolicies adds authorization rules to the current named policy. // If the rule already exists, the function returns false for the corresponding rule and the rule will not be added. // Otherwise the function returns true for the corresponding by adding the new rule. - bool AddNamedPolicies(const std::string& ptype, const std::vector>& rules); + bool AddNamedPolicies(const std::string& ptype, const std::vector>& rules) override; // RemovePolicy removes an authorization rule from the current policy. - bool RemovePolicy(const std::vector& params); + bool RemovePolicy(const std::vector& params) override; // UpdatePolicy updates an authorization rule from the current policy. - bool UpdatePolicy(const std::vector& oldPolicy, const std::vector& newPolicy); + bool UpdatePolicy(const std::vector& oldPolicy, const std::vector& newPolicy) override; - bool UpdateNamedPolicy(const std::string& ptype, const std::vector& p1, const std::vector& p2); + bool UpdateNamedPolicy(const std::string& ptype, const std::vector& p1, const std::vector& p2) override; // UpdatePolicies updates authorization rules from the current policies. - bool UpdatePolicies(const std::vector>& oldPolices, const std::vector>& newPolicies); + bool UpdatePolicies(const std::vector>& oldPolices, const std::vector>& newPolicies) override; - bool UpdateNamedPolicies(const std::string& ptype, const std::vector>& p1, const std::vector>& p2); + bool UpdateNamedPolicies(const std::string& ptype, const std::vector>& p1, const std::vector>& p2) override; // RemovePolicies removes authorization rules from the current policy. - bool RemovePolicies(const std::vector>& rules); + bool RemovePolicies(const std::vector>& rules) override; // RemoveFilteredPolicy removes an authorization rule from the current policy, field filters can be specified. - bool RemoveFilteredPolicy(int fieldIndex, const std::vector& fieldValues); + bool RemoveFilteredPolicy(int fieldIndex, const std::vector& fieldValues) override; // RemoveNamedPolicy removes an authorization rule from the current named policy. - bool RemoveNamedPolicy(const std::string& ptype, const std::vector& params); + bool RemoveNamedPolicy(const std::string& ptype, const std::vector& params) override; // RemoveNamedPolicies removes authorization rules from the current named policy. - bool RemoveNamedPolicies(const std::string& ptype, const std::vector>& rules); + bool RemoveNamedPolicies(const std::string& ptype, const std::vector>& rules) override; // RemoveFilteredNamedPolicy removes an authorization rule from the current named policy, field filters can be specified. - bool RemoveFilteredNamedPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues); + bool RemoveFilteredNamedPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues) override; // HasGroupingPolicy determines whether a role inheritance rule exists. - bool HasGroupingPolicy(const std::vector& params); + bool HasGroupingPolicy(const std::vector& params) override; // HasNamedGroupingPolicy determines whether a named role inheritance rule exists. - bool HasNamedGroupingPolicy(const std::string& ptype, const std::vector& params); + bool HasNamedGroupingPolicy(const std::string& ptype, const std::vector& params) override; // AddGroupingPolicy adds a role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. - bool AddGroupingPolicy(const std::vector& params); + bool AddGroupingPolicy(const std::vector& params) override; // AddGroupingPolicies adds role inheritance rulea to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. - bool AddGroupingPolicies(const std::vector>& rules); + bool AddGroupingPolicies(const std::vector>& rules) override; // AddNamedGroupingPolicy adds a named role inheritance rule to the current policy. // If the rule already exists, the function returns false and the rule will not be added. // Otherwise the function returns true by adding the new rule. - bool AddNamedGroupingPolicy(const std::string& ptype, const std::vector& params); + bool AddNamedGroupingPolicy(const std::string& ptype, const std::vector& params) override; // AddNamedGroupingPolicies adds named role inheritance rules to the current policy. // If the rule already exists, the function returns false for the corresponding policy rule and the rule will not be added. // Otherwise the function returns true for the corresponding policy rule by adding the new rule. - bool AddNamedGroupingPolicies(const std::string& ptype, const std::vector>& rules); + bool AddNamedGroupingPolicies(const std::string& ptype, const std::vector>& rules) override; // RemoveGroupingPolicy removes a role inheritance rule from the current policy. - bool RemoveGroupingPolicy(const std::vector& params); + bool RemoveGroupingPolicy(const std::vector& params) override; // RemoveGroupingPolicies removes role inheritance rules from the current policy. - bool RemoveGroupingPolicies(const std::vector>& rules); + bool RemoveGroupingPolicies(const std::vector>& rules) override; // RemoveFilteredGroupingPolicy removes a role inheritance rule from the current policy, field filters can be specified. - bool RemoveFilteredGroupingPolicy(int fieldIndex, const std::vector& fieldValues); + bool RemoveFilteredGroupingPolicy(int fieldIndex, const std::vector& fieldValues) override; // RemoveNamedGroupingPolicy removes a role inheritance rule from the current named policy. - bool RemoveNamedGroupingPolicy(const std::string& ptype, const std::vector& params); + bool RemoveNamedGroupingPolicy(const std::string& ptype, const std::vector& params) override; // RemoveNamedGroupingPolicies removes role inheritance rules from the current named policy. - bool RemoveNamedGroupingPolicies(const std::string& ptype, const std::vector>& rules); + bool RemoveNamedGroupingPolicies(const std::string& ptype, const std::vector>& rules) override; - bool UpdateGroupingPolicy(const std::vector& oldRule, const std::vector& newRule); + bool UpdateGroupingPolicy(const std::vector& oldRule, const std::vector& newRule) override; - bool UpdateNamedGroupingPolicy(const std::string& ptype, const std::vector& oldRule, const std::vector& newRule); + bool UpdateNamedGroupingPolicy(const std::string& ptype, const std::vector& oldRule, const std::vector& newRule) override; // RemoveFilteredNamedGroupingPolicy removes a role inheritance rule from the current named policy, field filters can be specified. - bool RemoveFilteredNamedGroupingPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues); + bool RemoveFilteredNamedGroupingPolicy(const std::string& ptype, int fieldIndex, const std::vector& fieldValues) override; }; } // namespace casbin -#endif // CASBIN_CPP_ENFORCER_SYNC +#endif diff --git a/tests/enforcer_synced_test.cpp b/tests/enforcer_synced_test.cpp index 32edda66..c96db25c 100644 --- a/tests/enforcer_synced_test.cpp +++ b/tests/enforcer_synced_test.cpp @@ -23,6 +23,71 @@ namespace { +std::string global_sub; +std::string global_obj; +std::string global_act; +std::string global_domain; + +template +std::shared_ptr InitializeParams(const std::string& sub, const std::string& obj, const std::string& act) { + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + // Because of "Short String Optimization", these strings's data is in stack. + // For MSVC compiler, when this stack frame return, these memory will can't access. + // So we need keep this memory accessiable. + global_sub = sub; + global_obj = obj; + global_act = act; + + evaluator->PushObjectString("r", "sub", global_sub); + evaluator->PushObjectString("r", "obj", global_obj); + evaluator->PushObjectString("r", "act", global_act); + + return evaluator; +} + +template +std::shared_ptr InitializeParamsWithoutUsers(const std::string& obj, const std::string& act) { + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + global_obj = obj; + global_act = act; + evaluator->PushObjectString("r", "obj", global_obj); + evaluator->PushObjectString("r", "act", global_act); + return evaluator; +} + +template +std::shared_ptr InitializeParamsWithoutResources(const std::string& sub, const std::string& act) { + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + global_sub = sub; + global_act = act; + evaluator->PushObjectString("r", "sub", global_sub); + evaluator->PushObjectString("r", "act", global_act); + return evaluator; +} + +template +std::shared_ptr InitializeParamsWithDomains(const std::string& sub, const std::string& domain, const std::string& obj, const std::string& act) { + auto evaluator = std::make_shared(); + evaluator->InitialObject("r"); + + global_sub = sub; + global_obj = obj; + global_act = act; + global_domain = domain; + + evaluator->PushObjectString("r", "sub", global_sub); + evaluator->PushObjectString("r", "dom", global_domain); + evaluator->PushObjectString("r", "obj", global_obj); + evaluator->PushObjectString("r", "act", global_act); + return evaluator; +} + void TestSyncFn(casbin::SyncedEnforcer& e, const std::string& sub, const std::string& obj, const std::string& act, bool control) { bool response = e.Enforce({sub, obj, act}); ASSERT_EQ(response, control); @@ -73,4 +138,200 @@ TEST(TestEnforcerSynced, TestStopLoadPolicy) { EXPECT_EQ(e.IsAutoLoadingRunning(), false); } +TEST(TestEnforcerSynced, TestMultiThreadEnforce) { + casbin::SyncedEnforcer e(basic_model_path, basic_policy_path); + + using namespace std::literals::chrono_literals; + std::chrono::duration t = 5ms; + + e.StartAutoLoadPolicy(t); + + EXPECT_EQ(e.IsAutoLoadingRunning(), true); + + for (int i = 0; i < 100; ++i) { + std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data1", "read"}), true); }); + std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data1", "write"}), false); }); + std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data2", "read"}), false); }); + std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"alice", "data2", "write"}), false); }); + std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data1", "read"}), false); }); + std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data1", "write"}), false); }); + std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data2", "read"}), false); }); + std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataList{"bob", "data2", "write"}), true); }); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); + t6.join(); + t7.join(); + t8.join(); + } + + for (int i = 0; i < 100; ++i) { + std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data1", "read"}), true); }); + std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data1", "write"}), false); }); + std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data2", "read"}), false); }); + std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"alice", "data2", "write"}), false); }); + std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data1", "read"}), false); }); + std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data1", "write"}), false); }); + std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data2", "read"}), false); }); + std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataVector{"bob", "data2", "write"}), true); }); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); + t6.join(); + t7.join(); + t8.join(); + } + + for (int i = 0; i < 100; ++i) { + std::thread t1([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data1"}, {"act", "read"}}), true); }); + std::thread t2([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data1"}, {"act", "write"}}), false); }); + std::thread t3([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data2"}, {"act", "read"}}), false); }); + std::thread t4([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "alice"}, {"obj", "data2"}, {"act", "write"}}), false); }); + std::thread t5([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data1"}, {"act", "read"}}), false); }); + std::thread t6([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data1"}, {"act", "write"}}), false); }); + std::thread t7([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data2"}, {"act", "read"}}), false); }); + std::thread t8([&] { ASSERT_EQ(e.Enforce(casbin::DataMap{{"sub", "bob"}, {"obj", "data2"}, {"act", "write"}}), true); }); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); + t6.join(); + t7.join(); + t8.join(); + } + + std::mutex mtx; // for evaluator + for (int i = 0; i < 100; ++i) { + std::thread t1([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("alice", "data1", "read"); + } + ASSERT_EQ(e.Enforce(evaluator), true); + }); + std::thread t2([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("alice", "data1", "write"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t3([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("alice", "data2", "read"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t4([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("alice", "data2", "write"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t5([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("bob", "data1", "read"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t6([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("bob", "data1", "write"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t7([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("bob", "data2", "read"); + } + ASSERT_EQ(e.Enforce(evaluator), false); + }); + std::thread t8([&] { + std::shared_ptr evaluator; + { + std::scoped_lock lock(mtx); + evaluator = InitializeParams("bob", "data2", "write"); + } + ASSERT_EQ(e.Enforce(evaluator), true); + }); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); + t6.join(); + t7.join(); + t8.join(); + } + + e.StopAutoLoadPolicy(); + std::this_thread::sleep_for(10ms); + + EXPECT_EQ(e.IsAutoLoadingRunning(), false); +} + +TEST(TestEnforcerSynced, TestMultiThreadBatchEnforce) { + casbin::SyncedEnforcer e(basic_model_path, basic_policy_path); + + using namespace std::literals::chrono_literals; + std::chrono::duration t = 5ms; + + e.StartAutoLoadPolicy(t); + + EXPECT_EQ(e.IsAutoLoadingRunning(), true); + + for (int i = 0; i < 100; ++i) { + std::thread t1([&] { + std::vector expect_result{true, false, false, false}; + ASSERT_EQ(e.BatchEnforce({{"alice", "data1", "read"}, {"alice", "data1", "write"}, {"alice", "data2", "read"}, {"alice", "data2", "write"}}), expect_result); + }); + + std::thread t2([&] { + std::vector expect_result{false, false, false, true}; + ASSERT_EQ(e.BatchEnforce({{"bob", "data1", "read"}, {"bob", "data1", "write"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}}), expect_result); + }); + + std::thread t3([&] { + std::vector expect_result{true, false, false, false}; + ASSERT_EQ(e.BatchEnforce({{"alice", "data1", "read"}, {"alice", "data1", "write"}, {"bob", "data1", "read"}, {"bob", "data1", "write"}}), expect_result); + }); + + std::thread t4([&] { + std::vector expect_result{false, false, false, true}; + ASSERT_EQ(e.BatchEnforce({{"alice", "data2", "read"}, {"alice", "data2", "write"}, {"bob", "data2", "read"}, {"bob", "data2", "write"}}), expect_result); + }); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } + + e.StopAutoLoadPolicy(); + std::this_thread::sleep_for(10ms); + + EXPECT_EQ(e.IsAutoLoadingRunning(), false); +} + } // namespace