diff --git a/Casbin.UnitTest/ModelTests/ManagementApiTest.cs b/Casbin.UnitTest/ModelTests/ManagementApiTest.cs index 173e5bc3..e4ce8bcf 100644 --- a/Casbin.UnitTest/ModelTests/ManagementApiTest.cs +++ b/Casbin.UnitTest/ModelTests/ManagementApiTest.cs @@ -122,6 +122,68 @@ public void TestModifyPolicy() e.RemoveFilteredPolicy(1, ""); TestGetPolicy(e, AsList(AsList("eve", "data3", "read"))); + + bool res = e.UpdatePolicy(AsList("eve", "data3", "read"), AsList("eve", "data3", "write")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy will not be updated. + res = e.UpdatePolicy(AsList("non_exist", "data3", "write"), AsList("non_exist", "data3", "read")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.False(res); + + e.AddPolicies(rules); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "write"), + AsList("jack", "data4", "read"), + AsList("katy", "data4", "write"), + AsList("leyo", "data4", "read"), + AsList("ham", "data4", "write"))); + + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "write"), + AsList("leyo", "data4", "read"), + AsList("katy", "data4", "write")), + AsList( + AsList("eve", "data3", "read"), + AsList("leyo", "data4", "write"), + AsList("katy", "data1", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy in oldParameters will not be updated, so other existent ones + // will be ignored and the return value will be False. + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "read"), AsList("non_exist", "data4", "read")), + AsList( + AsList("eve", "data3", "write"), AsList("non_exist", "data4", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "read"), AsList("leyo", "data4", "write")), + AsList(AsList("eve", "data3", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); } [Fact] @@ -141,7 +203,7 @@ public async Task TestModifyPolicyAsync() await e.RemovePolicyAsync("alice", "data1", "read"); await e.AddPolicyAsync("eve", "data3", "read"); await e.AddPolicyAsync("eve", "data3", "read"); - + var rules = AsList( AsList("jack", "data4", "read"), AsList("jack", "data4", "read"), @@ -182,6 +244,68 @@ public async Task TestModifyPolicyAsync() await e.RemoveFilteredPolicyAsync(1, "data2"); TestGetPolicy(e, AsList(AsList("eve", "data3", "read"))); + + bool res = await e.UpdatePolicyAsync(AsList("eve", "data3", "read"), AsList("eve", "data3", "write")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy will not be updated. + res = await e.UpdatePolicyAsync(AsList("non_exist", "data3", "write"), AsList("non_exist", "data3", "read")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.False(res); + + await e.AddPoliciesAsync(rules); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "write"), + AsList("jack", "data4", "read"), + AsList("katy", "data4", "write"), + AsList("leyo", "data4", "read"), + AsList("ham", "data4", "write"))); + + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "write"), + AsList("leyo", "data4", "read"), + AsList("katy", "data4", "write")), + AsList( + AsList("eve", "data3", "read"), + AsList("leyo", "data4", "write"), + AsList("katy", "data1", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy in oldParameters will not be updated, so other existent ones + // will be ignored and the return value will be False. + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "read"), AsList("non_exist", "data4", "read")), + AsList( + AsList("eve", "data3", "write"), AsList("non_exist", "data4", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "read"), AsList("leyo", "data4", "write")), + AsList(AsList("eve", "data3", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); } [Fact] @@ -236,6 +360,58 @@ public void TestModifyGroupingPolicy() TestGetUsers(e, "data1_admin", AsList()); TestGetUsers(e, "data2_admin", AsList()); TestGetUsers(e, "data3_admin", AsList("eve")); + + e.AddGroupingPolicy("data3_admin", "data4_admin"); + bool res = e.UpdateGroupingPolicy(AsList("eve", "data3_admin"), AsList("eve", "admin")); + Assert.True(res); + res = e.UpdateGroupingPolicy(AsList("data3_admin", "data4_admin"), AsList("admin", "data4_admin")); + Assert.True(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + TestGetUsers(e, "admin", AsList("eve")); + TestGetRoles(e, "eve", AsList("admin")); + TestGetRoles(e, "admin", AsList("data4_admin")); + + res = e.UpdateGroupingPolicy(AsList("non_exist", "data4_admin"), AsList("non_exist2", "data4_admin")); + Assert.False(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + + res = e.UpdateGroupingPolicies( + AsList( + AsList("eve", "admin"), + AsList("admin", "data4_admin")), + AsList( + AsList("eve", "admin_groups"), + AsList("admin", "data5_admin"))); + + Assert.True(res); + TestGetUsers(e, "data5_admin", AsList("admin")); + TestGetUsers(e, "admin_groups", AsList("eve")); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + res = e.UpdateGroupingPolicies( + AsList( + AsList("admin", "data5_admin"), + AsList("non_exist", "admin_groups") + ), + AsList( + AsList("admin", "data6_admin"), + AsList("non_exist2", "admin_groups") + )); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = e.UpdateGroupingPolicies( + AsList( + AsList("admin", "data5_admin"), + AsList("eve", "admin2_groups")), + AsList( + AsList("admin", "data6_admin"))); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); } [Fact] @@ -289,6 +465,58 @@ public async Task TestModifyGroupingPolicyAsync() TestGetUsers(e, "data1_admin", AsList()); TestGetUsers(e, "data2_admin", AsList()); TestGetUsers(e, "data3_admin", AsList("eve")); + + await e.AddGroupingPolicyAsync("data3_admin", "data4_admin"); + bool res = await e.UpdateGroupingPolicyAsync(AsList("eve", "data3_admin"), AsList("eve", "admin")); + Assert.True(res); + res = await e.UpdateGroupingPolicyAsync(AsList("data3_admin", "data4_admin"), AsList("admin", "data4_admin")); + Assert.True(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + TestGetUsers(e, "admin", AsList("eve")); + TestGetRoles(e, "eve", AsList("admin")); + TestGetRoles(e, "admin", AsList("data4_admin")); + + res = await e.UpdateGroupingPolicyAsync(AsList("non_exist", "data4_admin"), AsList("non_exist2", "data4_admin")); + Assert.False(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("eve", "admin"), + AsList("admin", "data4_admin")), + AsList( + AsList("eve", "admin_groups"), + AsList("admin", "data5_admin"))); + + Assert.True(res); + TestGetUsers(e, "data5_admin", AsList("admin")); + TestGetUsers(e, "admin_groups", AsList("eve")); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("admin", "data5_admin"), + AsList("non_exist", "admin_groups") + ), + AsList( + AsList("admin", "data6_admin"), + AsList("non_exist2", "admin_groups") + )); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("admin", "data5_admin"), + AsList("eve", "admin2_groups")), + AsList( + AsList("admin", "data6_admin"))); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); } } } diff --git a/Casbin.UnitTest/SyncedModelTests/SyncedManagementApiTest.cs b/Casbin.UnitTest/SyncedModelTests/SyncedManagementApiTest.cs index 5fdb741e..8b258805 100644 --- a/Casbin.UnitTest/SyncedModelTests/SyncedManagementApiTest.cs +++ b/Casbin.UnitTest/SyncedModelTests/SyncedManagementApiTest.cs @@ -122,6 +122,68 @@ public void TestModifyPolicy() e.RemoveFilteredPolicy(1, ""); TestGetPolicy(e, AsList(AsList("eve", "data3", "read"))); + + bool res = e.UpdatePolicy(AsList("eve", "data3", "read"), AsList("eve", "data3", "write")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy will not be updated. + res = e.UpdatePolicy(AsList("non_exist", "data3", "write"), AsList("non_exist", "data3", "read")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.False(res); + + e.AddPolicies(rules); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "write"), + AsList("jack", "data4", "read"), + AsList("katy", "data4", "write"), + AsList("leyo", "data4", "read"), + AsList("ham", "data4", "write"))); + + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "write"), + AsList("leyo", "data4", "read"), + AsList("katy", "data4", "write")), + AsList( + AsList("eve", "data3", "read"), + AsList("leyo", "data4", "write"), + AsList("katy", "data1", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy in oldParameters will not be updated, so other existent ones + // will be ignored and the return value will be False. + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "read"), AsList("non_exist", "data4", "read")), + AsList( + AsList("eve", "data3", "write"), AsList("non_exist", "data4", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = e.UpdatePolicies( + AsList( + AsList("eve", "data3", "read"), AsList("leyo", "data4", "write")), + AsList(AsList("eve", "data3", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); } [Fact] @@ -141,7 +203,7 @@ public async Task TestModifyPolicyAsync() await e.RemovePolicyAsync("alice", "data1", "read"); await e.AddPolicyAsync("eve", "data3", "read"); await e.AddPolicyAsync("eve", "data3", "read"); - + var rules = AsList( AsList("jack", "data4", "read"), AsList("jack", "data4", "read"), @@ -182,6 +244,68 @@ public async Task TestModifyPolicyAsync() await e.RemoveFilteredPolicyAsync(1, "data2"); TestGetPolicy(e, AsList(AsList("eve", "data3", "read"))); + + bool res = await e.UpdatePolicyAsync(AsList("eve", "data3", "read"), AsList("eve", "data3", "write")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy will not be updated. + res = await e.UpdatePolicyAsync(AsList("non_exist", "data3", "write"), AsList("non_exist", "data3", "read")); + TestGetPolicy(e, AsList(AsList("eve", "data3", "write"))); + Assert.False(res); + + await e.AddPoliciesAsync(rules); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "write"), + AsList("jack", "data4", "read"), + AsList("katy", "data4", "write"), + AsList("leyo", "data4", "read"), + AsList("ham", "data4", "write"))); + + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "write"), + AsList("leyo", "data4", "read"), + AsList("katy", "data4", "write")), + AsList( + AsList("eve", "data3", "read"), + AsList("leyo", "data4", "write"), + AsList("katy", "data1", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.True(res); + + // This test shows that a non-existent policy in oldParameters will not be updated, so other existent ones + // will be ignored and the return value will be False. + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "read"), AsList("non_exist", "data4", "read")), + AsList( + AsList("eve", "data3", "write"), AsList("non_exist", "data4", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = await e.UpdatePoliciesAsync( + AsList( + AsList("eve", "data3", "read"), AsList("leyo", "data4", "write")), + AsList(AsList("eve", "data3", "write"))); + TestGetPolicy(e, AsList( + AsList("eve", "data3", "read"), + AsList("jack", "data4", "read"), + AsList("katy", "data1", "write"), + AsList("leyo", "data4", "write"), + AsList("ham", "data4", "write"))); + Assert.False(res); } [Fact] @@ -236,6 +360,58 @@ public void TestModifyGroupingPolicy() TestGetUsers(e, "data1_admin", AsList()); TestGetUsers(e, "data2_admin", AsList()); TestGetUsers(e, "data3_admin", AsList("eve")); + + e.AddGroupingPolicy("data3_admin", "data4_admin"); + bool res = e.UpdateGroupingPolicy(AsList("eve", "data3_admin"), AsList("eve", "admin")); + Assert.True(res); + res = e.UpdateGroupingPolicy(AsList("data3_admin", "data4_admin"), AsList("admin", "data4_admin")); + Assert.True(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + TestGetUsers(e, "admin", AsList("eve")); + TestGetRoles(e, "eve", AsList("admin")); + TestGetRoles(e, "admin", AsList("data4_admin")); + + res = e.UpdateGroupingPolicy(AsList("non_exist", "data4_admin"), AsList("non_exist2", "data4_admin")); + Assert.False(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + + res = e.UpdateGroupingPolicies( + AsList( + AsList("eve", "admin"), + AsList("admin", "data4_admin")), + AsList( + AsList("eve", "admin_groups"), + AsList("admin", "data5_admin"))); + + Assert.True(res); + TestGetUsers(e, "data5_admin", AsList("admin")); + TestGetUsers(e, "admin_groups", AsList("eve")); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + res = e.UpdateGroupingPolicies( + AsList( + AsList("admin", "data5_admin"), + AsList("non_exist", "admin_groups") + ), + AsList( + AsList("admin", "data6_admin"), + AsList("non_exist2", "admin_groups") + )); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = e.UpdateGroupingPolicies( + AsList( + AsList("admin", "data5_admin"), + AsList("eve", "admin2_groups")), + AsList( + AsList("admin", "data6_admin"))); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); } [Fact] @@ -289,6 +465,58 @@ public async Task TestModifyGroupingPolicyAsync() TestGetUsers(e, "data1_admin", AsList()); TestGetUsers(e, "data2_admin", AsList()); TestGetUsers(e, "data3_admin", AsList("eve")); + + await e.AddGroupingPolicyAsync("data3_admin", "data4_admin"); + bool res = await e.UpdateGroupingPolicyAsync(AsList("eve", "data3_admin"), AsList("eve", "admin")); + Assert.True(res); + res = await e.UpdateGroupingPolicyAsync(AsList("data3_admin", "data4_admin"), AsList("admin", "data4_admin")); + Assert.True(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + TestGetUsers(e, "admin", AsList("eve")); + TestGetRoles(e, "eve", AsList("admin")); + TestGetRoles(e, "admin", AsList("data4_admin")); + + res = await e.UpdateGroupingPolicyAsync(AsList("non_exist", "data4_admin"), AsList("non_exist2", "data4_admin")); + Assert.False(res); + TestGetUsers(e, "data4_admin", AsList("admin")); + + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("eve", "admin"), + AsList("admin", "data4_admin")), + AsList( + AsList("eve", "admin_groups"), + AsList("admin", "data5_admin"))); + + Assert.True(res); + TestGetUsers(e, "data5_admin", AsList("admin")); + TestGetUsers(e, "admin_groups", AsList("eve")); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("admin", "data5_admin"), + AsList("non_exist", "admin_groups") + ), + AsList( + AsList("admin", "data6_admin"), + AsList("non_exist2", "admin_groups") + )); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); + + // If oldRules' length is not the same as newRules', no rules will be updated. + res = await e.UpdateGroupingPoliciesAsync( + AsList( + AsList("admin", "data5_admin"), + AsList("eve", "admin2_groups")), + AsList( + AsList("admin", "data6_admin"))); + Assert.False(res); + TestGetRoles(e, "admin", AsList("data5_admin")); + TestGetRoles(e, "eve", AsList("admin_groups")); } } } diff --git a/Casbin/Abstractions/Model/IModel.cs b/Casbin/Abstractions/Model/IModel.cs index ef34e9fe..468ea100 100644 --- a/Casbin/Abstractions/Model/IModel.cs +++ b/Casbin/Abstractions/Model/IModel.cs @@ -35,6 +35,17 @@ public interface IModel : IPolicyStore public void BuildIncrementalRoleLink(PolicyOperation policyOperation, string section, string roleType, IEnumerable rule); + /// + /// Provides incremental build the role inheritance relation. + /// + /// + /// + /// + /// + /// + public void BuildIncrementalRoleLink(PolicyOperation policyOperation, + string section, string roleType, IEnumerable oldRule, IEnumerable newRule); + /// /// Provides incremental build the role inheritance relations. /// @@ -45,6 +56,17 @@ public void BuildIncrementalRoleLink(PolicyOperation policyOperation, public void BuildIncrementalRoleLinks(PolicyOperation policyOperation, string section, string roleType, IEnumerable> rules); + /// + /// Provides incremental build the role inheritance relations. + /// + /// + /// + /// + /// + /// + public void BuildIncrementalRoleLinks(PolicyOperation policyOperation, + string section, string roleType, IEnumerable> oldRules, IEnumerable> newRules); + /// /// Initializes the roles in RBAC. /// diff --git a/Casbin/Abstractions/Model/IPolicyManager.cs b/Casbin/Abstractions/Model/IPolicyManager.cs index 3c9fdf4a..5147ac79 100644 --- a/Casbin/Abstractions/Model/IPolicyManager.cs +++ b/Casbin/Abstractions/Model/IPolicyManager.cs @@ -46,6 +46,10 @@ public interface IPolicyManager : IPolicyStore public Task AddPoliciesAsync(string section, string policyType, IEnumerable> rules); + public Task UpdatePolicyAsync(string section, string policyType, IEnumerable oldRule, IEnumerable newRule); + + public Task UpdatePoliciesAsync(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules); + public Task RemovePolicyAsync(string section, string policyType, IEnumerable rule); public Task RemovePoliciesAsync(string section, string policyType, diff --git a/Casbin/Abstractions/Model/IPolicyStore.cs b/Casbin/Abstractions/Model/IPolicyStore.cs index 9d7abf84..c4539df4 100644 --- a/Casbin/Abstractions/Model/IPolicyStore.cs +++ b/Casbin/Abstractions/Model/IPolicyStore.cs @@ -23,10 +23,16 @@ public IEnumerable> GetFilteredPolicy(string section, string public bool HasPolicies(string section, string policyType, IEnumerable> rules); + public bool HasAllPolicies(string section, string policyType, IEnumerable> rules); + public bool AddPolicy(string section, string policyType, IEnumerable rule); public bool AddPolicies(string section, string policyType, IEnumerable> rules); + public bool UpdatePolicy(string section, string policyType, IEnumerable oldRule, IEnumerable newRule); + + public bool UpdatePolicies(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules); + public bool RemovePolicy(string section, string policyType, IEnumerable rule); public bool RemovePolicies(string section, string policyType, IEnumerable> rules); diff --git a/Casbin/Abstractions/Persist/IBatchAdapter.cs b/Casbin/Abstractions/Persist/IBatchAdapter.cs index 8e5786c1..a8242524 100644 --- a/Casbin/Abstractions/Persist/IBatchAdapter.cs +++ b/Casbin/Abstractions/Persist/IBatchAdapter.cs @@ -9,6 +9,10 @@ public interface IBatchAdapter Task AddPoliciesAsync(string section, string policyType, IEnumerable> rules); + void UpdatePolicies(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules); + + Task UpdatePoliciesAsync(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules); + void RemovePolicies(string section, string policyType, IEnumerable> rules); Task RemovePoliciesAsync(string section, string policyType, IEnumerable> rules); diff --git a/Casbin/Abstractions/Persist/ISingleAdapter.cs b/Casbin/Abstractions/Persist/ISingleAdapter.cs index e7f0cf90..aa2df77c 100644 --- a/Casbin/Abstractions/Persist/ISingleAdapter.cs +++ b/Casbin/Abstractions/Persist/ISingleAdapter.cs @@ -9,6 +9,10 @@ public interface ISingleAdapter Task AddPolicyAsync(string section, string policyType, IEnumerable rule); + void UpdatePolicy(string section, string policyType, IEnumerable oldRule, IEnumerable newRule); + + Task UpdatePolicyAsync(string section, string policyType, IEnumerable oldRules, IEnumerable newRules); + void RemovePolicy(string section, string policyType, IEnumerable rule); Task RemovePolicyAsync(string section, string policyType, IEnumerable rule); diff --git a/Casbin/Extensions/Enforcer/InternalEnforcerExtension.cs b/Casbin/Extensions/Enforcer/InternalEnforcerExtension.cs index 1be960a1..df16f765 100644 --- a/Casbin/Extensions/Enforcer/InternalEnforcerExtension.cs +++ b/Casbin/Extensions/Enforcer/InternalEnforcerExtension.cs @@ -150,6 +150,126 @@ internal static async Task InternalAddPoliciesAsync(this IEnforcer enforce return true; } + /// + /// Updates a rule from the current policy. + /// + /// + /// + /// + /// + /// + /// + internal static bool InternalUpdatePolicy(this IEnforcer enforcer, string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + IEnumerable oldRuleArray = oldRule as string[] ?? oldRule.ToArray(); + if (enforcer.PolicyManager.HasPolicy(section, policyType, oldRuleArray) is false) + { + return false; + } + + IEnumerable newRuleArray = newRule as string[] ?? newRule.ToArray(); + bool ruleUpdated = enforcer.PolicyManager.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + + if (ruleUpdated is false) + { + return false; + } + + OnPolicyChanged(enforcer, PolicyOperation.PolicyUpdate, section, policyType, oldRuleArray, newRuleArray); + return true; + } + + /// + /// Updates a rule from the current policy. + /// + /// + /// + /// + /// + /// + /// + internal static async Task InternalUpdatePolicyAsync(this IEnforcer enforcer, string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + IEnumerable oldRuleArray = oldRule as string[] ?? oldRule.ToArray(); + if (enforcer.PolicyManager.HasPolicy(section, policyType, oldRuleArray) is false) + { + return false; + } + + IEnumerable newRuleArray = newRule as string[] ?? newRule.ToArray(); + bool ruleUpdated = await enforcer.PolicyManager.UpdatePolicyAsync(section, policyType, oldRuleArray, newRuleArray); + + if (ruleUpdated is false) + { + return false; + } + + await OnPolicyAsyncChanged(enforcer, PolicyOperation.PolicyUpdate, section, policyType, oldRuleArray, newRuleArray); + return true; + } + + /// + /// Updates rules from the current policy. + /// + /// + /// + /// + /// + /// + /// + internal static bool InternalUpdatePolicies(this IEnforcer enforcer, string section, string policyType, + IEnumerable> oldRules, IEnumerable> newRules) + { + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + + if (enforcer.PolicyManager.HasAllPolicies(section, policyType, oldRulesArray) is false) + { + return false; + } + + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + bool ruleUpdated = enforcer.PolicyManager.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + + if (ruleUpdated is false) + { + return false; + } + + OnPoliciesChanged(enforcer, PolicyOperation.PolicyUpdate, section, policyType, oldRulesArray, newRulesArray); + return true; + } + + /// + /// Updates rules from the current policy. + /// + /// + /// + /// + /// + /// + /// + internal static async Task InternalUpdatePoliciesAsync(this IEnforcer enforcer, string section, + string policyType, IEnumerable> oldRules, IEnumerable> newRules) + { + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + + if (enforcer.PolicyManager.HasAllPolicies(section, policyType, oldRulesArray) is false) + { + return false; + } + + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + bool ruleUpdated = await enforcer.PolicyManager.UpdatePoliciesAsync(section, policyType, oldRulesArray, newRulesArray); + + if (ruleUpdated is false) + { + return false; + } + + await OnPoliciesAsyncChanged(enforcer, PolicyOperation.PolicyUpdate, section, policyType, oldRulesArray, newRulesArray); + return true; + } + /// /// Removes a rule from the current policy. /// @@ -309,8 +429,18 @@ private static void OnPolicyChanged(IEnforcer enforcer, PolicyOperation policyOp { if (section.Equals(PermConstants.Section.RoleSection)) { - enforcer.Model.BuildIncrementalRoleLink(policyOperation, - section, policyType, rule); + enforcer.Model.BuildIncrementalRoleLink(policyOperation, section, policyType, rule); + } + + NotifyPolicyChanged(enforcer); + } + + private static void OnPolicyChanged(IEnforcer enforcer, PolicyOperation policyOperation, + string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + if (section.Equals(PermConstants.Section.RoleSection)) + { + enforcer.Model.BuildIncrementalRoleLink(policyOperation, section, policyType, oldRule, newRule); } NotifyPolicyChanged(enforcer); @@ -327,6 +457,17 @@ private static async Task OnPolicyAsyncChanged(IEnforcer enforcer, PolicyOperati await NotifyPolicyChangedAsync(enforcer); } + private static async Task OnPolicyAsyncChanged(IEnforcer enforcer, PolicyOperation policyOperation, + string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + if (section.Equals(PermConstants.Section.RoleSection)) + { + enforcer.Model.BuildIncrementalRoleLink(policyOperation, + section, policyType, oldRule, newRule); + } + await NotifyPolicyChangedAsync(enforcer); + } + private static void OnPoliciesChanged(IEnforcer enforcer, PolicyOperation policyOperation, string section, string policyType, IEnumerable> rules) { @@ -338,6 +479,17 @@ private static void OnPoliciesChanged(IEnforcer enforcer, PolicyOperation policy NotifyPolicyChanged(enforcer); } + private static void OnPoliciesChanged(IEnforcer enforcer, PolicyOperation policyOperation, + string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules) + { + if (section.Equals(PermConstants.Section.RoleSection)) + { + enforcer.Model.BuildIncrementalRoleLinks(policyOperation, + section, policyType, oldRules, newRules); + } + NotifyPolicyChanged(enforcer); + } + private static async Task OnPoliciesAsyncChanged(IEnforcer enforcer, PolicyOperation policyOperation, string section, string policyType, IEnumerable> rules) { @@ -349,6 +501,17 @@ private static async Task OnPoliciesAsyncChanged(IEnforcer enforcer, PolicyOpera await NotifyPolicyChangedAsync(enforcer); } + private static async Task OnPoliciesAsyncChanged(IEnforcer enforcer, PolicyOperation policyOperation, + string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules) + { + if (section.Equals(PermConstants.Section.RoleSection)) + { + enforcer.Model.BuildIncrementalRoleLinks(policyOperation, + section, policyType, oldRules, newRules); + } + await NotifyPolicyChangedAsync(enforcer); + } + private static void NotifyPolicyChanged(IEnforcer enforcer) { if (enforcer.AutoCleanEnforceCache) diff --git a/Casbin/Extensions/Enforcer/ManagementEnforcerExtension.cs b/Casbin/Extensions/Enforcer/ManagementEnforcerExtension.cs index fae69208..026e9b20 100644 --- a/Casbin/Extensions/Enforcer/ManagementEnforcerExtension.cs +++ b/Casbin/Extensions/Enforcer/ManagementEnforcerExtension.cs @@ -363,6 +363,160 @@ public static Task AddNamedPoliciesAsync(this IEnforcer enforcer, string p #endregion + #region Update Store + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The "p" policy rule to be replaced, ptype "p" is implicitly used. + /// The "p" policy rule to replace the old one, ptype "p" is implicitly used. + /// Succeeds or not. + public static bool UpdatePolicy(this IEnforcer enforcer, IEnumerable oldParameters, params string[] newParameters) + { + return UpdatePolicy(enforcer, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The "p" policy rule to be replaced, ptype "p" is implicitly used. + /// The "p" policy rule to replace the old one, ptype "p" is implicitly used. + /// Succeeds or not. + public static Task UpdatePolicyAsync(this IEnforcer enforcer, IEnumerable oldParameters, params string[] newParameters) + { + return UpdatePolicyAsync(enforcer, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The "p" policy rule to be replaced, ptype "p" is implicitly used. + /// The "p" policy rule to replace the old one, ptype "p" is implicitly used. + /// Succeeds or not. + public static bool UpdatePolicy(this IEnforcer enforcer, IEnumerable oldParameters, IEnumerable newParameters) + { + return UpdateNamedPolicy(enforcer, PermConstants.DefaultPolicyType, oldParameters, newParameters); + } + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The "p" policy rule to be replaced, ptype "p" is implicitly used. + /// The "p" policy rule to replace the old one, ptype "p" is implicitly used. + /// Succeeds or not. + public static Task UpdatePolicyAsync(this IEnforcer enforcer, IEnumerable oldParameters, IEnumerable newParameters) + { + return UpdateNamedPolicyAsync(enforcer, PermConstants.DefaultPolicyType, oldParameters, newParameters); + } + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old one. + /// Succeeds or not. + public static bool UpdateNamedPolicy(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateNamedPolicy(enforcer, ptype, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates an authorization rule to the current policy. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old one. + /// Succeeds or not. + public static Task UpdateNamedPolicyAsync(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateNamedPolicyAsync(enforcer, ptype, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates an authorization rule to the current named policy. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old one. + /// Succeeds or not. + public static bool UpdateNamedPolicy(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, IEnumerable newParameters) + { + return enforcer.InternalUpdatePolicy(PermConstants.Section.PolicySection, ptype, oldParameters, newParameters); + } + + /// + /// Updates an authorization rule to the current named policy. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old one. + /// Succeeds or not. + public static Task UpdateNamedPolicyAsync(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, IEnumerable newParameters) + { + return enforcer.InternalUpdatePolicyAsync(PermConstants.Section.PolicySection, ptype, oldParameters, newParameters); + } + + /// + /// Updates authorization rules to the current policies. + /// + /// + /// The "p" policy rules to be replaced, ptype "p" is implicitly used. + /// The "p" policy rules to replace the old ones, ptype "p" is implicitly used. + /// Succeeds or not. + public static bool UpdatePolicies(this IEnforcer enforcer, IEnumerable> oldRules, IEnumerable> newRules) + { + return UpdateNamedPolicies(enforcer, PermConstants.DefaultPolicyType, oldRules, newRules); + } + + /// + /// Updates authorization rules to the current policies. + /// + /// + /// The "p" policy rules to be replaced, ptype "p" is implicitly used. + /// The "p" policy rules to replace the old ones, ptype "p" is implicitly used. + /// Succeeds or not. + public static Task UpdatePoliciesAsync(this IEnforcer enforcer, IEnumerable> oldRules, IEnumerable> newRules) + { + return UpdateNamedPoliciesAsync(enforcer, PermConstants.DefaultPolicyType, oldRules, newRules); + } + + /// + /// Updates authorization rules to the current named policies. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old ones. + /// Succeeds or not. + public static bool UpdateNamedPolicies(this IEnforcer enforcer, string ptype, IEnumerable> oldRules, IEnumerable> newRules) + { + return enforcer.InternalUpdatePolicies(PermConstants.Section.PolicySection, ptype, oldRules, newRules); + } + + /// + /// Updates authorization rules to the current named policies. + /// + /// + /// The policy type, can be "p", "p2", "p3", .. + /// The "p" policy rule to be replaced. + /// The "p" policy rule to replace the old ones. + /// Succeeds or not. + public static Task UpdateNamedPoliciesAsync(this IEnforcer enforcer, string ptype, IEnumerable> oldRules, IEnumerable> newRules) + { + return enforcer.InternalUpdatePoliciesAsync(PermConstants.Section.PolicySection, ptype, oldRules, newRules); + } + + #endregion + #region Remove Store /// @@ -748,7 +902,7 @@ public static Task AddGroupingPolicyAsync(this IEnforcer enforcer, params } /// - /// Adds a named role inheritance rule to the current + /// Adds a named role inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -762,7 +916,7 @@ public static bool AddNamedGroupingPolicy(this IEnforcer enforcer, string ptype, } /// - /// Adds a named role inheritance rule to the current + /// Adds a named role inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -776,7 +930,7 @@ public static async Task AddNamedGroupingPolicyAsync(this IEnforcer enforc } /// - /// Adds a named role inheritance rule to the current + /// Adds a named role inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -790,7 +944,7 @@ public static bool AddNamedGroupingPolicy(this IEnforcer enforcer, string ptype, } /// - /// Adds a named role inheritance rule to the current + /// Adds a named role inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -830,7 +984,7 @@ public static Task AddGroupingPoliciesAsync(this IEnforcer enforcer, IEnum } /// - /// Adds named roles inheritance rule to the current + /// Adds named roles inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -844,7 +998,7 @@ public static bool AddNamedGroupingPolicies(this IEnforcer enforcer, string ptyp } /// - /// Adds named roles inheritance rule to the current + /// Adds named roles inheritance rule to the current /// policy. If the rule already exists, the function returns false and the rule /// will not be added. Otherwise the function returns true by adding the new rule. /// @@ -859,6 +1013,162 @@ public static async Task AddNamedGroupingPoliciesAsync(this IEnforcer enfo #endregion + #region Update Grouping/Role Store + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateGroupingPolicy(this IEnforcer enforcer, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateGroupingPolicy(enforcer, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static Task UpdateGroupingPolicyAsync(this IEnforcer enforcer, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateGroupingPolicyAsync(enforcer, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateGroupingPolicy(this IEnforcer enforcer, IEnumerable oldParameters, IEnumerable newParameters) + { + return UpdateNamedGroupingPolicy(enforcer, PermConstants.DefaultGroupingPolicyType, oldParameters, newParameters); + } + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static Task UpdateGroupingPolicyAsync(this IEnforcer enforcer, IEnumerable oldParameters, IEnumerable newParameters) + { + return UpdateNamedGroupingPolicyAsync(enforcer, PermConstants.DefaultGroupingPolicyType, oldParameters, newParameters); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateNamedGroupingPolicy(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateNamedGroupingPolicy(enforcer, ptype, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static Task UpdateNamedGroupingPolicyAsync(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, params string[] newParameters) + { + return UpdateNamedGroupingPolicyAsync(enforcer, ptype, oldParameters, newParameters as IEnumerable); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateNamedGroupingPolicy(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, IEnumerable newParameters) + { + return enforcer.InternalUpdatePolicy(PermConstants.Section.RoleSection, ptype, oldParameters, newParameters); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static async Task UpdateNamedGroupingPolicyAsync(this IEnforcer enforcer, string ptype, IEnumerable oldParameters, IEnumerable newParameters) + { + return await enforcer.InternalUpdatePolicyAsync(PermConstants.Section.RoleSection, ptype, oldParameters, newParameters); + } + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old ones, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateGroupingPolicies(this IEnforcer enforcer, IEnumerable> oldRules, IEnumerable> newRules) + { + return UpdateNamedGroupingPolicies(enforcer, PermConstants.DefaultGroupingPolicyType, oldRules, newRules); + + } + + /// + /// Updates a role inheritance rule from the current policy. + /// + /// + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old one, ptype "g" is implicitly used. + /// Succeeds or not. + public static Task UpdateGroupingPoliciesAsync(this IEnforcer enforcer, IEnumerable> oldRules, IEnumerable> newRules) + { + return UpdateNamedGroupingPoliciesAsync(enforcer, PermConstants.DefaultGroupingPolicyType, oldRules, newRules); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old ones, ptype "g" is implicitly used. + /// Succeeds or not. + public static bool UpdateNamedGroupingPolicies(this IEnforcer enforcer, string ptype, IEnumerable> oldRules, IEnumerable> newRules) + { + return enforcer.InternalUpdatePolicies(PermConstants.Section.RoleSection, ptype, oldRules, newRules); + } + + /// + /// Updates a role inheritance rule from the current policy, field filters can be specified. + /// + /// + /// The policy type, can be "g", "g2", "g3", .. + /// The "g" policy rule to be replaced, ptype "g" is implicitly used. + /// The "g" policy rule to replace the old ones, ptype "g" is implicitly used. + /// Succeeds or not. + public static async Task UpdateNamedGroupingPoliciesAsync(this IEnforcer enforcer, string ptype, + IEnumerable> oldRules, IEnumerable> newRules) + { + return await enforcer.InternalUpdatePoliciesAsync(PermConstants.Section.RoleSection, ptype, oldRules, newRules); + } + + #endregion + #region Remove Grouping/Role Store /// @@ -906,7 +1216,7 @@ public static Task RemoveGroupingPolicyAsync(this IEnforcer enforcer, IEnu } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -919,7 +1229,7 @@ public static bool RemoveNamedGroupingPolicy(this IEnforcer enforcer, string pty } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -932,7 +1242,7 @@ public static Task RemoveNamedGroupingPolicyAsync(this IEnforcer enforcer, } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -941,11 +1251,11 @@ public static Task RemoveNamedGroupingPolicyAsync(this IEnforcer enforcer, /// Succeeds or not. public static bool RemoveNamedGroupingPolicy(this IEnforcer enforcer, string ptype, IEnumerable parameters) { - return enforcer.InternalRemovePolicy(PermConstants.Section.RoleSection, ptype, parameters); ; + return enforcer.InternalRemovePolicy(PermConstants.Section.RoleSection, ptype, parameters); } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -954,7 +1264,7 @@ public static bool RemoveNamedGroupingPolicy(this IEnforcer enforcer, string pty /// Succeeds or not. public static async Task RemoveNamedGroupingPolicyAsync(this IEnforcer enforcer, string ptype, IEnumerable parameters) { - return await enforcer.InternalRemovePolicyAsync(PermConstants.Section.RoleSection, ptype, parameters); ; + return await enforcer.InternalRemovePolicyAsync(PermConstants.Section.RoleSection, ptype, parameters); } /// @@ -981,7 +1291,7 @@ public static Task RemoveGroupingPoliciesAsync(this IEnforcer enforcer, IE } /// - /// Removes roles inheritance rule from the current + /// Removes roles inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -994,7 +1304,7 @@ public static bool RemoveNamedGroupingPolicies(this IEnforcer enforcer, string p } /// - /// Removes roles inheritance rule from the current + /// Removes roles inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -1007,7 +1317,7 @@ public static async Task RemoveNamedGroupingPoliciesAsync(this IEnforcer e } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// @@ -1020,7 +1330,7 @@ public static bool RemoveFilteredGroupingPolicy(this IEnforcer enforcer, int fie } /// - /// Removes a role inheritance rule from the current + /// Removes a role inheritance rule from the current /// policy, field filters can be specified. /// /// diff --git a/Casbin/Model/Assertion.cs b/Casbin/Model/Assertion.cs index 97f545d2..0fdbb279 100644 --- a/Casbin/Model/Assertion.cs +++ b/Casbin/Model/Assertion.cs @@ -57,6 +57,17 @@ internal void BuildIncrementalRoleLink(PolicyOperation policyOperation, IEnumera BuildRoleLink(count, policyOperation, rule); } + internal void BuildIncrementalRoleLink(PolicyOperation policyOperation, IEnumerable oldRule, IEnumerable newRule) + { + int count = Value.Count(c => c is '_'); + if (count < 2) + { + throw new InvalidOperationException("the number of \"_\" in role definition should be at least 2."); + } + + BuildRoleLink(count, policyOperation, oldRule, newRule); + } + internal void BuildIncrementalRoleLinks(PolicyOperation policyOperation, IEnumerable> rules) { int count = Value.Count(c => c is '_'); @@ -71,6 +82,26 @@ internal void BuildIncrementalRoleLinks(PolicyOperation policyOperation, IEnumer } } + internal void BuildIncrementalRoleLinks(PolicyOperation policyOperation, IEnumerable> oldRules, IEnumerable> newRules) + { + int count = Value.Count(c => c is '_'); + if (count < 2) + { + throw new InvalidOperationException("the number of \"_\" in role definition should be at least 2."); + } + + var rulesList = oldRules as IReadOnlyList> ?? oldRules.ToList(); + var newRulesList = newRules as IReadOnlyList> ?? newRules.ToList(); + if (rulesList.Count != newRulesList.Count) + { + throw new InvalidOperationException($"the length of oldPolices should be equal to the length of newPolices, but got the length of oldPolices is {rulesList.Count}, the length of newPolices is {newRulesList.Count}."); + } + for (int i = 0; i < rulesList.Count; i++) + { + BuildRoleLink(count, policyOperation, rulesList[i], newRulesList[i]); + } + } + public void BuildRoleLinks() { int count = Value.Count(c => c is '_'); @@ -86,7 +117,7 @@ public void BuildRoleLinks() } private void BuildRoleLink(int groupPolicyCount, - PolicyOperation policyOperation, IEnumerable rule) + PolicyOperation policyOperation, IEnumerable rule, params IEnumerable[] newRule) { var roleManager = RoleManager; List ruleEnum = rule as List ?? rule.ToList(); @@ -117,6 +148,38 @@ private void BuildRoleLink(int groupPolicyCount, throw new ArgumentOutOfRangeException(nameof(groupPolicyCount), groupPolicyCount, null); } break; + case PolicyOperation.PolicyUpdate: + if (newRule.Length == 0) + { + throw new InvalidOperationException("Grouping policy elements do not meet role definition."); + } + + List newRuleEnum = newRule[0] as List ?? newRule[0].ToList(); + int newRuleCount = newRuleEnum.Count; + if (newRuleCount < groupPolicyCount) + { + throw new InvalidOperationException("Grouping policy elements do not meet role definition."); + } + + if (newRuleCount > groupPolicyCount) + { + newRuleEnum = newRuleEnum.GetRange(0, groupPolicyCount); + } + + switch (groupPolicyCount) + { + case 2: + roleManager.DeleteLink(ruleEnum[0], ruleEnum[1]); + roleManager.AddLink(newRuleEnum[0], newRuleEnum[1]); + break; + case 3: + roleManager.DeleteLink(ruleEnum[0], ruleEnum[1], ruleEnum[2]); + roleManager.AddLink(newRuleEnum[0], newRuleEnum[1], newRuleEnum[2]); + break; + default: + throw new ArgumentOutOfRangeException(nameof(groupPolicyCount), groupPolicyCount, null); + } + break; case PolicyOperation.PolicyRemove: switch (groupPolicyCount) { @@ -158,6 +221,31 @@ internal bool TryAddPolicy(IEnumerable rule) return true; } + internal bool TryUpdatePolicy(IEnumerable oldRule, IEnumerable newRule) + { + var oldRuleList = oldRule as IReadOnlyList ?? oldRule.ToArray(); + if (Contains(oldRuleList) is false) + { + return false; + } + + var newRuleList = newRule as IReadOnlyList ?? newRule.ToArray(); + for (int i = 0; i < Policy.Count; i++) + { + var ruleInPolicy = Policy[i]; + if (oldRuleList.DeepEquals(ruleInPolicy) is false) + { + continue; + } + _policy.RemoveAt(i); + PolicyStringSet.Remove(Utility.RuleToString(oldRuleList)); + _policy.Insert(i, Model.Policy.CreateOnlyString(newRuleList)); + PolicyStringSet.Add(Utility.RuleToString(newRuleList)); + break; + } + return true; + } + internal bool TryRemovePolicy(IEnumerable rule) { var ruleList = rule as IReadOnlyList ?? rule.ToArray(); diff --git a/Casbin/Model/DefaultModel.cs b/Casbin/Model/DefaultModel.cs index c59e9289..14a86eea 100644 --- a/Casbin/Model/DefaultModel.cs +++ b/Casbin/Model/DefaultModel.cs @@ -187,6 +187,28 @@ public void BuildIncrementalRoleLink(PolicyOperation policyOperation, GFunctionCachePool.Clear(roleType); } + /// + /// Provides incremental build the role inheritance relation. + /// + /// + /// + /// + /// + /// + public void BuildIncrementalRoleLink(PolicyOperation policyOperation, + string section, string roleType, IEnumerable oldRule, IEnumerable newRule) + { + if (Sections.ContainsKey(PermConstants.Section.RoleSection) is false) + { + return; + } + + Assertion assertion = GetRequiredAssertion(section, roleType); + assertion.BuildIncrementalRoleLink(policyOperation, oldRule, newRule); + + GFunctionCachePool.Clear(roleType); + } + /// /// Provides incremental build the role inheritance relations. /// @@ -208,6 +230,28 @@ public void BuildIncrementalRoleLinks(PolicyOperation policyOperation, GFunctionCachePool.Clear(roleType); } + /// + /// Provides incremental build the role inheritance relations. + /// + /// + /// + /// + /// + /// + public void BuildIncrementalRoleLinks(PolicyOperation policyOperation, + string section, string roleType, IEnumerable> oldRules, IEnumerable> newRules) + { + if (Sections.ContainsKey(PermConstants.Section.RoleSection) is false) + { + return; + } + + Assertion assertion = GetRequiredAssertion(section, roleType); + assertion.BuildIncrementalRoleLinks(policyOperation, oldRules, newRules); + + GFunctionCachePool.Clear(roleType); + } + /// /// Initializes the roles in RBAC. /// @@ -317,12 +361,21 @@ public bool HasPolicy(string section, string policyType, IEnumerable rul public bool HasPolicies(string section, string policyType, IEnumerable> rules) => PolicyManager.HasPolicies(section, policyType, rules); + public bool HasAllPolicies(string section, string policyType, IEnumerable> rules) + => PolicyManager.HasAllPolicies(section, policyType, rules); + public bool AddPolicy(string section, string policyType, IEnumerable rule) => PolicyManager.AddPolicy(section, policyType, rule); public bool AddPolicies(string section, string policyType, IEnumerable> rules) => PolicyManager.AddPolicies(section, policyType, rules); + public bool UpdatePolicy(string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + => PolicyManager.UpdatePolicy(section, policyType, oldRule, newRule); + + public bool UpdatePolicies(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules) + => PolicyManager.UpdatePolicies(section, policyType, oldRules, newRules); + public bool RemovePolicy(string section, string policyType, IEnumerable rule) => PolicyManager.RemovePolicy(section, policyType, rule); diff --git a/Casbin/Model/DefaultPolicyManager.cs b/Casbin/Model/DefaultPolicyManager.cs index ee9e259f..872be252 100644 --- a/Casbin/Model/DefaultPolicyManager.cs +++ b/Casbin/Model/DefaultPolicyManager.cs @@ -360,6 +360,19 @@ public bool HasPolicies(string section, string policyType, IEnumerable> rules) + { + StartRead(); + try + { + return PolicyStore.HasAllPolicies(section, policyType, rules); + } + finally + { + EndRead(); + } + } + public bool AddPolicy(string section, string policyType, IEnumerable rule) { if (TryStartWrite() is false) @@ -408,6 +421,56 @@ public bool AddPolicies(string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + if (TryStartWrite() is false) + { + return false; + } + + try + { + IEnumerable oldRuleArray = oldRule as string[] ?? oldRule.ToArray(); + IEnumerable newRuleArray = newRule as string[] ?? newRule.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + } + + SingleAdapter?.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + return PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + } + finally + { + EndWrite(); + } + } + + public bool UpdatePolicies(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules) + { + if (TryStartWrite() is false) + { + return false; + } + + try + { + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + } + + BatchAdapter?.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + return PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + } + finally + { + EndWrite(); + } + } + public bool RemovePolicy(string section, string policyType, IEnumerable rule) { if (TryStartWrite() is false) @@ -537,6 +600,65 @@ public virtual async Task AddPoliciesAsync(string section, string policyTy } } + public virtual async Task UpdatePolicyAsync(string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + if (TryStartWrite() is false) + { + return false; + } + + try + { + IEnumerable oldRuleArray = oldRule as string[] ?? oldRule.ToArray(); + IEnumerable newRuleArray = newRule as string[] ?? newRule.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + } + + if (SingleAdapter is not null) + { + await SingleAdapter.UpdatePolicyAsync(section, policyType, oldRuleArray, newRuleArray); + } + + return PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray); + } + finally + { + EndWrite(); + } + } + + public virtual async Task UpdatePoliciesAsync(string section, string policyType, + IEnumerable> oldRules, IEnumerable> newRules) + { + if (TryStartWrite() is false) + { + return false; + } + + try + { + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + } + + if (BatchAdapter is not null) + { + await BatchAdapter.UpdatePoliciesAsync(section, policyType, oldRulesArray, newRulesArray); + } + + return PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray); + } + finally + { + EndWrite(); + } + } + public virtual async Task RemovePolicyAsync(string section, string policyType, IEnumerable rule) { if (TryStartWrite() is false) diff --git a/Casbin/Model/DefaultPolicyStore.cs b/Casbin/Model/DefaultPolicyStore.cs index cc812c7e..b09b8f2c 100644 --- a/Casbin/Model/DefaultPolicyStore.cs +++ b/Casbin/Model/DefaultPolicyStore.cs @@ -95,6 +95,13 @@ public bool HasPolicies(string section, string policyType, IEnumerable> rules) + { + var assertion = GetRequiredAssertion(section, policyType); + var ruleArray = rules as IEnumerable[] ?? rules.ToArray(); + return ruleArray.Length == 0 || ruleArray.All(assertion.Contains); + } + public bool AddPolicy(string section, string policyType, IEnumerable rule) { var assertion = GetRequiredAssertion(section, policyType); @@ -123,6 +130,35 @@ public bool AddPolicies(string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + var assertion = GetRequiredAssertion(section, policyType); + return assertion.TryUpdatePolicy(oldRule, newRule); + } + + public bool UpdatePolicies(string section, string policyType, IEnumerable> oldRules, IEnumerable> newRules) + { + if (oldRules is null) + { + throw new ArgumentNullException(nameof(oldRules)); + } + + var assertion = GetRequiredAssertion(section, policyType); + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + + if (oldRulesArray.Length != newRulesArray.Length) + { + return false; + } + + for (int i = 0; i < oldRulesArray.Length; i++) + { + assertion.TryUpdatePolicy(oldRulesArray[i], newRulesArray[i]); + } + return true; + } + public bool RemovePolicy(string section, string policyType, IEnumerable rule) { var assertion = GetRequiredAssertion(section, policyType); diff --git a/Casbin/Model/ReaderWriterPolicyManager.cs b/Casbin/Model/ReaderWriterPolicyManager.cs index 302d4642..02cf9a20 100644 --- a/Casbin/Model/ReaderWriterPolicyManager.cs +++ b/Casbin/Model/ReaderWriterPolicyManager.cs @@ -209,6 +209,72 @@ public override Task AddPoliciesAsync(string section, string policyType, }); } + public override Task UpdatePolicyAsync(string section, string policyType, IEnumerable oldRule, IEnumerable newRule) + { + return Task.Run(() => + { + if (TryStartWrite() is false) + { + return Task.FromResult(false); + } + + try + { + IEnumerable oldRuleArray = oldRule as string[] ?? oldRule.ToArray(); + IEnumerable newRuleArray = newRule as string[] ?? newRule.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return Task.FromResult( + PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray)); + } + + if (SingleAdapter is not null) + { + SingleAdapter.UpdatePolicyAsync(section, policyType, oldRuleArray, newRuleArray).Wait(); + } + + return Task.FromResult(PolicyStore.UpdatePolicy(section, policyType, oldRuleArray, newRuleArray)); + } + finally + { + EndWrite(); + } + }); + } + + public override Task UpdatePoliciesAsync(string section, string policyType, + IEnumerable> oldRules, IEnumerable> newRules) + { + return Task.Run(() => + { + if (TryStartWrite() is false) + { + return Task.FromResult(false); + } + + try + { + var oldRulesArray = oldRules as IEnumerable[] ?? oldRules.ToArray(); + var newRulesArray = newRules as IEnumerable[] ?? newRules.ToArray(); + if (HasAdapter is false || AutoSave is false) + { + return Task.FromResult(PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray)); + } + + if (BatchAdapter is not null) + { + BatchAdapter.UpdatePoliciesAsync(section, policyType, oldRulesArray, newRulesArray).Wait(); + } + + return Task.FromResult(PolicyStore.UpdatePolicies(section, policyType, oldRulesArray, newRulesArray)); + } + finally + { + EndWrite(); + } + }); + } + public override Task RemovePolicyAsync(string section, string policyType, IEnumerable rule) { return Task.Run(() => diff --git a/Casbin/PolicyOperation.cs b/Casbin/PolicyOperation.cs index 980928b6..4f098340 100644 --- a/Casbin/PolicyOperation.cs +++ b/Casbin/PolicyOperation.cs @@ -3,6 +3,7 @@ public enum PolicyOperation { PolicyAdd, - PolicyRemove + PolicyRemove, + PolicyUpdate, } }