Skip to content

Commit 95143b6

Browse files
Copilothsluoyz
andcommitted
Add LoadIncrementalFilteredPolicy functionality
Co-authored-by: hsluoyz <[email protected]>
1 parent 2aa9d2b commit 95143b6

File tree

5 files changed

+287
-0
lines changed

5 files changed

+287
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
using System.IO;
2+
using System.Threading.Tasks;
3+
using Casbin.Model;
4+
using Casbin.Persist;
5+
using Casbin.Persist.Adapter.File;
6+
using Xunit;
7+
8+
namespace Casbin.UnitTests.PersistTests;
9+
10+
public class IncrementalFilteredAdapterTest
11+
{
12+
[Fact]
13+
public void TestLoadIncrementalFilteredPolicy()
14+
{
15+
Enforcer e = new("Examples/rbac_model.conf");
16+
Assert.False(e.Enforce("alice", "data1", "read"));
17+
18+
FileAdapter a = new("Examples/rbac_policy.csv");
19+
e.SetAdapter(a);
20+
21+
// Load only p policies for alice
22+
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
23+
24+
// alice can read data1 (from p policy)
25+
Assert.True(e.Enforce("alice", "data1", "read"));
26+
// bob cannot write data2 (not loaded yet)
27+
Assert.False(e.Enforce("bob", "data2", "write"));
28+
29+
// Incrementally load p policies for data2_admin role
30+
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["data2_admin"])));
31+
32+
// alice still can only read data1 (no role link yet)
33+
Assert.True(e.Enforce("alice", "data1", "read"));
34+
Assert.False(e.Enforce("alice", "data2", "read"));
35+
36+
// Incrementally load g policies for alice
37+
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultRoleType, 0, Policy.ValuesFrom(["alice"])));
38+
39+
// Now alice can read data2 through role inheritance
40+
Assert.True(e.Enforce("alice", "data2", "read"));
41+
Assert.True(e.Enforce("alice", "data2", "write"));
42+
// bob still cannot write data2 (not loaded)
43+
Assert.False(e.Enforce("bob", "data2", "write"));
44+
}
45+
46+
[Fact]
47+
public async Task TestLoadIncrementalFilteredPolicyAsync()
48+
{
49+
Enforcer e = new("Examples/rbac_model.conf");
50+
Assert.False(await e.EnforceAsync("alice", "data1", "read"));
51+
52+
FileAdapter a = new("Examples/rbac_policy.csv");
53+
e.SetAdapter(a);
54+
55+
// Load only p policies for alice
56+
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
57+
58+
// alice can read data1 (from p policy)
59+
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
60+
// bob cannot write data2 (not loaded yet)
61+
Assert.False(await e.EnforceAsync("bob", "data2", "write"));
62+
63+
// Incrementally load p policies for data2_admin role
64+
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["data2_admin"])));
65+
66+
// alice still can only read data1 (no role link yet)
67+
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
68+
Assert.False(await e.EnforceAsync("alice", "data2", "read"));
69+
70+
// Incrementally load g policies for alice
71+
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultRoleType, 0, Policy.ValuesFrom(["alice"])));
72+
73+
// Now alice can read data2 through role inheritance
74+
Assert.True(await e.EnforceAsync("alice", "data2", "read"));
75+
Assert.True(await e.EnforceAsync("alice", "data2", "write"));
76+
// bob still cannot write data2 (not loaded)
77+
Assert.False(await e.EnforceAsync("bob", "data2", "write"));
78+
}
79+
80+
[Fact]
81+
public void TestLoadIncrementalFilteredPolicyMultiplePTypes()
82+
{
83+
Enforcer e = new("Examples/rbac_model.conf");
84+
Assert.False(e.Enforce("alice", "data1", "read"));
85+
86+
FileAdapter a = new("Examples/rbac_policy.csv");
87+
e.SetAdapter(a);
88+
89+
// Load only p policies for alice
90+
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
91+
92+
// alice can read data1
93+
Assert.True(e.Enforce("alice", "data1", "read"));
94+
// bob cannot write data2
95+
Assert.False(e.Enforce("bob", "data2", "write"));
96+
97+
// Incrementally load p policies for bob
98+
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));
99+
100+
// alice still can read data1
101+
Assert.True(e.Enforce("alice", "data1", "read"));
102+
// Now bob can write data2
103+
Assert.True(e.Enforce("bob", "data2", "write"));
104+
}
105+
106+
[Fact]
107+
public async Task TestLoadIncrementalFilteredPolicyMultiplePTypesAsync()
108+
{
109+
Enforcer e = new("Examples/rbac_model.conf");
110+
Assert.False(await e.EnforceAsync("alice", "data1", "read"));
111+
112+
FileAdapter a = new("Examples/rbac_policy.csv");
113+
e.SetAdapter(a);
114+
115+
// Load only p policies for alice
116+
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
117+
118+
// alice can read data1
119+
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
120+
// bob cannot write data2
121+
Assert.False(await e.EnforceAsync("bob", "data2", "write"));
122+
123+
// Incrementally load p policies for bob
124+
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));
125+
126+
// alice still can read data1
127+
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
128+
// Now bob can write data2
129+
Assert.True(await e.EnforceAsync("bob", "data2", "write"));
130+
}
131+
132+
[Fact]
133+
public void TestLoadFilteredPolicyClearsExistingPolicies()
134+
{
135+
Enforcer e = new("Examples/rbac_model.conf");
136+
FileAdapter a = new("Examples/rbac_policy.csv");
137+
e.SetAdapter(a);
138+
139+
// Load only p policies for alice
140+
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
141+
Assert.True(e.Enforce("alice", "data1", "read"));
142+
143+
// Load filtered policy again (should clear previous policies)
144+
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));
145+
146+
// alice can no longer read data1 (previous policies cleared)
147+
Assert.False(e.Enforce("alice", "data1", "read"));
148+
// bob can write data2 (new filtered policies loaded)
149+
Assert.True(e.Enforce("bob", "data2", "write"));
150+
}
151+
152+
[Fact]
153+
public async Task TestLoadFilteredPolicyClearsExistingPoliciesAsync()
154+
{
155+
Enforcer e = new("Examples/rbac_model.conf");
156+
FileAdapter a = new("Examples/rbac_policy.csv");
157+
e.SetAdapter(a);
158+
159+
// Load only p policies for alice
160+
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
161+
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
162+
163+
// Load filtered policy again (should clear previous policies)
164+
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));
165+
166+
// alice can no longer read data1 (previous policies cleared)
167+
Assert.False(await e.EnforceAsync("alice", "data1", "read"));
168+
// bob can write data2 (new filtered policies loaded)
169+
Assert.True(await e.EnforceAsync("bob", "data2", "write"));
170+
}
171+
}

Casbin/Abstractions/Persist/BaseAdapter.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,36 @@ public Task LoadFilteredPolicyAsync(IPolicyStore store, Filter filter)
221221
#endif
222222
}
223223

224+
public void LoadIncrementalFilteredPolicy(IPolicyStore store, IPolicyFilter filter)
225+
{
226+
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
227+
if (filter is null)
228+
{
229+
LoadPolicy(store);
230+
return;
231+
}
232+
233+
IEnumerable<IPersistPolicy> policies = ReadPersistPolicy();
234+
policies = filter.Apply(policies.AsQueryable());
235+
foreach (IPersistPolicy policy in policies)
236+
{
237+
int requiredCount = store.GetRequiredValuesCount(policy.Section, policy.Type);
238+
IPolicyValues values = Policy.ValuesFrom(policy, requiredCount);
239+
store.AddPolicy(policy.Section, policy.Type, values);
240+
}
241+
IsFiltered = true;
242+
}
243+
244+
public Task LoadIncrementalFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter)
245+
{
246+
LoadIncrementalFilteredPolicy(store, filter);
247+
#if !NET452
248+
return Task.CompletedTask;
249+
#else
250+
return Task.FromResult(true);
251+
#endif
252+
}
253+
224254
#endregion
225255

226256
protected IEnumerable<IPersistPolicy> ReadPersistPolicy()

Casbin/Abstractions/Persist/IFilteredAdapter.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@ public interface IFilteredAdapter
1010
void LoadFilteredPolicy(IPolicyStore store, IPolicyFilter filter);
1111

1212
Task LoadFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter);
13+
14+
void LoadIncrementalFilteredPolicy(IPolicyStore store, IPolicyFilter filter);
15+
16+
Task LoadIncrementalFilteredPolicyAsync(IPolicyStore store, IPolicyFilter filter);
1317
}
1418
}

Casbin/Extensions/Enforcer/EnforcerExtension.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ public static bool LoadFilteredPolicy(this IEnforcer enforcer, IPolicyFilter fil
229229
return false;
230230
}
231231

232+
enforcer.ClearCache();
232233
if (enforcer.AutoBuildRoleLinks)
233234
{
234235
enforcer.BuildRoleLinks();
@@ -251,6 +252,7 @@ public static async Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer,
251252
return false;
252253
}
253254

255+
enforcer.ClearCache();
254256
if (enforcer.AutoBuildRoleLinks)
255257
{
256258
enforcer.BuildRoleLinks();
@@ -269,6 +271,52 @@ public static async Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer,
269271
public static Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer, Filter filter) =>
270272
LoadFilteredPolicyAsync(enforcer, filter as IPolicyFilter);
271273

274+
/// <summary>
275+
/// Appends a filtered policy from file/database without clearing the existing policies.
276+
/// </summary>
277+
/// <param name="enforcer"></param>
278+
/// <param name="filter">The filter used to specify which type of policy should be loaded.</param>
279+
/// <returns></returns>
280+
public static bool LoadIncrementalFilteredPolicy(this IEnforcer enforcer, IPolicyFilter filter)
281+
{
282+
bool result = enforcer.Model.LoadIncrementalFilteredPolicy(filter);
283+
if (result is false)
284+
{
285+
return false;
286+
}
287+
288+
enforcer.ClearCache();
289+
if (enforcer.AutoBuildRoleLinks)
290+
{
291+
enforcer.BuildRoleLinks();
292+
}
293+
294+
return true;
295+
}
296+
297+
/// <summary>
298+
/// Appends a filtered policy from file/database without clearing the existing policies.
299+
/// </summary>
300+
/// <param name="enforcer"></param>
301+
/// <param name="filter">The filter used to specify which type of policy should be loaded.</param>
302+
/// <returns></returns>
303+
public static async Task<bool> LoadIncrementalFilteredPolicyAsync(this IEnforcer enforcer, IPolicyFilter filter)
304+
{
305+
bool result = await enforcer.Model.LoadIncrementalFilteredPolicyAsync(filter);
306+
if (result is false)
307+
{
308+
return false;
309+
}
310+
311+
enforcer.ClearCache();
312+
if (enforcer.AutoBuildRoleLinks)
313+
{
314+
enforcer.BuildRoleLinks();
315+
}
316+
317+
return true;
318+
}
319+
272320
/// <summary>
273321
/// Saves the current policy (usually after changed with Casbin API)
274322
/// back to file/database.

Casbin/Extensions/Model/ModelExtension.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,40 @@ await model.AdapterHolder.FilteredAdapter.LoadFilteredPolicyAsync(policyStore,
155155
return true;
156156
}
157157

158+
public static bool LoadIncrementalFilteredPolicy(this IModel model, IPolicyFilter filter)
159+
{
160+
if (model.AdapterHolder.Adapter is null)
161+
{
162+
return false;
163+
}
164+
165+
if (model.AdapterHolder.FilteredAdapter is null)
166+
{
167+
return false;
168+
}
169+
170+
model.AdapterHolder.FilteredAdapter.LoadIncrementalFilteredPolicy(
171+
model.PolicyStoreHolder.PolicyStore, filter);
172+
return true;
173+
}
174+
175+
public static async Task<bool> LoadIncrementalFilteredPolicyAsync(this IModel model, IPolicyFilter filter)
176+
{
177+
if (model.AdapterHolder.Adapter is null)
178+
{
179+
return false;
180+
}
181+
182+
if (model.AdapterHolder.FilteredAdapter is null)
183+
{
184+
return false;
185+
}
186+
187+
await model.AdapterHolder.FilteredAdapter.LoadIncrementalFilteredPolicyAsync(
188+
model.PolicyStoreHolder.PolicyStore, filter);
189+
return true;
190+
}
191+
158192
public static bool SavePolicy(this IModel model)
159193
{
160194
if (model.AdapterHolder.Adapter is null)

0 commit comments

Comments
 (0)