From 8a779717120fedc5a328f4b4932c9dd53b9cab02 Mon Sep 17 00:00:00 2001 From: sagilio Date: Mon, 24 Oct 2022 00:35:43 +0800 Subject: [PATCH] feat: Add policy filter Signed-off-by: sagilio --- Casbin.UnitTests/ModelTests/EnforcerTest.cs | 146 ++++++------ Casbin/Abstractions/IEnforcer.cs | 13 -- .../Abstractions/Persist/IFilteredAdapter.cs | 4 +- .../Abstractions/Persist/IPersistedPolicy.cs | 22 ++ Casbin/Abstractions/Persist/IPolicyFilter.cs | 13 ++ Casbin/Adapter/File/FileAdapter.cs | 220 +++++++++--------- Casbin/Adapter/File/FileFilteredAdapter.cs | 172 ++++++-------- Casbin/Adapter/Stream/StreamAdapter.cs | 197 ++++++++-------- .../Adapter/Stream/StreamFilteredAdapter.cs | 148 +++++------- Casbin/DefaultEnforcer.cs | 29 --- Casbin/EnforceOptions.cs | 16 ++ Casbin/Enforcer.Internal.cs | 2 + Casbin/Enforcer.cs | 48 ++-- .../Extensions/Enforcer/EnforcerExtension.cs | 22 +- .../InternalEnforcerExtension.Events.cs | 6 +- Casbin/Extensions/Model/ModelExtension.cs | 4 +- .../Persist/PolicyFilterExtension.cs | 15 ++ Casbin/Persist/Filter.cs | 58 ++++- Casbin/Persist/PersistantPolicy.cs | 141 +++++++++++ Casbin/Persist/PolicyFilter.cs | 177 ++++++++++++++ 20 files changed, 884 insertions(+), 569 deletions(-) create mode 100644 Casbin/Abstractions/Persist/IPersistedPolicy.cs create mode 100644 Casbin/Abstractions/Persist/IPolicyFilter.cs delete mode 100644 Casbin/DefaultEnforcer.cs create mode 100644 Casbin/EnforceOptions.cs create mode 100644 Casbin/Extensions/Persist/PolicyFilterExtension.cs create mode 100644 Casbin/Persist/PersistantPolicy.cs create mode 100644 Casbin/Persist/PolicyFilter.cs diff --git a/Casbin.UnitTests/ModelTests/EnforcerTest.cs b/Casbin.UnitTests/ModelTests/EnforcerTest.cs index 9c123bb6..47baed17 100644 --- a/Casbin.UnitTests/ModelTests/EnforcerTest.cs +++ b/Casbin.UnitTests/ModelTests/EnforcerTest.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; using Casbin.Adapter.File; +using Casbin.Adapter.Stream; using Casbin.Model; using Casbin.Persist; using Casbin.Rbac; @@ -73,10 +73,10 @@ public void TestEnforceWithoutAutoLoadPolicy() FileAdapter a = new("examples/keymatch_policy.csv"); - IEnforcer e = DefaultEnforcer.Create(m, a, options => { options.AutoLoadPolicy = false; }); + IEnforcer e = new Enforcer(m, a, options => { options.AutoLoadPolicy = false; }); Assert.Empty(e.GetPolicy()); - e = DefaultEnforcer.Create(m, a); + e = new Enforcer(m, a); Assert.NotEmpty(e.GetPolicy()); } @@ -257,19 +257,11 @@ public void TestInOperator() _testModelFixture._rbacInOperatorModelText, _testModelFixture._rbacInOperatorPolicyText)); - TestEnforce(e, new - { - Name = "Alice", - Amount = 5100, - Roles = new string[] { "Manager", "DepartmentDirector" } - }, "authorization", "grant", true); + TestEnforce(e, new { Name = "Alice", Amount = 5100, Roles = new[] { "Manager", "DepartmentDirector" } }, + "authorization", "grant", true); - TestEnforce(e, new - { - Name = "Alice", - Amount = 5100, - Roles = new string[] { "DepartmentDirector" } - }, "authorization", "grant", false); + TestEnforce(e, new { Name = "Alice", Amount = 5100, Roles = new[] { "DepartmentDirector" } }, + "authorization", "grant", false); } [Fact] @@ -354,16 +346,16 @@ public void TestRbacBatchEnforceInMemory() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), true), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), true), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), false), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), true) - }; + { + (Request.CreateValues("alice", "data1", "read"), true), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), true), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), false), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), true) + }; TestBatchEnforce(e, testCases); } @@ -388,16 +380,16 @@ public void TestRbacParallelBatchEnforceInMemory() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), true), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), true), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), false), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), true) - }; + { + (Request.CreateValues("alice", "data1", "read"), true), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), true), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), false), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), true) + }; TestParallelBatchEnforce(e, testCases); } @@ -450,16 +442,16 @@ public void TestRbacBatchEnforceInMemoryAsync() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), true), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), true), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), false), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), true) - }; + { + (Request.CreateValues("alice", "data1", "read"), true), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), true), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), false), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), true) + }; TestBatchEnforceAsync(e, testCases); } @@ -1178,16 +1170,16 @@ public void TestBatchEnforceWithMatcherApi() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), false), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), false), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), true), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), false) - }; + { + (Request.CreateValues("alice", "data1", "read"), false), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), false), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), true), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), false) + }; e.TestBatchEnforceWithMatcher(matcher, testCases); } @@ -1200,16 +1192,16 @@ public void TestBatchEnforceWithMatcherParallel() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), false), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), false), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), true), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), false) - }; + { + (Request.CreateValues("alice", "data1", "read"), false), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), false), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), true), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), false) + }; e.TestBatchEnforceWithMatcherParallel(matcher, testCases); } @@ -1238,16 +1230,16 @@ public void TestBatchEnforceWithMatcherApiAsync() IEnumerable<(RequestValues, bool)> testCases = new (RequestValues, bool)[] - { - (Request.CreateValues("alice", "data1", "read"), false), - (Request.CreateValues("alice", "data1", "write"), false), - (Request.CreateValues("alice", "data2", "read"), false), - (Request.CreateValues("alice", "data2", "write"), true), - (Request.CreateValues("bob", "data1", "read"), true), - (Request.CreateValues("bob", "data1", "write"), false), - (Request.CreateValues("bob", "data2", "read"), false), - (Request.CreateValues("bob", "data2", "write"), false) - }; + { + (Request.CreateValues("alice", "data1", "read"), false), + (Request.CreateValues("alice", "data1", "write"), false), + (Request.CreateValues("alice", "data2", "read"), false), + (Request.CreateValues("alice", "data2", "write"), true), + (Request.CreateValues("bob", "data1", "read"), true), + (Request.CreateValues("bob", "data1", "write"), false), + (Request.CreateValues("bob", "data2", "read"), false), + (Request.CreateValues("bob", "data2", "write"), false) + }; TestBatchEnforceWithMatcherAsync(e, matcher, testCases); } @@ -1288,3 +1280,5 @@ public async Task TestEnforceExWithMatcherAsync() #endregion } + + diff --git a/Casbin/Abstractions/IEnforcer.cs b/Casbin/Abstractions/IEnforcer.cs index a31edf36..4d2f8d52 100644 --- a/Casbin/Abstractions/IEnforcer.cs +++ b/Casbin/Abstractions/IEnforcer.cs @@ -22,18 +22,6 @@ namespace Casbin /// public interface IEnforcer { - public class EnforcerOptions - { - public bool Enabled { get; set; } = true; - public bool EnabledCache { get; set; } = true; - - public bool AutoBuildRoleLinks { get; set; } = true; - public bool AutoNotifyWatcher { get; set; } = true; - public bool AutoCleanEnforceCache { get; set; } = true; - public bool AutoLoadPolicy { get; set; } = true; - public Filter AutoLoadPolicyFilter { get; set; } = null; - } - /// /// Decides whether a "subject" can access a "object" with the operation /// "action", input parameters are usually: (sub, obj, act). @@ -88,7 +76,6 @@ public IEnumerable BatchEnforce(EnforceContext context, IEnumera #region Options - public EnforcerOptions Options { get; set; } public bool Enabled { get; set; } public bool EnabledCache { get; set; } public bool AutoBuildRoleLinks { get; set; } diff --git a/Casbin/Abstractions/Persist/IFilteredAdapter.cs b/Casbin/Abstractions/Persist/IFilteredAdapter.cs index b5910409..7d4f3018 100644 --- a/Casbin/Abstractions/Persist/IFilteredAdapter.cs +++ b/Casbin/Abstractions/Persist/IFilteredAdapter.cs @@ -7,8 +7,8 @@ public interface IFilteredAdapter { bool IsFiltered { get; } - void LoadFilteredPolicy(IPolicyStore store, Filter filter); + void LoadFilteredPolicy(IPolicyStore store, IPolicyFilter filter); - Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter); + Task LoadFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter); } } diff --git a/Casbin/Abstractions/Persist/IPersistedPolicy.cs b/Casbin/Abstractions/Persist/IPersistedPolicy.cs new file mode 100644 index 00000000..a4c65c84 --- /dev/null +++ b/Casbin/Abstractions/Persist/IPersistedPolicy.cs @@ -0,0 +1,22 @@ +using Casbin.Model; + +namespace Casbin.Persist; + +public interface IPersistantPolicy +{ + public string Type { get; set; } + public string Value1 { get; set; } + public string Value2 { get; set; } + public string Value3 { get; set; } + public string Value4 { get; set; } + public string Value5 { get; set; } + public string Value6 { get; set; } + public string Value7 { get; set; } + public string Value8 { get; set; } + public string Value9 { get; set; } + public string Value10 { get; set; } + public string Value11 { get; set; } + public string Value12 { get; set; } + public IPolicyValues Values { get; } +} + diff --git a/Casbin/Abstractions/Persist/IPolicyFilter.cs b/Casbin/Abstractions/Persist/IPolicyFilter.cs new file mode 100644 index 00000000..f78a5190 --- /dev/null +++ b/Casbin/Abstractions/Persist/IPolicyFilter.cs @@ -0,0 +1,13 @@ +using System.Linq; + +namespace Casbin.Persist; + +public interface IPolicyFilter : IPolicyFilter +{ +} + +public interface IPolicyFilter where T : IPersistantPolicy +{ + public IQueryable ApplyFilter(IQueryable policies); +} + diff --git a/Casbin/Adapter/File/FileAdapter.cs b/Casbin/Adapter/File/FileAdapter.cs index a068a256..09aa3fab 100644 --- a/Casbin/Adapter/File/FileAdapter.cs +++ b/Casbin/Adapter/File/FileAdapter.cs @@ -8,155 +8,155 @@ using Casbin.Model; using Casbin.Persist; -namespace Casbin.Adapter.File +namespace Casbin.Adapter.File; + +public class FileAdapter : IEpochAdapter { - public class FileAdapter : IEpochAdapter + private readonly StreamReader _byteArrayInputStream; + private readonly bool _readOnly; + protected readonly string FilePath; + + public FileAdapter(string filePath) => FilePath = filePath; + + public FileAdapter(System.IO.Stream inputStream) { - private readonly StreamReader _byteArrayInputStream; - private readonly bool _readOnly; - protected readonly string FilePath; + _readOnly = true; + try + { + _byteArrayInputStream = new StreamReader(inputStream); + } + catch (IOException e) + { + throw new IOException("File adapter init error", e); + } + } - public FileAdapter(string filePath) + public void LoadPolicy(IPolicyStore model) + { + if (string.IsNullOrWhiteSpace(FilePath) is false) { - FilePath = filePath; + using StreamReader sr = new StreamReader(new FileStream( + FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); + LoadPolicyLine(model, sr); } - public FileAdapter(Stream inputStream) + if (_byteArrayInputStream is not null) { - _readOnly = true; - try - { - _byteArrayInputStream = new StreamReader(inputStream); - } - catch (IOException e) - { - throw new IOException("File adapter init error", e); - } + LoadPolicyLine(model, _byteArrayInputStream); } + } - public void LoadPolicy(IPolicyStore model) + public async Task LoadPolicyAsync(IPolicyStore store) + { + if (string.IsNullOrWhiteSpace(FilePath) is false) { - if (string.IsNullOrWhiteSpace(FilePath) is false) - { - using var sr = new StreamReader(new FileStream( - FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); - LoadPolicyData(model, sr); - } - - if (_byteArrayInputStream is not null) - { - LoadPolicyData(model, _byteArrayInputStream); - } + using StreamReader sr = new StreamReader(new FileStream( + FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + await LoadPolicyLineAsync(store, sr); + Debug.WriteLine("xxx"); } - public async Task LoadPolicyAsync(IPolicyStore store) + if (_byteArrayInputStream is not null) { - if (string.IsNullOrWhiteSpace(FilePath) is false) - { - using var sr = new StreamReader(new FileStream( - FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - await LoadPolicyDataAsync(store, sr); - Debug.WriteLine("xxx"); - } - - if (_byteArrayInputStream is not null) - { - await LoadPolicyDataAsync(store, _byteArrayInputStream); - } + await LoadPolicyLineAsync(store, _byteArrayInputStream); } + } - public void SavePolicy(IPolicyStore store) + public void SavePolicy(IPolicyStore store) + { + if (_byteArrayInputStream != null && _readOnly) { - if (_byteArrayInputStream != null && _readOnly) - { - throw new Exception("Store file can not write, because use inputStream is readOnly"); - } - - if (string.IsNullOrWhiteSpace(FilePath)) - { - throw new ArgumentException("Invalid file path, file path cannot be empty"); - } - - var policy = ConvertToPolicyStrings(store); - SavePolicyFile(string.Join("\n", policy)); + throw new Exception("Store file can not write, because use inputStream is readOnly"); } - public Task SavePolicyAsync(IPolicyStore store) + if (string.IsNullOrWhiteSpace(FilePath)) { - if (_byteArrayInputStream != null && _readOnly) - { - throw new Exception("Store file can not write, because use inputStream is readOnly"); - } - - if (string.IsNullOrWhiteSpace(FilePath)) - { - throw new ArgumentException("Invalid file path, file path cannot be empty"); - } - - var policy = ConvertToPolicyStrings(store); - return SavePolicyFileAsync(string.Join("\n", policy)); + throw new ArgumentException("Invalid file path, file path cannot be empty"); } - private static IEnumerable GetModelPolicy(IPolicyStore store, string section) + IEnumerable policy = ConvertToPolicyStrings(store); + SavePolicyFile(string.Join("\n", policy)); + } + + public Task SavePolicyAsync(IPolicyStore store) + { + if (_byteArrayInputStream != null && _readOnly) { - var policy = new List(); - foreach (KeyValuePair> kv in store.GetPolicyAllType(section)) - { - string key = kv.Key; - IEnumerable value = kv.Value; - policy.AddRange(value.Select(p => $"{key}, {p.ToText()}")); - } - - return policy; + throw new Exception("Store file can not write, because use inputStream is readOnly"); } - private static void LoadPolicyData(IPolicyStore store, StreamReader inputStream) + if (string.IsNullOrWhiteSpace(FilePath)) { - while (inputStream.EndOfStream is false) - { - string line = inputStream.ReadLine(); - store.TryLoadPolicyLine(line); - } + throw new ArgumentException("Invalid file path, file path cannot be empty"); } - private static async Task LoadPolicyDataAsync(IPolicyStore store, StreamReader inputStream) + IEnumerable policy = ConvertToPolicyStrings(store); + return SavePolicyFileAsync(string.Join("\n", policy)); + } + + private static IEnumerable GetModelPolicy(IPolicyStore store, string section) + { + List policy = new List(); + foreach (KeyValuePair> kv in store.GetPolicyAllType(section)) { - while (inputStream.EndOfStream is false) - { - string line = await inputStream.ReadLineAsync(); - store.TryLoadPolicyLine(line); - } + string key = kv.Key; + IEnumerable value = kv.Value; + policy.AddRange(value.Select(p => $"{key}, {p.ToText()}")); } - private static IEnumerable ConvertToPolicyStrings(IPolicyStore store) + return policy; + } + + private static void LoadPolicyLine(IPolicyStore store, StreamReader inputStream) + { + while (inputStream.EndOfStream is false) { - var policy = new List(); - if (store.ContainsNodes(PermConstants.Section.PolicySection)) - { - policy.AddRange(GetModelPolicy(store, PermConstants.Section.PolicySection)); - } - - if (store.ContainsNodes(PermConstants.Section.RoleSection)) - { - policy.AddRange(GetModelPolicy(store, PermConstants.Section.RoleSection)); - } - - return policy; + string line = inputStream.ReadLine(); + store.TryLoadPolicyLine(line); } + } + + private static async Task LoadPolicyLineAsync(IPolicyStore store, StreamReader inputStream) + { + while (inputStream.EndOfStream is false) + { + string line = await inputStream.ReadLineAsync(); + store.TryLoadPolicyLine(line); + } + } - private void SavePolicyFile(string text) + private static IEnumerable ConvertToPolicyStrings(IPolicyStore store) + { + List policy = new List(); + if (store.ContainsNodes(PermConstants.Section.PolicySection)) { - System.IO.File.WriteAllText(FilePath, text, Encoding.UTF8); + policy.AddRange(GetModelPolicy(store, PermConstants.Section.PolicySection)); } - private async Task SavePolicyFileAsync(string text) + if (store.ContainsNodes(PermConstants.Section.RoleSection)) { - text = text ?? string.Empty; - byte[] content = Encoding.UTF8.GetBytes(text); + policy.AddRange(GetModelPolicy(store, PermConstants.Section.RoleSection)); + } + + return policy; + } + + private void SavePolicyFile(string text) => System.IO.File.WriteAllText(FilePath, text, Encoding.UTF8); + + private async Task SavePolicyFileAsync(string text) + { + text ??= string.Empty; + byte[] content = Encoding.UTF8.GetBytes(text); +#if !NETFRAMEWORK && !NETSTANDARD2_0 + await using FileStream fs = new( + FilePath, FileMode.Create, FileAccess.Write, + FileShare.None, 4096, true); +#else using var fs = new FileStream( FilePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true); - await fs.WriteAsync(content, 0, content.Length); - } +#endif + await fs.WriteAsync(content, 0, content.Length); } } + diff --git a/Casbin/Adapter/File/FileFilteredAdapter.cs b/Casbin/Adapter/File/FileFilteredAdapter.cs index 8f11a61d..fc6b9fb9 100644 --- a/Casbin/Adapter/File/FileFilteredAdapter.cs +++ b/Casbin/Adapter/File/FileFilteredAdapter.cs @@ -6,133 +6,107 @@ using Casbin.Model; using Casbin.Persist; -namespace Casbin.Adapter.File +namespace Casbin.Adapter.File; + +public class FileFilteredAdapter : FileAdapter, IFilteredAdapter { - public class FileFilteredAdapter : FileAdapter, IFilteredAdapter + public FileFilteredAdapter(string filePath) : base(filePath) { - public bool IsFiltered { get; private set; } + } - public FileFilteredAdapter(string filePath) : base(filePath) - { - } + public FileFilteredAdapter(System.IO.Stream inputStream) : base(inputStream) + { + } - public FileFilteredAdapter(Stream inputStream) : base(inputStream) - { - } + public bool IsFiltered { get; private set; } - public void LoadFilteredPolicy(IPolicyStore store, Filter filter) + public void LoadFilteredPolicy(IPolicyStore store, IPolicyFilter filter) + { + if (filter is null) { - if (filter is null) - { - LoadPolicy(store); - return; - } - - if (string.IsNullOrWhiteSpace(FilePath)) - { - throw new InvalidOperationException("invalid file path, file path cannot be empty"); - } - - LoadFilteredPolicyFile(store, filter); + LoadPolicy(store); + return; } - public Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter) + if (string.IsNullOrWhiteSpace(FilePath)) { - if (filter is null) - { - return LoadPolicyAsync(store); - } - - if (string.IsNullOrWhiteSpace(FilePath)) - { - throw new InvalidOperationException("invalid file path, file path cannot be empty"); - } - - return LoadFilteredPolicyFileAsync(store, filter); + throw new InvalidOperationException("invalid file path, file path cannot be empty"); } - private void LoadFilteredPolicyFile(IPolicyStore store, Filter filter) + LoadFilteredPolicyFile(store, filter); + } + + public Task LoadFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter) + { + if (filter is null) { - var reader = new StreamReader(new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - while (reader.EndOfStream is false) - { - string line = reader.ReadLine()?.Trim(); - if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter)) - { - return; - } - store.TryLoadPolicyLine(line); - } - IsFiltered = true; + return LoadPolicyAsync(store); } - private async Task LoadFilteredPolicyFileAsync(IPolicyStore store, Filter filter) + if (string.IsNullOrWhiteSpace(FilePath)) { - var reader = new StreamReader(new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - while (reader.EndOfStream is false) - { - string line = (await reader.ReadLineAsync())?.Trim(); - if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter)) - { - return; - } - store.TryLoadPolicyLine(line); - } - IsFiltered = true; + throw new InvalidOperationException("invalid file path, file path cannot be empty"); } - private static bool FilterLine(string line, Filter filter) - { - if (filter == null) - { - return false; - } + return LoadFilteredPolicyFileAsync(store, filter); + } - string[] p = line.Split(','); - if (p.Length == 0) - { - return true; - } + public void LoadFilteredPolicy(IPolicyStore store, Filter filter) + { + IPolicyFilter policyFilter = filter; + LoadFilteredPolicy(store, policyFilter); + } - IEnumerable filterSlice = new List(); - switch (p[0].Trim()) - { - case PermConstants.DefaultPolicyType: - filterSlice = filter.P; - break; - case PermConstants.DefaultGroupingPolicyType: - filterSlice = filter.G; - break; - } + public Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter) + { + IPolicyFilter policyFilter = filter; + return LoadFilteredPolicyAsync(store, policyFilter); + } - return FilterWords(p, filterSlice); + private void LoadFilteredPolicyFile(IPolicyStore store, IPolicyFilter filter) + { + IEnumerable policies = ReadPersistantPolicy(FilePath); + policies = filter.ApplyFilter(policies.AsQueryable()); + foreach (IPersistantPolicy policy in policies) + { + string section = policy.Type.Substring(0, 1); + store.AddPolicy(section, policy.Type, policy.Values); } - private static bool FilterWords(string[] line, IEnumerable filter) - { - string[] filterArray = filter.ToArray(); - int length = filterArray.Length; + IsFiltered = true; + } + + private Task LoadFilteredPolicyFileAsync(IPolicyStore store, IPolicyFilter filter) + { + LoadFilteredPolicyFile(store, filter); +#if NET452 + return Task.FromResult(true); +#else + return Task.CompletedTask; +#endif + } - if (line.Length < length + 1) + private static IEnumerable ReadPersistantPolicy(string filePath) + { + using StreamReader reader = new(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + while (reader.EndOfStream is false) + { + string line = reader.ReadLine(); + if (string.IsNullOrWhiteSpace(line)) { - return true; + continue; } - bool skipLine = false; - for (int i = 0; i < length; i++) + if (line.StartsWith("/") || line.StartsWith("#")) { - string current = filterArray.ElementAt(i).Trim(); - string next = filterArray.ElementAt(i + 1); - - if (string.IsNullOrEmpty(current) || current == next) - { - continue; - } - - skipLine = true; - break; + continue; } - return skipLine; + + string[] tokens = line.Split(PermConstants.PolicySeparatorChar).Select(x => x.Trim()).ToArray(); + string type = tokens[0]; + IPolicyValues values = Policy.ValuesFrom(tokens.Skip(1)); + yield return new PersistantPolicy(type, values); } } } + diff --git a/Casbin/Adapter/Stream/StreamAdapter.cs b/Casbin/Adapter/Stream/StreamAdapter.cs index 2a27a142..03f67936 100644 --- a/Casbin/Adapter/Stream/StreamAdapter.cs +++ b/Casbin/Adapter/Stream/StreamAdapter.cs @@ -1,152 +1,137 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using Casbin.Model; using Casbin.Persist; -namespace Casbin.Adapter.File +namespace Casbin.Adapter.Stream; + +internal class StreamAdapter : IEpochAdapter { - public class StreamAdapter : IEpochAdapter + protected readonly System.IO.Stream InputStream; + protected readonly System.IO.Stream OutputStream; + + public StreamAdapter(System.IO.Stream inputStream, System.IO.Stream outputStream) + { + InputStream = inputStream; + OutputStream = outputStream; + } + + public void LoadPolicy(IPolicyStore store) { - protected readonly Stream _byteArrayInputStream; - protected readonly Stream _byteArrayOutputStream; - public StreamAdapter(Stream inputStream, Stream outputStream) + using StreamReader streamReader = new StreamReader(InputStream); + if (InputStream is not null) { - _byteArrayInputStream = inputStream; - _byteArrayOutputStream = outputStream; + LoadPolicyData(store, streamReader); } + } - public void LoadPolicy(IPolicyStore store) + public async Task LoadPolicyAsync(IPolicyStore store) + { + using StreamReader streamReader = new StreamReader(InputStream); + if (InputStream is not null) { - try - { - var streamReader = new StreamReader(_byteArrayInputStream); - if (_byteArrayInputStream is not null) - { - LoadPolicyData(store, streamReader); - } - streamReader.Dispose(); - } - catch (Exception) - { - - } + await LoadPolicyDataAsync(store, streamReader); } + } - public async Task LoadPolicyAsync(IPolicyStore store) + public void SavePolicy(IPolicyStore store) + { + if (OutputStream is null) { - try - { - var streamReader = new StreamReader(_byteArrayInputStream); - if (_byteArrayInputStream is not null) - { - await LoadPolicyDataAsync(store, streamReader); - } - streamReader.Dispose(); - } - catch (Exception) - { - - } + throw new Exception("Store file can not write, because use outputStream has not been set."); } - public void SavePolicy(IPolicyStore store) - { - if (_byteArrayOutputStream is null) - { - throw new Exception("Store file can not write, because use outputStream has not been set."); - } + IEnumerable policy = ConvertToPolicyStrings(store); + SavePolicyFile(string.Join("\n", policy)); + } - var policy = ConvertToPolicyStrings(store); - SavePolicyFile(string.Join("\n", policy)); + public Task SavePolicyAsync(IPolicyStore store) + { + if (OutputStream is null) + { + throw new Exception("Store file can not write, because use outputStream has not been set."); } - public Task SavePolicyAsync(IPolicyStore store) + IEnumerable policy = ConvertToPolicyStrings(store); + return SavePolicyFileAsync(string.Join("\n", policy)); + } + + private static IEnumerable GetModelPolicy(IPolicyStore store, string section) + { + List policy = new List(); + foreach (KeyValuePair> kv in store.GetPolicyAllType(section)) { - if (_byteArrayOutputStream is null) - { - throw new Exception("Store file can not write, because use outputStream has not been set."); - } + string key = kv.Key; + IEnumerable value = kv.Value; + policy.AddRange(value.Select(p => $"{key}, {p.ToText()}")); + } + + return policy; + } - var policy = ConvertToPolicyStrings(store); - return SavePolicyFileAsync(string.Join("\n", policy)); + private static void LoadPolicyData(IPolicyStore store, StreamReader inputStream) + { + if (inputStream.EndOfStream is true) + { + inputStream.BaseStream.Position = 0; } - private static IEnumerable GetModelPolicy(IPolicyStore store, string section) + while (inputStream.EndOfStream is false) { - var policy = new List(); - foreach (var kv in store.GetPolicyAllType(section)) - { - var key = kv.Key; - var value = kv.Value; - policy.AddRange(value.Select(p => $"{key}, {p.ToText()}")); - } - - return policy; + string line = inputStream.ReadLine(); + store.TryLoadPolicyLine(line); } + } - private static void LoadPolicyData(IPolicyStore store, StreamReader inputStream) + private static async Task LoadPolicyDataAsync(IPolicyStore store, StreamReader inputStream) + { + if (inputStream.EndOfStream is true) { - if (inputStream.EndOfStream is true) - { - inputStream.BaseStream.Position = 0; - } - while (inputStream.EndOfStream is false) - { - string line = inputStream.ReadLine(); - store.TryLoadPolicyLine(line); - } + inputStream.BaseStream.Position = 0; } - private static async Task LoadPolicyDataAsync(IPolicyStore store, StreamReader inputStream) + while (inputStream.EndOfStream is false) { - if (inputStream.EndOfStream is true) - { - inputStream.BaseStream.Position = 0; - } - while (inputStream.EndOfStream is false) - { - string line = await inputStream.ReadLineAsync(); - store.TryLoadPolicyLine(line); - } + string line = await inputStream.ReadLineAsync(); + store.TryLoadPolicyLine(line); } + } - private static IEnumerable ConvertToPolicyStrings(IPolicyStore store) + private static IEnumerable ConvertToPolicyStrings(IPolicyStore store) + { + List policy = new List(); + if (store.ContainsNodes(PermConstants.Section.PolicySection)) { - var policy = new List(); - if (store.ContainsNodes(PermConstants.Section.PolicySection)) - { - policy.AddRange(GetModelPolicy(store, PermConstants.Section.PolicySection)); - } - - if (store.ContainsNodes(PermConstants.Section.RoleSection)) - { - policy.AddRange(GetModelPolicy(store, PermConstants.Section.RoleSection)); - } - - return policy; + policy.AddRange(GetModelPolicy(store, PermConstants.Section.PolicySection)); } - private void SavePolicyFile(string text) + if (store.ContainsNodes(PermConstants.Section.RoleSection)) { - var streamWriter = new StreamWriter(_byteArrayOutputStream); + policy.AddRange(GetModelPolicy(store, PermConstants.Section.RoleSection)); + } + + return policy; + } + + private void SavePolicyFile(string text) + { + StreamWriter streamWriter = new StreamWriter(OutputStream); #if (NET6_0 || NET5_0 || NETCOREAPP3_1) streamWriter.Write(text.AsSpan()); #else - streamWriter.Write(text.ToCharArray()); + streamWriter.Write(text.ToCharArray()); #endif - streamWriter.Dispose(); - } + streamWriter.Dispose(); + } - private async Task SavePolicyFileAsync(string text) - { - var streamWriter = new StreamWriter(_byteArrayOutputStream); - await streamWriter.WriteAsync(text); - streamWriter.Dispose(); - } + private async Task SavePolicyFileAsync(string text) + { + StreamWriter streamWriter = new StreamWriter(OutputStream); + await streamWriter.WriteAsync(text); + streamWriter.Dispose(); } } + diff --git a/Casbin/Adapter/Stream/StreamFilteredAdapter.cs b/Casbin/Adapter/Stream/StreamFilteredAdapter.cs index 60ffd2cf..e8725412 100644 --- a/Casbin/Adapter/Stream/StreamFilteredAdapter.cs +++ b/Casbin/Adapter/Stream/StreamFilteredAdapter.cs @@ -1,124 +1,86 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Casbin.Model; using Casbin.Persist; -namespace Casbin.Adapter.File +namespace Casbin.Adapter.Stream; + +internal class StreamFilteredAdapter : StreamAdapter, IFilteredAdapter { - public class StreamFilteredAdapter : StreamAdapter, IFilteredAdapter + public StreamFilteredAdapter(System.IO.Stream inputStream, System.IO.Stream outputStream) : base(inputStream, + outputStream) { - public bool IsFiltered { get; private set; } + } - public StreamFilteredAdapter(Stream inputStream, Stream outputStream) : base(inputStream, outputStream) - { - } + public bool IsFiltered { get; private set; } - public void LoadFilteredPolicy(IPolicyStore store, Filter filter) + public void LoadFilteredPolicy(IPolicyStore store, IPolicyFilter filter) + { + if (filter is null) { - if (filter is null) - { - LoadPolicy(store); - return; - } - - LoadFilteredPolicyFile(store, filter); + LoadPolicy(store); + return; } - public Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter) - { - if (filter is null) - { - return LoadPolicyAsync(store); - } - - return LoadFilteredPolicyFileAsync(store, filter); - } + LoadFilteredPolicyFile(store, filter); + } - private void LoadFilteredPolicyFile(IPolicyStore store, Filter filter) + public Task LoadFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter) + { + if (filter is null) { - var reader = new StreamReader(_byteArrayInputStream); - while (reader.EndOfStream is false) - { - string line = reader.ReadLine()?.Trim(); - if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter)) - { - return; - } - store.TryLoadPolicyLine(line); - } - IsFiltered = true; + return LoadPolicyAsync(store); } - private async Task LoadFilteredPolicyFileAsync(IPolicyStore store, Filter filter) - { - var reader = new StreamReader(_byteArrayInputStream); - while (reader.EndOfStream is false) - { - string line = (await reader.ReadLineAsync())?.Trim(); - if (string.IsNullOrWhiteSpace(line) || FilterLine(line, filter)) - { - return; - } - store.TryLoadPolicyLine(line); - } - IsFiltered = true; - } + return LoadFilteredPolicyFileAsync(store, filter); + } - private static bool FilterLine(string line, Filter filter) + private void LoadFilteredPolicyFile(IPolicyStore store, IPolicyFilter filter) + { + IEnumerable policies = ReadPersistantPolicy(InputStream); + policies = filter.ApplyFilter(policies.AsQueryable()); + foreach (IPersistantPolicy policy in policies) { - if (filter == null) - { - return false; - } - - string[] p = line.Split(','); - if (p.Length == 0) - { - return true; - } + string section = policy.Type.Substring(0, 1); + store.AddPolicy(section, policy.Type, policy.Values); + } - IEnumerable filterSlice = new List(); - switch (p[0].Trim()) - { - case PermConstants.DefaultPolicyType: - filterSlice = filter.P; - break; - case PermConstants.DefaultGroupingPolicyType: - filterSlice = filter.G; - break; - } + IsFiltered = true; + } - return FilterWords(p, filterSlice); - } + private Task LoadFilteredPolicyFileAsync(IPolicyStore store, IPolicyFilter filter) + { + LoadFilteredPolicyFile(store, filter); +#if NET452 + return Task.FromResult(true); +#else + return Task.CompletedTask; +#endif + } - private static bool FilterWords(string[] line, IEnumerable filter) + private static IEnumerable ReadPersistantPolicy(System.IO.Stream inputStream) + { + using StreamReader reader = new(inputStream); + while (reader.EndOfStream is false) { - string[] filterArray = filter.ToArray(); - int length = filterArray.Length; - - if (line.Length < length + 1) + string line = reader.ReadLine(); + if (string.IsNullOrWhiteSpace(line)) { - return true; + continue; } - bool skipLine = false; - for (int i = 0; i < length; i++) + if (line.StartsWith("/") || line.StartsWith("#")) { - string current = filterArray.ElementAt(i).Trim(); - string next = filterArray.ElementAt(i + 1); - - if (string.IsNullOrEmpty(current) || current == next) - { - continue; - } - - skipLine = true; - break; + continue; } - return skipLine; + + string[] tokens = line.Split(PermConstants.PolicySeparatorChar).Select(x => x.Trim()).ToArray(); + string type = tokens[0]; + IPolicyValues values = Policy.ValuesFrom(tokens.Skip(1)); + yield return new PersistantPolicy(type, values); } } } + diff --git a/Casbin/DefaultEnforcer.cs b/Casbin/DefaultEnforcer.cs deleted file mode 100644 index cf633ba5..00000000 --- a/Casbin/DefaultEnforcer.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Casbin.Model; -using Casbin.Persist; - -namespace Casbin -{ - public static class DefaultEnforcer - { - public static IEnforcer Create(IReadOnlyAdapter adapter = null, Action optionSettings = null) - { - return new Enforcer(DefaultModel.Create(), adapter, optionSettings); - } - - public static IEnforcer Create(string modelPath, string policyPath, Action optionSettings = null) - { - return new Enforcer(modelPath, policyPath, optionSettings); - } - - public static IEnforcer Create(string modelPath, IReadOnlyAdapter adapter = null, Action optionSettings = null) - { - return new Enforcer(modelPath, adapter, optionSettings); - } - - public static IEnforcer Create(IModel model, IReadOnlyAdapter adapter = null, Action optionSettings = null) - { - return new Enforcer(model, adapter, optionSettings); - } - } -} diff --git a/Casbin/EnforceOptions.cs b/Casbin/EnforceOptions.cs new file mode 100644 index 00000000..72ec87fd --- /dev/null +++ b/Casbin/EnforceOptions.cs @@ -0,0 +1,16 @@ +using Casbin.Persist; + +namespace Casbin; + +public class EnforcerOptions +{ + public bool Enabled { get; set; } = true; + public bool EnabledCache { get; set; } = true; + + public bool AutoBuildRoleLinks { get; set; } = true; + public bool AutoNotifyWatcher { get; set; } = true; + public bool AutoCleanEnforceCache { get; set; } = true; + public bool AutoLoadPolicy { get; set; } = true; + public IPolicyFilter AutoLoadPolicyFilter { get; set; } = new Filter(); +} + diff --git a/Casbin/Enforcer.Internal.cs b/Casbin/Enforcer.Internal.cs index e498ccd0..c7d58dc6 100644 --- a/Casbin/Enforcer.Internal.cs +++ b/Casbin/Enforcer.Internal.cs @@ -333,3 +333,5 @@ private static string RewriteInOperator(in EnforceContext context, string expres return expressionString; } } + + diff --git a/Casbin/Enforcer.cs b/Casbin/Enforcer.cs index 54b92a5b..ae7b8279 100644 --- a/Casbin/Enforcer.cs +++ b/Casbin/Enforcer.cs @@ -18,33 +18,42 @@ public Enforcer() { } - public Enforcer(string modelPath, string policyPath, Action optionSettings = null) + public Enforcer(string modelPath, string policyPath, Action optionSettings = null) : this(modelPath, new FileAdapter(policyPath), optionSettings) { } - public Enforcer(string modelPath, IReadOnlyAdapter adapter = null, Action optionSettings = null) + public Enforcer(string modelPath, IReadOnlyAdapter adapter = null, + Action optionSettings = null) : this(DefaultModel.CreateFromFile(modelPath), adapter, optionSettings) { } - public Enforcer(IModel model, IReadOnlyAdapter adapter = null, Action optionSettings = null) + public Enforcer(IModel model, IReadOnlyAdapter adapter = null, Action optionSettings = null) { - if(optionSettings is not null) + EnforcerOptions options = new(); + if (optionSettings is not null) { - optionSettings(Options); + optionSettings(options); } + + Enabled = options.Enabled; + EnabledCache = options.EnabledCache; + AutoNotifyWatcher = options.AutoNotifyWatcher; + AutoBuildRoleLinks = options.AutoBuildRoleLinks; + AutoCleanEnforceCache = options.AutoCleanEnforceCache; + this.SetModel(model); if (adapter is not null) { this.SetAdapter(adapter); } - if (Options.AutoLoadPolicy is true) + if (options.AutoLoadPolicy) { - if(Adapter is IFilteredAdapter && Options.AutoLoadPolicyFilter is not null) + if (Adapter is IFilteredAdapter && options.AutoLoadPolicyFilter is not null) { - this.LoadFilteredPolicy(Options.AutoLoadPolicyFilter); + this.LoadFilteredPolicy(options.AutoLoadPolicyFilter); } else { @@ -57,12 +66,11 @@ public Enforcer(IModel model, IReadOnlyAdapter adapter = null, Action Options.Enabled; set => Options.Enabled = value; } - public bool EnabledCache { get => Options.EnabledCache; set => Options.EnabledCache = value; } - public bool AutoBuildRoleLinks { get => Options.AutoBuildRoleLinks; set => Options.AutoBuildRoleLinks = value; } - public bool AutoNotifyWatcher { get => Options.AutoNotifyWatcher; set => Options.AutoNotifyWatcher = value; } - public bool AutoCleanEnforceCache { get => Options.AutoCleanEnforceCache; set => Options.AutoCleanEnforceCache = value; } + public bool Enabled { get; set; } + public bool EnabledCache { get; set; } + public bool AutoBuildRoleLinks { get; set; } + public bool AutoNotifyWatcher { get; set; } + public bool AutoCleanEnforceCache { get; set; } #endregion @@ -115,12 +123,12 @@ public IEnforceCache EnforceCache return InternalEnforce(in context, in requestValues); } - if (Options.Enabled is false) + if (Enabled is false) { return true; } - if (Options.EnabledCache) + if (EnabledCache) { if (EnforceCache.TryGetResult(requestValues, out bool cachedResult)) { @@ -133,7 +141,7 @@ public IEnforceCache EnforceCache bool result = InternalEnforce(in context, in requestValues); - if (Options.EnabledCache) + if (EnabledCache) { EnforceCache.TrySetResult(requestValues, result); } @@ -159,12 +167,12 @@ public async Task EnforceAsync(EnforceContext context, TRequest return await InternalEnforceAsync(context, requestValues); } - if (Options.Enabled is false) + if (Enabled is false) { return true; } - if (Options.EnabledCache) + if (EnabledCache) { bool? cachedResult = await EnforceCache.TryGetResultAsync(requestValues); if (cachedResult.HasValue) @@ -179,7 +187,7 @@ public async Task EnforceAsync(EnforceContext context, TRequest context.HandleOptionAndCached = true; bool result = await InternalEnforceAsync(context, requestValues); - if (Options.EnabledCache) + if (EnabledCache) { await EnforceCache.TrySetResultAsync(requestValues, result); } diff --git a/Casbin/Extensions/Enforcer/EnforcerExtension.cs b/Casbin/Extensions/Enforcer/EnforcerExtension.cs index 6f2db3dc..6f18bcb2 100644 --- a/Casbin/Extensions/Enforcer/EnforcerExtension.cs +++ b/Casbin/Extensions/Enforcer/EnforcerExtension.cs @@ -42,7 +42,7 @@ public static void LoadModel(this IEnforcer enforcer) /// public static IEnforcer EnableEnforce(this IEnforcer enforcer, bool enable) { - enforcer.Options.Enabled = enable; + enforcer.Enabled = enable; return enforcer; } @@ -66,7 +66,7 @@ public static IEnforcer EnableAutoSave(this IEnforcer enforcer, bool autoSave) /// Whether to automatically build the role links. public static IEnforcer EnableAutoBuildRoleLinks(this IEnforcer enforcer, bool autoBuildRoleLinks) { - enforcer.Options.AutoBuildRoleLinks = autoBuildRoleLinks; + enforcer.AutoBuildRoleLinks = autoBuildRoleLinks; return enforcer; } @@ -78,19 +78,19 @@ public static IEnforcer EnableAutoBuildRoleLinks(this IEnforcer enforcer, bool a /// Whether to automatically notify watcher. public static IEnforcer EnableAutoNotifyWatcher(this IEnforcer enforcer, bool autoNotifyWatcher) { - enforcer.Options.AutoNotifyWatcher = autoNotifyWatcher; + enforcer.AutoNotifyWatcher = autoNotifyWatcher; return enforcer; } public static IEnforcer EnableCache(this IEnforcer enforcer, bool enableCache) { - enforcer.Options.EnabledCache = enableCache; + enforcer.EnabledCache = enableCache; return enforcer; } public static IEnforcer EnableAutoCleanEnforceCache(this IEnforcer enforcer, bool autoCleanEnforceCache) { - enforcer.Options.AutoCleanEnforceCache = autoCleanEnforceCache; + enforcer.AutoCleanEnforceCache = autoCleanEnforceCache; return enforcer; } @@ -186,7 +186,7 @@ public static bool LoadPolicy(this IEnforcer enforcer) enforcer.ClearCache(); enforcer.Model.SortPolicy(); - if (enforcer.Options.AutoBuildRoleLinks) + if (enforcer.AutoBuildRoleLinks) { enforcer.BuildRoleLinks(); } @@ -207,7 +207,7 @@ public static async Task LoadPolicyAsync(this IEnforcer enforcer) enforcer.ClearCache(); enforcer.Model.SortPolicy(); - if (enforcer.Options.AutoBuildRoleLinks) + if (enforcer.AutoBuildRoleLinks) { enforcer.BuildRoleLinks(); } @@ -221,7 +221,7 @@ public static async Task LoadPolicyAsync(this IEnforcer enforcer) /// /// The filter used to specify which type of policy should be loaded. /// - public static bool LoadFilteredPolicy(this IEnforcer enforcer, Filter filter) + public static bool LoadFilteredPolicy(this IEnforcer enforcer, IPolicyFilter filter) { bool result = enforcer.Model.LoadFilteredPolicy(filter); if (result is false) @@ -229,7 +229,7 @@ public static bool LoadFilteredPolicy(this IEnforcer enforcer, Filter filter) return false; } - if (enforcer.Options.AutoBuildRoleLinks) + if (enforcer.AutoBuildRoleLinks) { enforcer.BuildRoleLinks(); } @@ -251,7 +251,7 @@ public static async Task LoadFilteredPolicyAsync(this IEnforcer enforcer, return false; } - if (enforcer.Options.AutoBuildRoleLinks) + if (enforcer.AutoBuildRoleLinks) { enforcer.BuildRoleLinks(); } @@ -336,7 +336,7 @@ public static void BuildRoleLinks(this IEnforcer enforcer) public static void SetRoleManager(this IEnforcer enforcer, string roleType, IRoleManager roleManager) { enforcer.Model.SetRoleManager(roleType, roleManager); - if (enforcer.Options.AutoBuildRoleLinks) + if (enforcer.AutoBuildRoleLinks) { enforcer.BuildRoleLinks(); } diff --git a/Casbin/Extensions/Enforcer/InternalEnforcerExtension.Events.cs b/Casbin/Extensions/Enforcer/InternalEnforcerExtension.Events.cs index 8b3fccf8..cbd6c86a 100644 --- a/Casbin/Extensions/Enforcer/InternalEnforcerExtension.Events.cs +++ b/Casbin/Extensions/Enforcer/InternalEnforcerExtension.Events.cs @@ -71,7 +71,7 @@ internal static void TryBuildIncrementalRoleLinks(this IEnforcer enforcer, Polic internal static void TryCleanEnforceCache(this IEnforcer enforcer) { - if (enforcer.Options.AutoCleanEnforceCache) + if (enforcer.AutoCleanEnforceCache) { enforcer.ClearCache(); } @@ -80,7 +80,7 @@ internal static void TryCleanEnforceCache(this IEnforcer enforcer) internal static void TryNotifyPolicyChanged(this IEnforcer enforcer, PolicyChangedMessage message) { // ReSharper disable once InvertIf - if (enforcer.Options.AutoNotifyWatcher && enforcer.Watcher is not null) + if (enforcer.AutoNotifyWatcher && enforcer.Watcher is not null) { WatcherHolder holder = enforcer.Model.WatcherHolder; if (holder.WatcherEx is not null) @@ -105,7 +105,7 @@ internal static void TryNotifyPolicyChanged(this IEnforcer enforcer, PolicyChang internal static async Task TryNotifyPolicyChangedAsync(this IEnforcer enforcer, PolicyChangedMessage message) { - if (enforcer.Options.AutoNotifyWatcher && enforcer.Watcher is not null) + if (enforcer.AutoNotifyWatcher && enforcer.Watcher is not null) { WatcherHolder holder = enforcer.Model.WatcherHolder; if (holder.WatcherEx is not null) diff --git a/Casbin/Extensions/Model/ModelExtension.cs b/Casbin/Extensions/Model/ModelExtension.cs index 83073fdd..d0e8b592 100644 --- a/Casbin/Extensions/Model/ModelExtension.cs +++ b/Casbin/Extensions/Model/ModelExtension.cs @@ -92,7 +92,7 @@ public static async Task LoadPolicyAsync(this IModel model) return true; } - public static bool LoadFilteredPolicy(this IModel model, Filter filter) + public static bool LoadFilteredPolicy(this IModel model, IPolicyFilter filter) { if (model.AdapterHolder.FilteredAdapter is null) { @@ -103,7 +103,7 @@ public static bool LoadFilteredPolicy(this IModel model, Filter filter) return true; } - public static async Task LoadFilteredPolicyAsync(this IModel model, Filter filter) + public static async Task LoadFilteredPolicyAsync(this IModel model, IPolicyFilter filter) { if (model.AdapterHolder.FilteredAdapter is null) { diff --git a/Casbin/Extensions/Persist/PolicyFilterExtension.cs b/Casbin/Extensions/Persist/PolicyFilterExtension.cs new file mode 100644 index 00000000..8e3d5ada --- /dev/null +++ b/Casbin/Extensions/Persist/PolicyFilterExtension.cs @@ -0,0 +1,15 @@ +using System.Linq; + +namespace Casbin.Persist; + +public static class PolicyFilterExtension +{ + public static IPolicyFilter And(this IPolicyFilter filter1, IPolicyFilter filter2) + where T : IPersistantPolicy => + new PolicyFilter(p => filter1.ApplyFilter(filter1.ApplyFilter(p))); + + public static IPolicyFilter Or(this IPolicyFilter filter1, IPolicyFilter filter2) + where T : IPersistantPolicy => + new PolicyFilter(p => filter1.ApplyFilter(p).Union(filter2.ApplyFilter(p))); +} + diff --git a/Casbin/Persist/Filter.cs b/Casbin/Persist/Filter.cs index 8487bb5e..7a648cd3 100644 --- a/Casbin/Persist/Filter.cs +++ b/Casbin/Persist/Filter.cs @@ -1,11 +1,59 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using Casbin.Model; -namespace Casbin.Persist +namespace Casbin.Persist; + +[Obsolete("Please use PolicyFilter instead")] +public class Filter : IPolicyFilter { - public class Filter + private readonly PolicyFilter _filter; + + public Filter() => _filter = this; + + public IEnumerable G { get; set; } + + public IEnumerable P { get; set; } + + public IQueryable ApplyFilter(IQueryable policies) => + _filter is not null ? _filter.ApplyFilter(policies) : policies; + + public static implicit operator PolicyFilter(Filter filter) { - public IEnumerable G { get; set; } + if (filter is null) + { + return PolicyFilter.Empty; + } - public IEnumerable P { get; set; } + if (filter.P is null && filter.G is null) + { + return PolicyFilter.Empty; + } + + if (filter.P is not null && filter.G is not null) + { + PolicyFilter filterP = new(PermConstants.DefaultPolicyType, 0, + Policy.CreateValues(filter.P)); + PolicyFilter filterG = new(PermConstants.DefaultGroupingPolicyType, 0, + Policy.CreateValues(filter.G)); + return filterP.Or(filterG) as PolicyFilter; + } + + if (filter.P is not null) + { + return new PolicyFilter(PermConstants.DefaultPolicyType, 0, + Policy.CreateValues(filter.P)); + } + + if (filter.G is not null) + { + return new PolicyFilter(PermConstants.DefaultPolicyType, 0, + Policy.CreateValues(filter.P)); + } + + return PolicyFilter.Empty; } } + + diff --git a/Casbin/Persist/PersistantPolicy.cs b/Casbin/Persist/PersistantPolicy.cs new file mode 100644 index 00000000..5da98c78 --- /dev/null +++ b/Casbin/Persist/PersistantPolicy.cs @@ -0,0 +1,141 @@ +using Casbin.Model; + +namespace Casbin.Persist; + +public class PersistantPolicy : IPersistantPolicy +{ + private IPolicyValues _values; + + public PersistantPolicy() + { + } + + public PersistantPolicy(string type, IPolicyValues values) + { + _values = values; + Type = type; + switch (values.Count) + { + case 1: + Value1 = values[0]; + break; + case 2: + Value1 = values[0]; + Value2 = values[1]; + break; + case 3: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + break; + case 4: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + break; + case 5: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + break; + case 6: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + break; + case 7: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + break; + case 8: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + Value8 = values[7]; + break; + case 9: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + Value8 = values[7]; + Value9 = values[8]; + break; + case 10: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + Value8 = values[7]; + Value9 = values[8]; + Value10 = values[9]; + break; + case 11: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + Value8 = values[7]; + Value9 = values[8]; + Value10 = values[9]; + Value11 = values[10]; + break; + case 12: + Value1 = values[0]; + Value2 = values[1]; + Value3 = values[2]; + Value4 = values[3]; + Value5 = values[4]; + Value6 = values[5]; + Value7 = values[6]; + Value8 = values[7]; + Value9 = values[8]; + Value10 = values[9]; + Value11 = values[10]; + Value12 = values[11]; + break; + } + } + + public string Type { get; set; } + public string Value1 { get; set; } + public string Value2 { get; set; } + public string Value3 { get; set; } + public string Value4 { get; set; } + public string Value5 { get; set; } + public string Value6 { get; set; } + public string Value7 { get; set; } + public string Value8 { get; set; } + public string Value9 { get; set; } + public string Value10 { get; set; } + public string Value11 { get; set; } + public string Value12 { get; set; } + + public IPolicyValues Values => _values ??= new StringPolicyValues( + Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8, Value9, Value10, Value11, Value12); +} + diff --git a/Casbin/Persist/PolicyFilter.cs b/Casbin/Persist/PolicyFilter.cs new file mode 100644 index 00000000..28d8f810 --- /dev/null +++ b/Casbin/Persist/PolicyFilter.cs @@ -0,0 +1,177 @@ +using System; +using System.Linq; +using Casbin.Model; + +namespace Casbin.Persist; + +public class PolicyFilter : PolicyFilter +{ + public static new readonly PolicyFilter Empty = new(); + + protected PolicyFilter() + { + } + + internal PolicyFilter(Func, IQueryable> filter) : base(filter) + { + } + + public PolicyFilter(string policyType, int fieldIndex, IPolicyValues values) : base(policyType, fieldIndex, values) + { + } +} + +public class PolicyFilter : IPolicyFilter where T : IPersistantPolicy +{ + public static readonly PolicyFilter Empty = new(); + + private readonly Func, IQueryable> _filter; + + protected PolicyFilter() + { + } + + internal PolicyFilter(Func, IQueryable> filter) => _filter = filter; + + public PolicyFilter(string policyType, int fieldIndex, IPolicyValues values) + : this(p => FilterValues(p, policyType, fieldIndex, values)) + { + } + + public IQueryable ApplyFilter(IQueryable policies) => _filter is null ? policies : _filter(policies); + + private static IQueryable FilterValues(IQueryable query, + string policyType, int fieldIndex, IPolicyValues values) + { + if (fieldIndex > 12) + { + throw new ArgumentOutOfRangeException(nameof(fieldIndex)); + } + + int fieldValueCount = values.Count; + if (fieldValueCount is 0) + { + return query; + } + + int lastIndex = fieldIndex + fieldValueCount - 1; + + if (lastIndex > 12) + { + throw new ArgumentOutOfRangeException(nameof(lastIndex)); + } + + query = query.Where(p => string.Equals(p.Type, policyType)); + + if (fieldIndex is 0 && lastIndex >= 0) + { + string field = values[fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value1 == field); + } + } + + if (fieldIndex <= 1 && lastIndex >= 1) + { + string field = values[1 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value2 == field); + } + } + + if (fieldIndex <= 2 && lastIndex >= 2) + { + string field = values[2 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value3 == field); + } + } + + if (fieldIndex <= 3 && lastIndex >= 3) + { + string field = values[3 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value4 == field); + } + } + + if (fieldIndex <= 4 && lastIndex >= 4) + { + string field = values[4 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value5 == field); + } + } + + if (fieldIndex <= 5 && lastIndex >= 5) + { + string field = values[5 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value6 == field); + } + } + + if (fieldIndex <= 6 && lastIndex >= 6) + { + string field = values[6 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value7 == field); + } + } + + if (fieldIndex <= 7 && lastIndex >= 7) + { + string field = values[7 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value8 == field); + } + } + + if (fieldIndex <= 8 && lastIndex >= 8) + { + string field = values[8 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value9 == field); + } + } + + if (fieldIndex <= 9 && lastIndex >= 9) + { + string field = values[9 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value10 == field); + } + } + + if (fieldIndex <= 10 && lastIndex >= 10) + { + string field = values[5 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value11 == field); + } + } + + if (lastIndex is 11) // and fieldIndex <= 11 + { + string field = values[11 - fieldIndex]; + if (string.IsNullOrWhiteSpace(field) is false) + { + query = query.Where(p => p.Value12 == field); + } + } + + return query; + } +} +