diff --git a/casbin/.clang-format b/casbin/.clang-format new file mode 100644 index 00000000..df0c5b87 --- /dev/null +++ b/casbin/.clang-format @@ -0,0 +1,25 @@ +BasedOnStyle: Google +PointerAlignment: Right +AccessModifierOffset: -4 +IndentWidth: 4 +MaxEmptyLinesToKeep: 1 +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AlignAfterOpenBracket: true +IndentCaseLabels: true +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ColumnLimit: 0 +AlignTrailingComments: true +SpaceAfterCStyleCast: false +AlignOperands: true +SpacesInSquareBrackets: false +AlignConsecutiveDeclarations: false +SpacesInContainerLiterals: false +BreakConstructorInitializersBeforeComma: true +AllowAllParametersOfDeclarationOnNextLine: false +ContinuationIndentWidth: 4 +TabWidth: 4 +SpaceBeforeAssignmentOperators: true +SpacesBeforeTrailingComments: 1 \ No newline at end of file diff --git a/casbin/casbin.vcxproj b/casbin/casbin.vcxproj index 6dd19d10..f11f7cf8 100644 --- a/casbin/casbin.vcxproj +++ b/casbin/casbin.vcxproj @@ -157,6 +157,7 @@ + @@ -229,6 +230,7 @@ + @@ -286,6 +288,9 @@ + + + diff --git a/casbin/casbin.vcxproj.filters b/casbin/casbin.vcxproj.filters index a3a2905a..88a6a79d 100644 --- a/casbin/casbin.vcxproj.filters +++ b/casbin/casbin.vcxproj.filters @@ -252,6 +252,9 @@ Source Files\persist\file_adapter + + Source Files + @@ -464,5 +467,11 @@ Header Files\persist\file_adapter + + Header Files + + + + \ No newline at end of file diff --git a/casbin/enforcer_cached.cpp b/casbin/enforcer_cached.cpp new file mode 100644 index 00000000..35f4d6ef --- /dev/null +++ b/casbin/enforcer_cached.cpp @@ -0,0 +1,213 @@ +/* +* Copyright 2020 The casbin Authors. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#include "pch.h" + +#include "./enforcer_cached.h" +#include "./persist/watcher_ex.h" +#include "./persist/file_adapter/file_adapter.h" +#include "./rbac/default_role_manager.h" +#include "./effect/default_effector.h" +#include "./exception/casbin_adapter_exception.h" +#include "./exception/casbin_enforcer_exception.h" +#include "./util/util.h" +using namespace std; + + +/** + * Enforcer is the default constructor. + */ +CachedEnforcer ::CachedEnforcer() { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a model file and a policy file. + * + * @param model_path the path of the model file. + * @param policyFile the path of the policy file. + */ +CachedEnforcer ::CachedEnforcer(string model_path, string policy_file): Enforcer(model_path, policy_file) { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a database adapter. + * + * @param model_path the path of the model file. + * @param adapter the adapter. + */ +CachedEnforcer ::CachedEnforcer(string model_path, shared_ptr adapter): Enforcer(model_path,adapter) { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a model and a database adapter. + * + * @param m the model. + * @param adapter the adapter. + */ +CachedEnforcer :: CachedEnforcer(shared_ptr m, shared_ptr adapter): Enforcer(m,adapter) { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a model. + * + * @param m the model. + */ +CachedEnforcer ::CachedEnforcer(shared_ptr m): Enforcer(m) { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a model file. + * + * @param model_path the path of the model file. + */ +CachedEnforcer ::CachedEnforcer(string model_path): Enforcer(model_path) { + this->enableCache = true; +} + +/** + * Enforcer initializes an enforcer with a model file, a policy file and an enable log flag. + * + * @param model_path the path of the model file. + * @param policyFile the path of the policy file. + * @param enableLog whether to enable Casbin's log. + */ +CachedEnforcer :: CachedEnforcer(string model_path, string policy_file, bool enable_log): Enforcer(model_path,policy_file,enable_log) { + this->enableCache = true; +} + +CachedEnforcer::CachedEnforcer(const CachedEnforcer& ce):Enforcer(ce){ + this->m = ce.m; + this->enableCache = ce.enableCache; +} + +CachedEnforcer::CachedEnforcer(CachedEnforcer&& ce):Enforcer(ce){ + this->m = move(ce.m); + this->enableCache = ce.enableCache; +} + + +void CachedEnforcer::EnableCache(const bool& enableCache) { + this->enableCache = enableCache; +} + +pair CachedEnforcer::getCachedResult(const string& key) { + locker.lock(); + bool ok = m.count(key); + if (!ok) { + locker.unlock(); + return pair(false, false); + } + + pair res_ok(m[key], ok); + locker.unlock(); + return res_ok; +} + +void CachedEnforcer::setCachedResult(const string& key, const bool& res) { + locker.lock(); + m[key] = res; + locker.unlock(); +} + +void CachedEnforcer::InvalidateCache() { + m.clear(); +} + +// Enforce decides whether a "subject" can access a "object" with the operation +// "action", input parameters are usually: (sub, obj, act). +bool CachedEnforcer ::Enforce(Scope scope) { + return EnforceWithMatcher("", scope); +} + +// 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 CachedEnforcer::Enforce(vector params) { + return EnforceWithMatcher("", 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 CachedEnforcer::Enforce(unordered_map params) { + return EnforceWithMatcher("", 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 CachedEnforcer ::EnforceWithMatcher(string matcher, Scope scope) { + return Enforcer::EnforceWithMatcher(matcher, scope); +} + +// 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 CachedEnforcer::EnforceWithMatcher(string matcher, vector params) { + if (!enableCache) { + return Enforcer::EnforceWithMatcher(matcher,params); + } + + string key; + for (auto r : params) { + key += r; + key += "$$"; + } + key += matcher; + key += "$"; + + pair res_ok = getCachedResult(key); + + if (res_ok.second) { + return res_ok.first; + } + + bool res = Enforcer::EnforceWithMatcher(matcher,params); + setCachedResult(key, res); + return res; +} + +// 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 CachedEnforcer::EnforceWithMatcher(string matcher, unordered_map params) { + if (!enableCache) { + return Enforcer::EnforceWithMatcher(matcher,params); + } + + string key; + for (auto r : params) { + key += r.second; + key += "$$"; + } + key += matcher; + key += "$"; + + pair res_ok = getCachedResult(key); + + if (res_ok.second) { + return res_ok.first; + } + + bool res = Enforcer::EnforceWithMatcher(matcher,params); + setCachedResult(key, res); + return res; +} \ No newline at end of file diff --git a/casbin/enforcer_cached.h b/casbin/enforcer_cached.h new file mode 100644 index 00000000..f019ad49 --- /dev/null +++ b/casbin/enforcer_cached.h @@ -0,0 +1,109 @@ +/* + * Copyright 2020 The casbin Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CASBIN_CPP_ENFORCER_CACHED +#define CASBIN_CPP_ENFORCER_CACHED + +#include + +#include "./enforcer.h" + +class CachedEnforcer : public Enforcer { +public: + unordered_map m; + bool enableCache; + mutex locker; + + CachedEnforcer(const CachedEnforcer& ce); + CachedEnforcer(CachedEnforcer&& ce); + + void EnableCache(const bool& enableCache); + pair getCachedResult(const string& key); + void setCachedResult(const string& key, const bool& res); + void InvalidateCache(); + +public: + /** + * Enforcer is the default constructor. + */ + CachedEnforcer(); + /** + * Enforcer initializes an enforcer with a model file and a policy file. + * + * @param model_path the path of the model file. + * @param policy_file the path of the policy file. + */ + CachedEnforcer(string model_path, string policy_file); + /** + * Enforcer initializes an enforcer with a database adapter. + * + * @param model_path the path of the model file. + * @param adapter the adapter. + */ + CachedEnforcer(string model_path, shared_ptr adapter); + /** + * Enforcer initializes an enforcer with a model and a database adapter. + * + * @param m the model. + * @param adapter the adapter. + */ + CachedEnforcer(shared_ptr m, shared_ptr adapter); + /** + * Enforcer initializes an enforcer with a model. + * + * @param m the model. + */ + CachedEnforcer(shared_ptr m); + /** + * Enforcer initializes an enforcer with a model file. + * + * @param model_path the path of the model file. + */ + CachedEnforcer(string model_path); + /** + * Enforcer initializes an enforcer with a model file, a policy file and an enable log flag. + * + * @param model_path the path of the model file. + * @param policy_file the path of the policy file. + * @param enable_log whether to enable Casbin's log. + */ + CachedEnforcer(string model_path, string policy_file, bool enable_log); + + bool Enforce(Scope scope); + // 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(vector 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(unordered_map 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(string matcher, Scope scope); + // 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(string matcher, vector 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(string matcher, unordered_map params); +}; +#endif \ No newline at end of file diff --git a/test/.clang-format b/test/.clang-format new file mode 100644 index 00000000..a37ae61b --- /dev/null +++ b/test/.clang-format @@ -0,0 +1,27 @@ +# Visual Studio 生成的 .clang-format 文件 + +BasedOnStyle: Google +PointerAlignment: Right +AccessModifierOffset: -4 +IndentWidth: 4 +MaxEmptyLinesToKeep: 1 +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AlignAfterOpenBracket: true +IndentCaseLabels: true +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ColumnLimit: 0 +AlignTrailingComments: true +SpaceAfterCStyleCast: false +AlignOperands: true +SpacesInSquareBrackets: false +AlignConsecutiveDeclarations: false +SpacesInContainerLiterals: false +BreakConstructorInitializersBeforeComma: true +AllowAllParametersOfDeclarationOnNextLine: true +ContinuationIndentWidth: 4 +TabWidth: 4 +SpaceBeforeAssignmentOperators: true +SpacesBeforeTrailingComments: 1 \ No newline at end of file diff --git a/test/test.vcxproj b/test/test.vcxproj index a2a151d6..99235b14 100644 --- a/test/test.vcxproj +++ b/test/test.vcxproj @@ -168,6 +168,7 @@ + @@ -179,6 +180,9 @@ + + + diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters index 4b5e33f9..4289d648 100644 --- a/test/test.vcxproj.filters +++ b/test/test.vcxproj.filters @@ -48,10 +48,16 @@ Source Files + + Source Files + Header Files + + + \ No newline at end of file diff --git a/test/test_enforcer_cached.cpp b/test/test_enforcer_cached.cpp new file mode 100644 index 00000000..2dcd55f1 --- /dev/null +++ b/test/test_enforcer_cached.cpp @@ -0,0 +1,48 @@ +#pragma once + +#include "pch.h" + +#include + +using namespace std; + +namespace test_enforcer_cached +{ + TEST_CLASS(TestEnforcerCached) + { + public: + + void testEnforceCache(CachedEnforcer& e, string sub,string obj,string act, bool res) { + Assert::AreEqual(res, e.Enforce({sub,obj,act})); + } + + + + TEST_METHOD(TestCache) { + string model = "../../examples/basic_model.conf"; + string policy = "../../examples/basic_policy.csv"; + CachedEnforcer e = CachedEnforcer(model, policy); + testEnforceCache(e, "alice", "data1", "read", true); + testEnforceCache(e, "alice", "data1", "write", false); + testEnforceCache(e, "alice", "data2", "read", false); + testEnforceCache(e, "alice", "data2", "write", false); + + // The cache is enabled, so even if we remove a policy rule, the decision + // for ("alice", "data1", "read") will still be true, as it uses the cached result. + e.RemovePolicy({"alice", "data1", "read"}); + testEnforceCache(e, "alice", "data1", "read", true); + testEnforceCache(e, "alice", "data1", "write", false); + testEnforceCache(e, "alice", "data2", "read", false); + testEnforceCache(e, "alice", "data2", "write", false); + + // Now we invalidate the cache, then all first-coming Enforce() has to be evaluated in real-time. + // The decision for ("alice", "data1", "read") will be false now. + e.InvalidateCache(); + testEnforceCache(e, "alice", "data1", "read", false); + testEnforceCache(e, "alice", "data1", "write", false); + testEnforceCache(e, "alice", "data2", "read", false); + testEnforceCache(e, "alice", "data2", "write", false); + } + + }; +} \ No newline at end of file