diff --git a/Casbin.UnitTests/Fixtures/TestModelFixture.cs b/Casbin.UnitTests/Fixtures/TestModelFixture.cs index 6c9e66a..7fc800d 100644 --- a/Casbin.UnitTests/Fixtures/TestModelFixture.cs +++ b/Casbin.UnitTests/Fixtures/TestModelFixture.cs @@ -16,6 +16,9 @@ public class TestModelFixture public static readonly string BasicWithoutUserPolicyText = ReadTestFile("basic_without_users_policy.csv"); public static readonly string BasicWithRootModelText = ReadTestFile("basic_with_root_model.conf"); + public static readonly string SaaRbacModelText = ReadTestFile("saa_model.conf"); + public static readonly string SaaRbacPolicyText = ReadTestFile("saa_rbac_policy.csv"); + public static readonly string RbacPolicyText = ReadTestFile("rbac_policy.csv"); public static readonly string RbacCommentText = ReadTestFile("rbac_comment.conf"); public static readonly string RbacInOperatorModelText = ReadTestFile("rbac_in_operator_model.conf"); @@ -119,6 +122,8 @@ public static IModel GetNewPriorityExplicitTestModel() => public static IModel GetNewPriorityExplicitDenyOverrideModel() => GetNewTestModel(PriorityExplicitDenyOverrideModelText, PriorityExplicitDenyOverridePolicyText); + public static IModel GetNewSaaRbacTestModel() => GetNewTestModel(SaaRbacModelText, SaaRbacPolicyText); + public static IModel GetNewRbacTestModel() => GetNewTestModel(RbacModelText, RbacPolicyText); public static IModel GetNewRbacWithDenyTestModel() => GetNewTestModel(RbacWithDenyModelText, RbacWithDenyPolicyText); diff --git a/Casbin.UnitTests/ModelTests/ManagementApiTest.cs b/Casbin.UnitTests/ModelTests/ManagementApiTest.cs index adf92b6..b4d45a7 100644 --- a/Casbin.UnitTests/ModelTests/ManagementApiTest.cs +++ b/Casbin.UnitTests/ModelTests/ManagementApiTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Casbin.Model; using Casbin.UnitTests.Extensions; @@ -323,6 +324,46 @@ public async Task TestModifyPolicyAsync() Assert.False(res); } + private static List> GenerateGroupingRules(int numberOfItems) + { + var groupingRules = new List>(); + + for (int i = 1; i <= numberOfItems; i++) + { + string parent = $"Parent{i}"; + string child = $"Child{i}"; + groupingRules.Add(new List { parent, child }); + } + + return groupingRules; + } + + [Fact] + public void TestConcurrentModifyGroupingPolicy() + { + Enforcer e = new(TestModelFixture.GetNewSaaRbacTestModel()); + e.BuildRoleLinks(); + + // Arrange + var policiesBeforeAct = e.GetNamedGroupingPolicy(PermConstants.GroupingPolicyType2).ToArray(); + Assert.Empty(policiesBeforeAct); + + var groupingRules = GenerateGroupingRules(1000); + + Task.WaitAll(groupingRules.Select(rule => Task.Run(() => + { + bool result = e.AddNamedGroupingPolicies(PermConstants.GroupingPolicyType2, new List> { rule }); + Assert.True(result); + })).ToArray()); + + // Assert + var policiesAfterAct = e.GetNamedGroupingPolicy(PermConstants.GroupingPolicyType2).ToArray(); + foreach (var policy in groupingRules) + { + Assert.Contains(policy, policiesAfterAct); + } + } + [Fact] public void TestModifyGroupingPolicy() { diff --git a/Casbin.UnitTests/examples/saa_model.conf b/Casbin.UnitTests/examples/saa_model.conf new file mode 100644 index 0000000..97b862c --- /dev/null +++ b/Casbin.UnitTests/examples/saa_model.conf @@ -0,0 +1,17 @@ +[request_definition] +# request includes subject, domain parent of obj, object requesting access for, action requested +r = sub, dom, obj, act + +[policy_definition] +p = sub, dom, act + +[role_definition] +g = _, _ +g2 = _, _ +g3 = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && (g2(r.dom, p.dom) || r.obj == p.dom) && g3(r.act, p.act) diff --git a/Casbin.UnitTests/examples/saa_rbac_policy.csv b/Casbin.UnitTests/examples/saa_rbac_policy.csv new file mode 100644 index 0000000..2362ee1 --- /dev/null +++ b/Casbin.UnitTests/examples/saa_rbac_policy.csv @@ -0,0 +1,3 @@ +# sample policy, not needed for test +p, tellers, banks/1, wager-editor + diff --git a/Casbin/Model/DefaultPolicyStore.Node.cs b/Casbin/Model/DefaultPolicyStore.Node.cs index 8c469e3..e44e96d 100644 --- a/Casbin/Model/DefaultPolicyStore.Node.cs +++ b/Casbin/Model/DefaultPolicyStore.Node.cs @@ -34,7 +34,17 @@ public IReadOnlyList SetPolicy(List valuesList) => Interlocked.Exchange(ref Policy, valuesList); public bool ContainsPolicy(IPolicyValues values) - => PolicyTextSet.Contains(values.ToText()); + { + Lock.EnterReadLock(); + try + { + return PolicyTextSet.Contains(values.ToText()); + } + finally + { + Lock.ExitReadLock(); + } + } public bool ValidatePolicy(IPolicyValues values) { @@ -67,13 +77,13 @@ public bool TryAddPolicy(IPolicyValues values) try { Policy.Add(values); + PolicyTextSet.Add(values.ToText()); } finally { Lock.ExitWriteLock(); } - PolicyTextSet.Add(values.ToText()); return true; } @@ -244,13 +254,13 @@ bool LastLessOrEqualPriority(IPolicyValues v) { int lastIndex = Policy.FindLastIndex(LastLessOrEqualPriority); Policy.Insert(lastIndex + 1, values); + PolicyTextSet.Add(values.ToText()); } finally { Lock.ExitWriteLock(); } - PolicyTextSet.Add(values.ToText()); return true; }