Skip to content

Commit 6863c41

Browse files
fix: concurrent access issues with HashSet<string> (#361)
1 parent 6d3cb37 commit 6863c41

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

Casbin.UnitTests/Fixtures/TestModelFixture.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public class TestModelFixture
1616
public static readonly string BasicWithoutUserPolicyText = ReadTestFile("basic_without_users_policy.csv");
1717
public static readonly string BasicWithRootModelText = ReadTestFile("basic_with_root_model.conf");
1818

19+
public static readonly string SaaRbacModelText = ReadTestFile("saa_model.conf");
20+
public static readonly string SaaRbacPolicyText = ReadTestFile("saa_rbac_policy.csv");
21+
1922
public static readonly string RbacPolicyText = ReadTestFile("rbac_policy.csv");
2023
public static readonly string RbacCommentText = ReadTestFile("rbac_comment.conf");
2124
public static readonly string RbacInOperatorModelText = ReadTestFile("rbac_in_operator_model.conf");
@@ -123,6 +126,8 @@ public static IModel GetNewPriorityExplicitTestModel() =>
123126
public static IModel GetNewPriorityExplicitDenyOverrideModel() => GetNewTestModel(PriorityExplicitDenyOverrideModelText,
124127
PriorityExplicitDenyOverridePolicyText);
125128

129+
public static IModel GetNewSaaRbacTestModel() => GetNewTestModel(SaaRbacModelText, SaaRbacPolicyText);
130+
126131
public static IModel GetNewRbacTestModel() => GetNewTestModel(RbacModelText, RbacPolicyText);
127132

128133
public static IModel GetNewRbacWithDenyTestModel() => GetNewTestModel(RbacWithDenyModelText, RbacWithDenyPolicyText);

Casbin.UnitTests/ModelTests/ManagementApiTest.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using Casbin.Model;
45
using Casbin.UnitTests.Extensions;
@@ -323,6 +324,46 @@ public async Task TestModifyPolicyAsync()
323324
Assert.False(res);
324325
}
325326

327+
private static List<List<string>> GenerateGroupingRules(int numberOfItems)
328+
{
329+
var groupingRules = new List<List<string>>();
330+
331+
for (int i = 1; i <= numberOfItems; i++)
332+
{
333+
string parent = $"Parent{i}";
334+
string child = $"Child{i}";
335+
groupingRules.Add(new List<string> { parent, child });
336+
}
337+
338+
return groupingRules;
339+
}
340+
341+
[Fact]
342+
public void TestConcurrentModifyGroupingPolicy()
343+
{
344+
Enforcer e = new(TestModelFixture.GetNewSaaRbacTestModel());
345+
e.BuildRoleLinks();
346+
347+
// Arrange
348+
var policiesBeforeAct = e.GetNamedGroupingPolicy(PermConstants.GroupingPolicyType2).ToArray();
349+
Assert.Empty(policiesBeforeAct);
350+
351+
var groupingRules = GenerateGroupingRules(1000);
352+
353+
Task.WaitAll(groupingRules.Select(rule => Task.Run(() =>
354+
{
355+
bool result = e.AddNamedGroupingPolicies(PermConstants.GroupingPolicyType2, new List<List<string>> { rule });
356+
Assert.True(result);
357+
})).ToArray());
358+
359+
// Assert
360+
var policiesAfterAct = e.GetNamedGroupingPolicy(PermConstants.GroupingPolicyType2).ToArray();
361+
foreach (var policy in groupingRules)
362+
{
363+
Assert.Contains(policy, policiesAfterAct);
364+
}
365+
}
366+
326367
[Fact]
327368
public void TestModifyGroupingPolicy()
328369
{
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[request_definition]
2+
# request includes subject, domain parent of obj, object requesting access for, action requested
3+
r = sub, dom, obj, act
4+
5+
[policy_definition]
6+
p = sub, dom, act
7+
8+
[role_definition]
9+
g = _, _
10+
g2 = _, _
11+
g3 = _, _
12+
13+
[policy_effect]
14+
e = some(where (p.eft == allow))
15+
16+
[matchers]
17+
m = g(r.sub, p.sub) && (g2(r.dom, p.dom) || r.obj == p.dom) && g3(r.act, p.act)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# sample policy, not needed for test
2+
p, tellers, banks/1, wager-editor
3+

Casbin/Model/DefaultPolicyStore.Node.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ public IReadOnlyList<IPolicyValues> SetPolicy(List<IPolicyValues> valuesList) =>
3434
Interlocked.Exchange(ref Policy, valuesList);
3535

3636
public bool ContainsPolicy(IPolicyValues values)
37-
=> PolicyTextSet.Contains(values.ToText());
37+
{
38+
Lock.EnterReadLock();
39+
try
40+
{
41+
return PolicyTextSet.Contains(values.ToText());
42+
}
43+
finally
44+
{
45+
Lock.ExitReadLock();
46+
}
47+
}
3848

3949
public bool ValidatePolicy(IPolicyValues values)
4050
{
@@ -67,13 +77,13 @@ public bool TryAddPolicy(IPolicyValues values)
6777
try
6878
{
6979
Policy.Add(values);
80+
PolicyTextSet.Add(values.ToText());
7081
}
7182
finally
7283
{
7384
Lock.ExitWriteLock();
7485
}
7586

76-
PolicyTextSet.Add(values.ToText());
7787
return true;
7888
}
7989

@@ -244,13 +254,13 @@ bool LastLessOrEqualPriority(IPolicyValues v)
244254
{
245255
int lastIndex = Policy.FindLastIndex(LastLessOrEqualPriority);
246256
Policy.Insert(lastIndex + 1, values);
257+
PolicyTextSet.Add(values.ToText());
247258
}
248259
finally
249260
{
250261
Lock.ExitWriteLock();
251262
}
252263

253-
PolicyTextSet.Add(values.ToText());
254264
return true;
255265
}
256266

0 commit comments

Comments
 (0)