From 849c6327571263367da35a9d9d132619648e3ca9 Mon Sep 17 00:00:00 2001 From: Sagilio Date: Fri, 2 Apr 2021 21:39:25 +0800 Subject: [PATCH] feat: Support thread-safe policy management Signed-off-by: Sagilio --- NetCasbin.UnitTest/EnforcerTest.cs | 2 +- ...rCacheTest.cs => EnforcerWithCacheTest.cs} | 4 +- NetCasbin.UnitTest/GroupRoleManagerTest.cs | 3 +- NetCasbin.UnitTest/Util/TestUtil.cs | 48 ++-- NetCasbin.UnitTest/WatcherTest.cs | 1 + NetCasbin/Abstractions/IEnforcer.cs | 138 ++++------ NetCasbin/Abstractions/IModel.cs | 4 +- NetCasbin/Abstractions/IPolicyManager.cs | 8 +- NetCasbin/Casbin.csproj | 12 +- NetCasbin/Config/DefaultConfig.cs | 2 +- NetCasbin/DefaultEnforcer.cs | 28 ++ NetCasbin/Enforcer.cs | 247 +++++------------- NetCasbin/Extensions/EnforcerExtension.cs | 173 ++++++++++++ NetCasbin/Extensions/ModelExtension.cs | 19 ++ .../Extensions/PolicyManagerExtension.cs | 19 ++ NetCasbin/Extensions/RbacEnforcerExtension.cs | 1 - NetCasbin/Model/DefaultModel.cs | 19 +- NetCasbin/Model/DefaultPolicy.cs | 2 +- NetCasbin/Model/DefaultPolicyManager.cs | 150 ++++++----- NetCasbin/Model/ReaderWriterPolicyManager.cs | 60 +++++ .../Model/ReaderWriterPolicyManagerOptions.cs | 9 + NetCasbin/SyncedEnforcer.cs | 28 ++ 22 files changed, 589 insertions(+), 388 deletions(-) rename NetCasbin.UnitTest/{EnforcerCacheTest.cs => EnforcerWithCacheTest.cs} (95%) create mode 100644 NetCasbin/DefaultEnforcer.cs create mode 100644 NetCasbin/Extensions/EnforcerExtension.cs create mode 100644 NetCasbin/Extensions/ModelExtension.cs create mode 100644 NetCasbin/Extensions/PolicyManagerExtension.cs create mode 100644 NetCasbin/Model/ReaderWriterPolicyManager.cs create mode 100644 NetCasbin/Model/ReaderWriterPolicyManagerOptions.cs create mode 100644 NetCasbin/SyncedEnforcer.cs diff --git a/NetCasbin.UnitTest/EnforcerTest.cs b/NetCasbin.UnitTest/EnforcerTest.cs index d9e9fa79..38d8741a 100644 --- a/NetCasbin.UnitTest/EnforcerTest.cs +++ b/NetCasbin.UnitTest/EnforcerTest.cs @@ -537,7 +537,7 @@ public void TestEnableAutoSave() [Fact] public async Task TestEnableAutoSaveAsync() { - var e = new Enforcer("examples/basic_model.conf", "examples/basic_policy_for_async_adapter_test.csv"); + var e = SyncedEnforcer.Create("examples/basic_model.conf", "examples/basic_policy_for_async_adapter_test.csv"); e.EnableAutoSave(false); // Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer, diff --git a/NetCasbin.UnitTest/EnforcerCacheTest.cs b/NetCasbin.UnitTest/EnforcerWithCacheTest.cs similarity index 95% rename from NetCasbin.UnitTest/EnforcerCacheTest.cs rename to NetCasbin.UnitTest/EnforcerWithCacheTest.cs index 8eb19002..a076382f 100644 --- a/NetCasbin.UnitTest/EnforcerCacheTest.cs +++ b/NetCasbin.UnitTest/EnforcerWithCacheTest.cs @@ -8,12 +8,12 @@ namespace Casbin.UnitTests { [Collection("Model collection")] - public class EnforcerCacheTest + public class EnforcerWithCacheTest { private readonly ITestOutputHelper _testOutputHelper; private readonly TestModelFixture _testModelFixture; - public EnforcerCacheTest(ITestOutputHelper testOutputHelper, TestModelFixture testModelFixture) + public EnforcerWithCacheTest(ITestOutputHelper testOutputHelper, TestModelFixture testModelFixture) { _testOutputHelper = testOutputHelper; _testModelFixture = testModelFixture; diff --git a/NetCasbin.UnitTest/GroupRoleManagerTest.cs b/NetCasbin.UnitTest/GroupRoleManagerTest.cs index f1e4c6b4..9084806b 100644 --- a/NetCasbin.UnitTest/GroupRoleManagerTest.cs +++ b/NetCasbin.UnitTest/GroupRoleManagerTest.cs @@ -1,4 +1,5 @@ -using Casbin.Rbac; +using Casbin.Extensions; +using Casbin.Rbac; using Xunit; using static Casbin.UnitTests.Util.TestUtil; diff --git a/NetCasbin.UnitTest/Util/TestUtil.cs b/NetCasbin.UnitTest/Util/TestUtil.cs index d734ede4..c292508d 100644 --- a/NetCasbin.UnitTest/Util/TestUtil.cs +++ b/NetCasbin.UnitTest/Util/TestUtil.cs @@ -21,13 +21,13 @@ internal static List AsList(params string[] values) return values.ToList(); } - internal static void TestEnforce(Enforcer e, object sub, object obj, string act, bool res) + internal static void TestEnforce(IEnforcer e, object sub, object obj, string act, bool res) { Assert.Equal(res, e.Enforce(sub, obj, act)); } #if !NET452 - internal static void TestEnforceEx(Enforcer e, object sub, object obj, string act, List res) + internal static void TestEnforceEx(IEnforcer e, object sub, object obj, string act, List res) { var myRes = e.EnforceEx(sub, obj, act).Explains.ToList(); string message = "Key: " + myRes + ", supposed to be " + res; @@ -37,7 +37,7 @@ internal static void TestEnforceEx(Enforcer e, object sub, object obj, string ac } } #else - internal static void TestEnforceEx(Enforcer e, object sub, object obj, string act, List res) + internal static void TestEnforceEx(IEnforcer e, object sub, object obj, string act, List res) { var myRes = e.EnforceEx(sub, obj, act).Item2.ToList(); string message = "Key: " + myRes + ", supposed to be " + res; @@ -48,127 +48,127 @@ internal static void TestEnforceEx(Enforcer e, object sub, object obj, string ac } #endif - internal static async Task TestEnforceExAsync(Enforcer e, object sub, object obj, string act, List res) + internal static async Task TestEnforceExAsync(IEnforcer e, object sub, object obj, string act, List res) { var myRes = (await e.EnforceExAsync(sub, obj, act)).Item2.ToList(); string message = "Key: " + myRes + ", supposed to be " + res; - if (myRes.Count > 0) + if (myRes.Any()) { Assert.True(Utility.SetEquals(res, myRes[0].ToList()), message); } } - internal static async Task TestEnforceAsync(Enforcer e, object sub, object obj, string act, bool res) + internal static async Task TestEnforceAsync(IEnforcer e, object sub, object obj, string act, bool res) { Assert.Equal(res, await e.EnforceAsync(sub, obj, act)); } - internal static void TestEnforceWithoutUsers(Enforcer e, string obj, string act, bool res) + internal static void TestEnforceWithoutUsers(IEnforcer e, string obj, string act, bool res) { Assert.Equal(res, e.Enforce(obj, act)); } - internal static async Task TestEnforceWithoutUsersAsync(Enforcer e, string obj, string act, bool res) + internal static async Task TestEnforceWithoutUsersAsync(IEnforcer e, string obj, string act, bool res) { Assert.Equal(res, await e.EnforceAsync(obj, act)); } - internal static void TestDomainEnforce(Enforcer e, string sub, string dom, string obj, string act, bool res) + internal static void TestDomainEnforce(IEnforcer e, string sub, string dom, string obj, string act, bool res) { Assert.Equal(res, e.Enforce(sub, dom, obj, act)); } - internal static void TestGetPolicy(Enforcer e, List> res) + internal static void TestGetPolicy(IEnforcer e, List> res) { var myRes = e.GetPolicy(); Assert.True(res.DeepEquals(myRes)); } - internal static void TestGetFilteredPolicy(Enforcer e, int fieldIndex, List> res, params string[] fieldValues) + internal static void TestGetFilteredPolicy(IEnforcer e, int fieldIndex, List> res, params string[] fieldValues) { var myRes = e.GetFilteredPolicy(fieldIndex, fieldValues); Assert.True(res.DeepEquals(myRes)); } - internal static void TestGetGroupingPolicy(Enforcer e, List> res) + internal static void TestGetGroupingPolicy(IEnforcer e, List> res) { var myRes = e.GetGroupingPolicy(); Assert.Equal(res, myRes); } - internal static void TestGetFilteredGroupingPolicy(Enforcer e, int fieldIndex, List> res, params string[] fieldValues) + internal static void TestGetFilteredGroupingPolicy(IEnforcer e, int fieldIndex, List> res, params string[] fieldValues) { var myRes = e.GetFilteredGroupingPolicy(fieldIndex, fieldValues); Assert.Equal(res, myRes); } - internal static void TestHasPolicy(Enforcer e, List policy, bool res) + internal static void TestHasPolicy(IEnforcer e, List policy, bool res) { bool myRes = e.HasPolicy(policy); Assert.Equal(res, myRes); } - internal static void TestHasGroupingPolicy(Enforcer e, List policy, bool res) + internal static void TestHasGroupingPolicy(IEnforcer e, List policy, bool res) { bool myRes = e.HasGroupingPolicy(policy); Assert.Equal(res, myRes); } - internal static void TestGetRoles(Enforcer e, string name, List res, string domain = null) + internal static void TestGetRoles(IEnforcer e, string name, List res, string domain = null) { List myRes = e.GetRolesForUser(name, domain); string message = "Roles for " + name + ": " + myRes + ", supposed to be " + res; Assert.True(Utility.SetEquals(res, myRes), message); } - internal static void TestGetUsers(Enforcer e, string name, List res, string domain = null) + internal static void TestGetUsers(IEnforcer e, string name, List res, string domain = null) { List myRes = e.GetUsersForRole(name, domain); string message = "Users for " + name + ": " + myRes + ", supposed to be " + res; Assert.True(Utility.SetEquals(res, myRes), message); } - internal static void TestHasRole(Enforcer e, string name, string role, bool res, string domain = null) + internal static void TestHasRole(IEnforcer e, string name, string role, bool res, string domain = null) { bool myRes = e.HasRoleForUser(name, role, domain); Assert.Equal(res, myRes); } - internal static void TestGetPermissions(Enforcer e, string name, List> res, string domain = null) + internal static void TestGetPermissions(IEnforcer e, string name, List> res, string domain = null) { var myRes = e.GetPermissionsForUser(name, domain); string message = "Permissions for " + name + ": " + myRes + ", supposed to be " + res; Assert.True(res.DeepEquals(myRes), message); } - internal static void TestGetImplicitPermissions(Enforcer e, string name, List> res, string domain = null) + internal static void TestGetImplicitPermissions(IEnforcer e, string name, List> res, string domain = null) { var myRes = e.GetImplicitPermissionsForUser(name, domain); string message = "Implicit permissions for " + name + ": " + myRes + ", supposed to be " + res; Assert.True(res.DeepEquals(myRes), message); } - internal static void TestHasPermission(Enforcer e, string name, List permission, bool res) + internal static void TestHasPermission(IEnforcer e, string name, List permission, bool res) { bool myRes = e.HasPermissionForUser(name, permission); Assert.Equal(res, myRes); } - internal static void TestGetRolesInDomain(Enforcer e, string name, string domain, List res) + internal static void TestGetRolesInDomain(IEnforcer e, string name, string domain, List res) { List myRes = e.GetRolesForUserInDomain(name, domain); string message = "Roles for " + name + " under " + domain + ": " + myRes + ", supposed to be " + res; Assert.True(Utility.SetEquals(res, myRes), message); } - internal static void TestGetImplicitRolesInDomain(Enforcer e, string name, string domain, List res) + internal static void TestGetImplicitRolesInDomain(IEnforcer e, string name, string domain, List res) { List myRes = e.GetImplicitRolesForUser(name, domain); string message = "Implicit roles in domain " + name + " under " + domain + ": " + myRes + ", supposed to be " + res; Assert.True(Utility.SetEquals(res, myRes), message); } - internal static void TestGetPermissionsInDomain(Enforcer e, string name, string domain, List> res) + internal static void TestGetPermissionsInDomain(IEnforcer e, string name, string domain, List> res) { var myRes = e.GetPermissionsForUserInDomain(name, domain); Assert.True(res.DeepEquals(myRes), "Permissions for " + name + " under " + domain + ": " + myRes + ", supposed to be " + res); diff --git a/NetCasbin.UnitTest/WatcherTest.cs b/NetCasbin.UnitTest/WatcherTest.cs index f8238ea1..c6cc28ed 100644 --- a/NetCasbin.UnitTest/WatcherTest.cs +++ b/NetCasbin.UnitTest/WatcherTest.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Casbin.Adapter.File; +using Casbin.Extensions; using Casbin.Persist; using Casbin.UnitTests.Fixtures; using Xunit; diff --git a/NetCasbin/Abstractions/IEnforcer.cs b/NetCasbin/Abstractions/IEnforcer.cs index 48d132bd..9d3a9182 100644 --- a/NetCasbin/Abstractions/IEnforcer.cs +++ b/NetCasbin/Abstractions/IEnforcer.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Casbin.Persist; using Casbin.Rbac; @@ -12,22 +14,23 @@ namespace Casbin public interface IEnforcer { #region Options - public bool Enabled { get; } - public bool EnabledCache { get; } - public bool AutoSave { get; } - public bool AutoBuildRoleLinks { get; } - public bool AutoNotifyWatcher { get; } - public bool AutoCleanEnforceCache { get; } + public bool Enabled { get; set; } + public bool EnabledCache { get; set; } + public bool AutoSave { get; set; } + public bool AutoBuildRoleLinks { get; set; } + public bool AutoNotifyWatcher { get; set; } + public bool AutoCleanEnforceCache { get; set; } #endregion #region Extensions - public IEffector Effector { get; } - public IModel Model { get; } - public IPolicyManager PolicyManager { get; } - public IAdapter Adapter { get; } - public IWatcher Watcher { get; } - public IRoleManager RoleManager { get; } - public IEnforceCache EnforceCache { get; } + public IEffector Effector { get; set; } + public IModel Model { get; set; } + public IPolicyManager PolicyManager { get; set; } + public IAdapter Adapter { get; set; } + public IWatcher Watcher { get; set; } + public IRoleManager RoleManager { get; set; } + public IEnforceCache EnforceCache { get; set; } + public IExpressionHandler ExpressionHandler { get; set; } #if !NET45 public ILogger Logger { get; set; } #endif @@ -35,73 +38,6 @@ public interface IEnforcer public string ModelPath { get; } public bool IsFiltered { get; } - public IExpressionHandler ExpressionHandler { get; } - - /// - /// Changes the enforcing state of Casbin, when Casbin is disabled, - /// all access will be allowed by the enforce() function. - /// - /// - public void EnableEnforce(bool enable); - - /// - /// Controls whether to save a policy rule automatically to the - /// adapter when it is added or removed. - /// - /// - public void EnableAutoSave(bool autoSave); - - /// - /// Controls whether to save a policy rule automatically - /// to the adapter when it is added or removed. - /// - /// Whether to automatically build the role links. - public void EnableAutoBuildRoleLinks(bool autoBuildRoleLinks); - - - - /// - /// Sets the current model. - /// - /// - public void SetModel(string modelPath); - - /// - /// Sets the current model. - /// - /// - public void SetModel(IModel model); - - /// - /// Sets an adapter. - /// - /// - public void SetAdapter(IAdapter adapter); - - /// - /// Sets an watcher. - /// - /// - /// Whether use async update callback. - public void SetWatcher(IWatcher watcher, bool useAsync = true); - - /// - /// Sets the current role manager. - /// - /// - public void SetRoleManager(IRoleManager roleManager); - - /// - /// Sets the current effector. - /// - /// - public void SetEffector(IEffector effector); - - /// - /// Sets an enforce cache. - /// - /// - public void SetEnforceCache(IEnforceCache enforceCache); /// /// LoadModel reloads the model from the model CONF file. Because the policy is @@ -164,10 +100,48 @@ public interface IEnforcer /// Decides whether a "subject" can access a "object" with the operation /// "action", input parameters are usually: (sub, obj, act). /// - /// The request needs to be mediated, usually an array of strings, + /// The request needs to be mediated, usually an array of strings, + /// can be class instances if ABAC is used. + /// Whether to allow the request. + public bool Enforce(params object[] requestValues); + + /// + /// Decides whether a "subject" can access a "object" with the operation + /// "action", input parameters are usually: (sub, obj, act). + /// + /// The request needs to be mediated, usually an array of strings, /// can be class instances if ABAC is used. /// Whether to allow the request. - public bool Enforce(params object[] rvals); + public Task EnforceAsync(params object[] requestValues); + + /// + /// Explains enforcement by informing matched rules + /// + /// The request needs to be mediated, usually an array of strings, + /// can be class instances if ABAC is used. + /// Whether to allow the request and explains. +#if !NET45 + + public (bool Result, IEnumerable> Explains) + EnforceEx(params object[] requestValues); +#else + public Tuple>> + EnforceEx(params object[] requestValues); +#endif + + /// + /// Explains enforcement by informing matched rules + /// + /// The request needs to be mediated, usually an array of strings, + /// can be class instances if ABAC is used. + /// Whether to allow the request and explains. +#if !NET45 + public Task<(bool Result, IEnumerable> Explains)> + EnforceExAsync(params object[] requestValues); +#else + public Task>>> + EnforceExAsync(params object[] requestValues); +#endif } } diff --git a/NetCasbin/Abstractions/IModel.cs b/NetCasbin/Abstractions/IModel.cs index 381fe2fe..62972640 100644 --- a/NetCasbin/Abstractions/IModel.cs +++ b/NetCasbin/Abstractions/IModel.cs @@ -2,9 +2,9 @@ { public interface IModel : IPolicy { - public IPolicyManager PolicyManager { get; } + public string ModelPath { get; } - public void SetPolicyManager(IPolicyManager policyManager); + public IPolicyManager PolicyManager { get; set; } public void LoadModelFromFile(string path); diff --git a/NetCasbin/Abstractions/IPolicyManager.cs b/NetCasbin/Abstractions/IPolicyManager.cs index 76dbb65b..5d0c85f0 100644 --- a/NetCasbin/Abstractions/IPolicyManager.cs +++ b/NetCasbin/Abstractions/IPolicyManager.cs @@ -6,15 +6,15 @@ namespace Casbin { public interface IPolicyManager { - public IAdapter Adapter { get; } + public bool IsSynchronized { get; } + + public IAdapter Adapter { get; set; } public bool HasAdapter { get; } public bool AutoSave { get; set; } - public IPolicy Policy { get; } - - public void SetAdapter(IAdapter adapter); + public IPolicy Policy { get; set; } public void StartRead(); diff --git a/NetCasbin/Casbin.csproj b/NetCasbin/Casbin.csproj index 3f4c3f68..6c0d02c7 100644 --- a/NetCasbin/Casbin.csproj +++ b/NetCasbin/Casbin.csproj @@ -29,16 +29,8 @@ - - - 5.0.0 - - - - - - 5.0.0 - + + diff --git a/NetCasbin/Config/DefaultConfig.cs b/NetCasbin/Config/DefaultConfig.cs index a7508f57..db27ab11 100644 --- a/NetCasbin/Config/DefaultConfig.cs +++ b/NetCasbin/Config/DefaultConfig.cs @@ -32,7 +32,7 @@ public static IConfig Create() /// /// The path of the model file. /// The constructor of Config. - public static IConfig CreatefromFile(string configFilePath) + public static IConfig CreateFromFile(string configFilePath) { var config = new DefaultConfig(); config.Parse(configFilePath); diff --git a/NetCasbin/DefaultEnforcer.cs b/NetCasbin/DefaultEnforcer.cs new file mode 100644 index 00000000..238d8cdf --- /dev/null +++ b/NetCasbin/DefaultEnforcer.cs @@ -0,0 +1,28 @@ +using Casbin.Model; +using Casbin.Persist; + +namespace Casbin +{ + public static class DefaultEnforcer + { + public static IEnforcer Create(IAdapter adapter = null) + { + return new Enforcer(DefaultModel.Create(), adapter); + } + + public static IEnforcer Create(string modelPath, string policyPath) + { + return new Enforcer(modelPath, policyPath); + } + + public static IEnforcer Create(string modelPath, IAdapter adapter = null) + { + return new Enforcer(modelPath, adapter); + } + + public static IEnforcer Create(IModel model, IAdapter adapter = null) + { + return new Enforcer(model, adapter); + } + } +} diff --git a/NetCasbin/Enforcer.cs b/NetCasbin/Enforcer.cs index e9981d02..93e28c0a 100644 --- a/NetCasbin/Enforcer.cs +++ b/NetCasbin/Enforcer.cs @@ -5,7 +5,6 @@ using Casbin.Adapter.File; using Casbin.Caching; using Casbin.Effect; -using Casbin.Evaluation; using Casbin.Extensions; using Casbin.Model; using Casbin.Persist; @@ -32,181 +31,54 @@ public Enforcer(string modelPath, string policyPath) public Enforcer(string modelPath, IAdapter adapter = null) : this(DefaultModel.CreateFromFile(modelPath), adapter) { - ModelPath = modelPath; } public Enforcer(IModel model, IAdapter adapter = null) { - SetModel(model); + this.SetModel(model); if (adapter is not null) { - SetAdapter(adapter); + this.SetAdapter(adapter); } LoadPolicy(); } #region Options - public bool Enabled { get; private set; } = true; - public bool EnabledCache { get; private set; } = true; - public bool AutoSave => PolicyManager.AutoSave; - public bool AutoBuildRoleLinks { get; private set; } = true; - public bool AutoNotifyWatcher { get; private set; } = true; - public bool AutoCleanEnforceCache { get; private set; } = true; - #endregion - - #region Extensions - public IEffector Effector { get; private set; } = new DefaultEffector(); - public IModel Model { get; private set; } - public IPolicyManager PolicyManager => Model.PolicyManager; - public IAdapter Adapter => PolicyManager.Adapter; - public IWatcher Watcher { get; private set; } - public IRoleManager RoleManager { get; private set; } = new DefaultRoleManager(10); - public IEnforceCache EnforceCache { get; private set; } -#if !NET45 - public ILogger Logger { get; set; } -#endif - #endregion - - public string ModelPath { get; private set; } - public bool IsFiltered => Adapter is IFilteredAdapter {IsFiltered: true}; - public IExpressionHandler ExpressionHandler { get; private set; } - - #region Set options - - /// - /// Changes the enforcing state of Casbin, when Casbin is disabled, - /// all access will be allowed by the enforce() function. - /// - /// - public void EnableEnforce(bool enable) + public bool Enabled { get; set; } = true; + public bool EnabledCache { get; set; } = true; + public bool AutoSave { - Enabled = enable; + get => PolicyManager.AutoSave; + set => PolicyManager.AutoSave = value; } - - /// - /// Controls whether to save a policy rule automatically to the - /// adapter when it is added or removed. - /// - /// - public void EnableAutoSave(bool autoSave) - { - PolicyManager.AutoSave = autoSave; - } - - /// - /// Controls whether to save a policy rule automatically - /// to the adapter when it is added or removed. - /// - /// Whether to automatically build the role links. - public void EnableAutoBuildRoleLinks(bool autoBuildRoleLinks) - { - AutoBuildRoleLinks = autoBuildRoleLinks; - } - - /// - /// Controls whether to save a policy rule automatically - /// notify the Watcher when it is added or removed. - /// - /// Whether to automatically notify watcher. - public void EnableAutoNotifyWatcher(bool autoNotifyWatcher) - { - AutoNotifyWatcher = autoNotifyWatcher; - } - - public void EnableCache(bool enableCache) - { - EnabledCache = enableCache; - } - - public void EnableAutoCleanEnforceCache(bool autoCleanEnforceCache) - { - AutoCleanEnforceCache = autoCleanEnforceCache; - } - + public bool AutoBuildRoleLinks { get; set; } = true; + public bool AutoNotifyWatcher { get; set; } = true; + public bool AutoCleanEnforceCache { get; set; } = true; #endregion - #region Set extensions - - /// - /// Sets the current effector. - /// - /// - public void SetEffector(IEffector effector) + #region Extensions + public IEffector Effector { get; set; } = new DefaultEffector(); + public IModel Model { get; set; } + public IPolicyManager PolicyManager { - Effector = effector; + get => Model?.PolicyManager; + set => Model.SetPolicyManager(value); } - - /// - /// Sets the current model. - /// - /// - public void SetModel(string modelPath) + public IAdapter Adapter { - IModel model = DefaultModel.CreateFromFile(modelPath); - SetModel(model); - ModelPath = modelPath; + get => PolicyManager?.Adapter; + set => PolicyManager.SetAdapter(value); } - - /// - /// Sets the current model. - /// - /// - public void SetModel(IModel model) - { - Model = model; - ExpressionHandler = new ExpressionHandler(model); - if (AutoCleanEnforceCache) - { - EnforceCache?.Clear(); + public IWatcher Watcher { get; set; } + public IRoleManager RoleManager { get; set; } = new DefaultRoleManager(10); + public IEnforceCache EnforceCache { get; set; } + public IExpressionHandler ExpressionHandler { get; set; } #if !NET45 - Logger?.LogInformation("Enforcer Cache, Cleared all enforce cache."); + public ILogger Logger { get; set; } #endif - } - } - - /// - /// Sets an adapter. - /// - /// - public void SetAdapter(IAdapter adapter) - { - PolicyManager.SetAdapter(adapter); - } - - /// - /// Sets an watcher. - /// - /// - /// Whether use async update callback. - public void SetWatcher(IWatcher watcher, bool useAsync = true) - { - Watcher = watcher; - if (useAsync) - { - watcher?.SetUpdateCallback(LoadPolicyAsync); - return; - } - watcher?.SetUpdateCallback(LoadPolicy); - } - - /// - /// Sets the current role manager. - /// - /// - public void SetRoleManager(IRoleManager roleManager) - { - RoleManager = roleManager; - } - - /// - /// Sets an enforce cache. - /// - /// - public void SetEnforceCache(IEnforceCache enforceCache) - { - EnforceCache = enforceCache; - } #endregion + public string ModelPath => Model?.ModelPath; + public bool IsFiltered => Adapter is IFilteredAdapter {IsFiltered: true}; /// /// LoadModel reloads the model from the model CONF file. Because the policy is @@ -307,7 +179,6 @@ public async Task LoadFilteredPolicyAsync(Filter filter) ClearPolicy(); await filteredAdapter.LoadFilteredPolicyAsync(Model, filter); - if (AutoBuildRoleLinks) { BuildRoleLinks(); @@ -394,12 +265,12 @@ public bool Enforce(params object[] requestValues) if (EnabledCache is false) { - return InternalEnforce(requestValues); + return InternalEnforce(PolicyManager, requestValues); } if (requestValues.Any(requestValue => requestValue is not string)) { - return InternalEnforce(requestValues); + return InternalEnforce(PolicyManager, requestValues); } string key = string.Join("$$", requestValues); @@ -412,7 +283,7 @@ public bool Enforce(params object[] requestValues) return cachedResult; } - bool result = InternalEnforce(requestValues); + bool result = InternalEnforce(PolicyManager, requestValues); EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions()); EnforceCache.TrySetResult(requestValues, key, result); return result; @@ -434,12 +305,12 @@ public async Task EnforceAsync(params object[] requestValues) if (EnabledCache is false) { - return await InternalEnforceAsync(requestValues); + return await InternalEnforceAsync(PolicyManager, requestValues); } if (requestValues.Any(requestValue => requestValue is not string)) { - return await InternalEnforceAsync(requestValues); + return await InternalEnforceAsync(PolicyManager, requestValues); } string key = string.Join("$$", requestValues); @@ -454,7 +325,7 @@ public async Task EnforceAsync(params object[] requestValues) return cachedResult; } - bool result = await InternalEnforceAsync(requestValues); + bool result = await InternalEnforceAsync(PolicyManager, requestValues); EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions()); await EnforceCache.TrySetResultAsync(requestValues, key, result); @@ -479,12 +350,12 @@ public async Task EnforceAsync(params object[] requestValues) if (EnabledCache is false) { - return (InternalEnforce(requestValues, explains), explains); + return (InternalEnforce(PolicyManager, requestValues, explains), explains); } if (requestValues.Any(requestValue => requestValue is not string)) { - return (InternalEnforce(requestValues, explains), explains); + return (InternalEnforce(PolicyManager, requestValues, explains), explains); } string key = string.Join("$$", requestValues); @@ -495,7 +366,7 @@ public async Task EnforceAsync(params object[] requestValues) return (cachedResult, explains); } - bool result = InternalEnforce(requestValues, explains); + bool result = InternalEnforce(PolicyManager, requestValues, explains); EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions()); EnforceCache.TrySetResult(requestValues, key, result); return (result, explains); @@ -511,7 +382,7 @@ public Tuple>> EnforceEx(params object[] requestValues) { var explains = new List>(); - bool result = InternalEnforce(requestValues, explains); + bool result = InternalEnforce(PolicyManager, requestValues, explains); return new Tuple>>(result, explains); } #endif @@ -534,12 +405,12 @@ public Tuple>> if (EnabledCache is false) { - return (await InternalEnforceAsync(requestValues, explains), explains); + return (await InternalEnforceAsync(PolicyManager, requestValues, explains), explains); } if (requestValues.Any(requestValue => requestValue is not string)) { - return (await InternalEnforceAsync(requestValues, explains), explains); + return (await InternalEnforceAsync(PolicyManager, requestValues, explains), explains); } string key = string.Join("$$", requestValues); @@ -550,7 +421,7 @@ public Tuple>> return (cachedResult, explains); } - bool result = await InternalEnforceAsync(requestValues, explains); + bool result = await InternalEnforceAsync(PolicyManager, requestValues, explains); EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions()); await EnforceCache.TrySetResultAsync(requestValues, key, result); return (result, explains); @@ -560,32 +431,34 @@ public async Task>>> EnforceExAsync(params object[] requestValues) { var explains = new List>(); - bool result = await InternalEnforceAsync(requestValues, explains); + bool result = await InternalEnforceAsync(PolicyManager, requestValues, explains); return new Tuple>>(result, explains); } #endif - /// - /// Decides whether a "subject" can access a "object" with the operation - /// "action", input parameters are usually: (sub, obj, act). - /// - /// The request needs to be mediated, usually an array of strings, - /// can be class instances if ABAC is used. - /// - /// Whether to allow the request. - private Task InternalEnforceAsync(IReadOnlyList requestValues, ICollection> explains = null) + private async Task InternalEnforceAsync(IPolicyManager policyManager, IReadOnlyList requestValues, ICollection> explains = null) { - return Task.FromResult(InternalEnforce(requestValues, explains)); + if (policyManager.IsSynchronized) + { + return await Task.Run(() => InternalEnforce(requestValues, explains)); + } + return InternalEnforce(policyManager, requestValues, explains); + } + + private bool InternalEnforce(IPolicyManager policyManager, IReadOnlyList requestValues, + ICollection> explains = null) + { + try + { + policyManager.StartRead(); + return InternalEnforce(requestValues, explains); + } + finally + { + policyManager.EndRead(); + } } - /// - /// Decides whether a "subject" can access a "object" with the operation - /// "action", input parameters are usually: (sub, obj, act). - /// - /// - /// The request needs to be mediated, usually an array of strings, - /// can be class instances if ABAC is used. - /// Whether to allow the request. private bool InternalEnforce(IReadOnlyList requestValues, ICollection> explains = null) { bool explain = explains is not null; diff --git a/NetCasbin/Extensions/EnforcerExtension.cs b/NetCasbin/Extensions/EnforcerExtension.cs new file mode 100644 index 00000000..e8792319 --- /dev/null +++ b/NetCasbin/Extensions/EnforcerExtension.cs @@ -0,0 +1,173 @@ +using Casbin.Evaluation; +using Casbin.Model; +using Casbin.Persist; +using Casbin.Rbac; +#if !NET45 +using Microsoft.Extensions.Logging; +#endif + +namespace Casbin.Extensions +{ + public static class EnforcerExtension + { + #region Set options + + /// + /// Changes the enforcing state of Casbin, when Casbin is disabled, + /// all access will be allowed by the enforce() function. + /// + /// + /// + public static IEnforcer EnableEnforce(this IEnforcer enforcer, bool enable) + { + enforcer.Enabled = enable; + return enforcer; + } + + /// + /// Controls whether to save a policy rule automatically to the + /// adapter when it is added or removed. + /// + /// + /// + public static IEnforcer EnableAutoSave(this IEnforcer enforcer, bool autoSave) + { + enforcer.AutoSave = autoSave; + return enforcer; + } + + /// + /// Controls whether to save a policy rule automatically + /// to the adapter when it is added or removed. + /// + /// + /// Whether to automatically build the role links. + public static IEnforcer EnableAutoBuildRoleLinks(this IEnforcer enforcer, bool autoBuildRoleLinks) + { + enforcer.AutoBuildRoleLinks = autoBuildRoleLinks; + return enforcer; + } + + /// + /// Controls whether to save a policy rule automatically + /// notify the Watcher when it is added or removed. + /// + /// + /// Whether to automatically notify watcher. + public static IEnforcer EnableAutoNotifyWatcher(this IEnforcer enforcer, bool autoNotifyWatcher) + { + enforcer.AutoNotifyWatcher = autoNotifyWatcher; + return enforcer; + } + + public static IEnforcer EnableCache(this IEnforcer enforcer, bool enableCache) + { + enforcer.EnabledCache = enableCache; + return enforcer; + } + + public static IEnforcer EnableAutoCleanEnforceCache(this IEnforcer enforcer, bool autoCleanEnforceCache) + { + enforcer.AutoCleanEnforceCache = autoCleanEnforceCache; + return enforcer; + } + + #endregion + + #region Set extensions + + /// + /// Sets the current effector. + /// + /// + /// + public static IEnforcer SetEffector(this IEnforcer enforcer, IEffector effector) + { + enforcer.Effector = effector; + return enforcer; + } + + /// + /// Sets the current model. + /// + /// + /// + public static IEnforcer SetModel(this IEnforcer enforcer, string modelPath) + { + IModel model = DefaultModel.CreateFromFile(modelPath); + enforcer.SetModel(model); + return enforcer; + } + + /// + /// Sets the current model. + /// + /// + /// + public static IEnforcer SetModel(this IEnforcer enforcer, IModel model) + { + enforcer.Model = model; + enforcer.ExpressionHandler = new ExpressionHandler(model); + if (enforcer.AutoCleanEnforceCache) + { + enforcer.EnforceCache?.Clear(); +#if !NET45 + enforcer.Logger?.LogInformation("Enforcer Cache, Cleared all enforce cache."); +#endif + } + return enforcer; + } + + /// + /// Sets an adapter. + /// + /// + /// + public static IEnforcer SetAdapter(this IEnforcer enforcer, IAdapter adapter) + { + enforcer.Adapter = adapter; + return enforcer; + } + + /// + /// Sets an watcher. + /// + /// + /// + /// Whether use async update callback. + public static IEnforcer SetWatcher(this IEnforcer enforcer, IWatcher watcher, bool useAsync = true) + { + enforcer.Watcher = watcher; + if (useAsync) + { + watcher?.SetUpdateCallback(enforcer.LoadPolicyAsync); + return enforcer; + } + watcher?.SetUpdateCallback(enforcer.LoadPolicy); + return enforcer; + } + + /// + /// Sets the current role manager. + /// + /// + /// + public static IEnforcer SetRoleManager(this IEnforcer enforcer, IRoleManager roleManager) + { + enforcer.RoleManager = roleManager; + return enforcer; + } + + /// + /// Sets an enforce cache. + /// + /// + /// + public static IEnforcer SetEnforceCache(this IEnforcer enforcer, IEnforceCache enforceCache) + { + enforcer.EnforceCache = enforceCache; + return enforcer; + } + #endregion + } +} diff --git a/NetCasbin/Extensions/ModelExtension.cs b/NetCasbin/Extensions/ModelExtension.cs new file mode 100644 index 00000000..f8398335 --- /dev/null +++ b/NetCasbin/Extensions/ModelExtension.cs @@ -0,0 +1,19 @@ +namespace Casbin.Extensions +{ + public static class ModelExtension + { + public static IModel SetPolicyManager(this IModel model, IPolicyManager policyManager) + { + model.PolicyManager = policyManager; + return model; + } + + public static IModel ReplacePolicyManager(this IModel model, IPolicyManager policyManager) + { + model.SetPolicyManager(policyManager + .SetAdapter(model.PolicyManager.Adapter) + .SetPolicy(model.PolicyManager.Policy)); + return model; + } + } +} diff --git a/NetCasbin/Extensions/PolicyManagerExtension.cs b/NetCasbin/Extensions/PolicyManagerExtension.cs new file mode 100644 index 00000000..8ecff530 --- /dev/null +++ b/NetCasbin/Extensions/PolicyManagerExtension.cs @@ -0,0 +1,19 @@ +using Casbin.Persist; + +namespace Casbin.Extensions +{ + public static class PolicyManagerExtension + { + public static IPolicyManager SetAdapter(this IPolicyManager policyManager, IAdapter adapter) + { + policyManager.Adapter = adapter; + return policyManager; + } + + public static IPolicyManager SetPolicy(this IPolicyManager policyManager, IPolicy policy) + { + policyManager.Policy = policy; + return policyManager; + } + } +} diff --git a/NetCasbin/Extensions/RbacEnforcerExtension.cs b/NetCasbin/Extensions/RbacEnforcerExtension.cs index cd3f7674..12a7735b 100644 --- a/NetCasbin/Extensions/RbacEnforcerExtension.cs +++ b/NetCasbin/Extensions/RbacEnforcerExtension.cs @@ -7,7 +7,6 @@ namespace Casbin.Extensions { public static class RbacEnforcerExtension { - #region Get roles or users /// diff --git a/NetCasbin/Model/DefaultModel.cs b/NetCasbin/Model/DefaultModel.cs index c2e49d01..a4ad94ff 100644 --- a/NetCasbin/Model/DefaultModel.cs +++ b/NetCasbin/Model/DefaultModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security; using Casbin.Config; using Casbin.Rbac; using Casbin.Util; @@ -18,14 +19,22 @@ public class DefaultModel : IModel { PermConstants.Section.MatcherSection, PermConstants.Section.MatcherSectionName} }; + private DefaultModel(IPolicyManager policyManager) { PolicyManager = policyManager; } - public IPolicyManager PolicyManager { get; private set; } + public IPolicyManager PolicyManager { get; set; } + + public Dictionary> Sections + => PolicyManager.Policy.Sections; - public Dictionary> Sections => PolicyManager.Policy.Sections; + private string _modelPath; + public string ModelPath + { + get => _modelPath; + } /// /// Creates a default model. @@ -75,14 +84,16 @@ public static IModel CreateFromText(string text) return model; } - public void SetPolicyManager(IPolicyManager policyManager) + public IModel SetPolicyManager(IPolicyManager policyManager) { PolicyManager = policyManager; + return this; } public void LoadModelFromFile(string path) { - LoadModel(DefaultConfig.CreatefromFile(path)); + LoadModel(DefaultConfig.CreateFromFile(path)); + _modelPath = path; } public void LoadModelFromText(string text) diff --git a/NetCasbin/Model/DefaultPolicy.cs b/NetCasbin/Model/DefaultPolicy.cs index 6123f319..9bf4e3a7 100644 --- a/NetCasbin/Model/DefaultPolicy.cs +++ b/NetCasbin/Model/DefaultPolicy.cs @@ -17,7 +17,7 @@ public class DefaultPolicy : IPolicy internal ILogger Logger { get; set; } #endif - internal DefaultPolicy() + private DefaultPolicy() { Sections = new Dictionary>(); } diff --git a/NetCasbin/Model/DefaultPolicyManager.cs b/NetCasbin/Model/DefaultPolicyManager.cs index 7cbf4424..0b152ce8 100644 --- a/NetCasbin/Model/DefaultPolicyManager.cs +++ b/NetCasbin/Model/DefaultPolicyManager.cs @@ -8,7 +8,7 @@ namespace Casbin.Model { public class DefaultPolicyManager : IPolicyManager { - private DefaultPolicyManager(IPolicy policy, IAdapter adapter = null) + public DefaultPolicyManager(IPolicy policy, IAdapter adapter = null) { Policy = policy; if (adapter is not null) @@ -17,21 +17,17 @@ private DefaultPolicyManager(IPolicy policy, IAdapter adapter = null) } } + public virtual bool IsSynchronized => false; public bool AutoSave { get; set; } - public IAdapter Adapter { get; private set; } + public IAdapter Adapter { get; set; } public bool HasAdapter => Adapter is null; - public IPolicy Policy { get; } + public IPolicy Policy { get; set; } public static IPolicyManager Create() { return new DefaultPolicyManager(DefaultPolicy.Create()); } - public void SetAdapter(IAdapter adapter) - { - Adapter = adapter; - } - public virtual void StartRead() { } @@ -61,9 +57,14 @@ public virtual bool TryStartWrite() public IEnumerable> GetPolicy(string section, string policyType) { + if (TryStartRead() is false) + { + return null; + } + try { - return TryStartRead() ? Policy.GetPolicy(section, policyType) : null; + return Policy.GetPolicy(section, policyType); } finally { @@ -73,9 +74,14 @@ public IEnumerable> GetPolicy(string section, string policyT public IEnumerable> GetFilteredPolicy(string section, string policyType, int fieldIndex, params string[] fieldValues) { + if (TryStartRead() is false) + { + return null; + } + try { - return TryStartRead() ? Policy.GetFilteredPolicy(section, policyType, fieldIndex, fieldValues) : null; + return Policy.GetFilteredPolicy(section, policyType, fieldIndex, fieldValues); } finally { @@ -85,9 +91,14 @@ public IEnumerable> GetFilteredPolicy(string section, string public IEnumerable GetValuesForFieldInPolicy(string section, string policyType, int fieldIndex) { + if (TryStartRead() is false) + { + return null; + } + try { - return TryStartRead() ? Policy.GetValuesForFieldInPolicy(section, policyType, fieldIndex) : null; + return Policy.GetValuesForFieldInPolicy(section, policyType, fieldIndex); } finally { @@ -97,9 +108,14 @@ public IEnumerable GetValuesForFieldInPolicy(string section, string poli public IEnumerable GetValuesForFieldInPolicyAllTypes(string section, int fieldIndex) { + if (TryStartRead() is false) + { + return null; + } + try { - return TryStartRead() ? Policy.GetValuesForFieldInPolicyAllTypes(section, fieldIndex) : null; + return Policy.GetValuesForFieldInPolicyAllTypes(section, fieldIndex); } finally { @@ -109,9 +125,9 @@ public IEnumerable GetValuesForFieldInPolicyAllTypes(string section, int public bool HasPolicy(string section, string policyType, IEnumerable rule) { + StartRead(); try { - StartRead(); return Policy.HasPolicy(section, policyType, rule); } finally @@ -122,9 +138,9 @@ public bool HasPolicy(string section, string policyType, IEnumerable rul public bool HasPolicies(string section, string policyType, IEnumerable> rules) { + StartRead(); try { - StartRead(); return Policy.HasPolicies(section, policyType, rules); } finally @@ -135,13 +151,13 @@ public bool HasPolicies(string section, string policyType, IEnumerable rule) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { IEnumerable ruleArray = rule as string[] ?? rule.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -168,13 +184,13 @@ public bool AddPolicy(string section, string policyType, IEnumerable rul public bool AddPolicies(string section, string policyType, IEnumerable> rules) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { var rulesArray = rules as IEnumerable[] ?? rules.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -201,13 +217,13 @@ public bool AddPolicies(string section, string policyType, IEnumerable rule) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { string[] ruleArray = rule as string[] ?? rule.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -234,13 +250,13 @@ public bool RemovePolicy(string section, string policyType, IEnumerable public bool RemovePolicies(string section, string policyType, IEnumerable> rules) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { var rulesArray = rules as IEnumerable[] ?? rules.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -267,13 +283,13 @@ public bool RemovePolicies(string section, string policyType, IEnumerable> RemoveFilteredPolicy(string section, string policyType, int fieldIndex, params string[] fieldValues) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return null; - } + return null; + } + try + { if (HasAdapter is false || AutoSave is false) { return Policy.RemoveFilteredPolicy(section, policyType, fieldIndex, fieldValues); @@ -298,13 +314,13 @@ public IEnumerable> RemoveFilteredPolicy(string section, str public async Task AddPolicyAsync(string section, string policyType, IEnumerable rule) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { IEnumerable ruleArray = rule as string[] ?? rule.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -331,13 +347,13 @@ public async Task AddPolicyAsync(string section, string policyType, IEnume public async Task AddPoliciesAsync(string section, string policyType, IEnumerable> rules) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { var rulesArray = rules as IEnumerable[] ?? rules.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -364,13 +380,13 @@ public async Task AddPoliciesAsync(string section, string policyType, IEnu public async Task RemovePolicyAsync(string section, string policyType, IEnumerable rule) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { string[] ruleArray = rule as string[] ?? rule.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -397,13 +413,13 @@ public async Task RemovePolicyAsync(string section, string policyType, IEn public async Task RemovePoliciesAsync(string section, string policyType, IEnumerable> rules) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return false; - } + return false; + } + try + { var rulesArray = rules as IEnumerable[] ?? rules.ToArray(); if (HasAdapter is false || AutoSave is false) @@ -430,13 +446,13 @@ public async Task RemovePoliciesAsync(string section, string policyType, I public async Task>> RemoveFilteredPolicyAsync(string section, string policyType, int fieldIndex, params string[] fieldValues) { - try + if (TryStartWrite() is false) { - if (TryStartWrite() is false) - { - return null; - } + return null; + } + try + { if (HasAdapter is false || AutoSave is false) { return Policy.RemoveFilteredPolicy(section, policyType, fieldIndex, fieldValues); @@ -461,12 +477,10 @@ public async Task>> RemoveFilteredPolicyAsync(st public void ClearPolicy() { + StartRead(); try { - if (TryStartRead()) - { - Policy.ClearPolicy(); - } + Policy.ClearPolicy(); } finally { diff --git a/NetCasbin/Model/ReaderWriterPolicyManager.cs b/NetCasbin/Model/ReaderWriterPolicyManager.cs new file mode 100644 index 00000000..6b775637 --- /dev/null +++ b/NetCasbin/Model/ReaderWriterPolicyManager.cs @@ -0,0 +1,60 @@ +using System.Threading; +using Casbin.Persist; + +namespace Casbin.Model +{ + public class ReaderWriterPolicyManager : DefaultPolicyManager + { + private readonly ReaderWriterPolicyManagerOptions _options; + private readonly ReaderWriterLockSlim _lockSlim = new(); + + public ReaderWriterPolicyManager(IPolicy policy, IAdapter adapter = null) + : base(policy, adapter) + { + _options = new ReaderWriterPolicyManagerOptions(); + } + + public ReaderWriterPolicyManager(IPolicy policy, ReaderWriterPolicyManagerOptions options, IAdapter adapter = null) + : base(policy, adapter) + { + _options = options; + } + + public static new IPolicyManager Create() + { + return new ReaderWriterPolicyManager(DefaultPolicy.Create()); + } + + public override bool IsSynchronized => true; + + public override void StartRead() + { + _lockSlim.EnterReadLock(); + } + + public override void StartWrite() + { + _lockSlim.EnterWriteLock(); + } + + public override void EndRead() + { + _lockSlim.ExitReadLock(); + } + + public override void EndWrite() + { + _lockSlim.ExitWriteLock(); + } + + public override bool TryStartRead() + { + return _lockSlim.TryEnterReadLock(_options.WaitTimeOut); + } + + public override bool TryStartWrite() + { + return _lockSlim.TryEnterWriteLock(_options.WaitTimeOut); + } + } +} diff --git a/NetCasbin/Model/ReaderWriterPolicyManagerOptions.cs b/NetCasbin/Model/ReaderWriterPolicyManagerOptions.cs new file mode 100644 index 00000000..a41d19b3 --- /dev/null +++ b/NetCasbin/Model/ReaderWriterPolicyManagerOptions.cs @@ -0,0 +1,9 @@ +using System; + +namespace Casbin.Model +{ + public class ReaderWriterPolicyManagerOptions + { + public TimeSpan WaitTimeOut { get; } = TimeSpan.FromMilliseconds(50); + } +} diff --git a/NetCasbin/SyncedEnforcer.cs b/NetCasbin/SyncedEnforcer.cs new file mode 100644 index 00000000..b7540912 --- /dev/null +++ b/NetCasbin/SyncedEnforcer.cs @@ -0,0 +1,28 @@ +using System.IO; +using Casbin.Adapter.File; +using Casbin.Extensions; +using Casbin.Model; +using Casbin.Persist; + +namespace Casbin +{ + public static class SyncedEnforcer + { + public static IEnforcer Create(string modelPath, string policyPath) + { + return Create(modelPath, new FileAdapter(policyPath)); + } + + public static IEnforcer Create(string modelPath, IAdapter adapter = null) + { + IModel model = DefaultModel.CreateFromFile(modelPath); + return Create(model, adapter); + } + + public static IEnforcer Create(IModel model, IAdapter adapter = null) + { + model = model.ReplacePolicyManager(ReaderWriterPolicyManager.Create()); + return new Enforcer(model, adapter); + } + } +}