Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions Casbin.UnitTests/PersistTests/IncrementalFilteredAdapterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
using System.IO;
using System.Threading.Tasks;
using Casbin.Model;
using Casbin.Persist;
using Casbin.Persist.Adapter.File;
using Xunit;

namespace Casbin.UnitTests.PersistTests;

public class IncrementalFilteredAdapterTest
{
[Fact]
public void TestLoadIncrementalFilteredPolicy()
{
Enforcer e = new("Examples/rbac_model.conf");
Assert.False(e.Enforce("alice", "data1", "read"));

FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));

// alice can read data1 (from p policy)
Assert.True(e.Enforce("alice", "data1", "read"));
// bob cannot write data2 (not loaded yet)
Assert.False(e.Enforce("bob", "data2", "write"));

// Incrementally load p policies for data2_admin role
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["data2_admin"])));

// alice still can only read data1 (no role link yet)
Assert.True(e.Enforce("alice", "data1", "read"));
Assert.False(e.Enforce("alice", "data2", "read"));

// Incrementally load g policies for alice
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultRoleType, 0, Policy.ValuesFrom(["alice"])));

// Now alice can read data2 through role inheritance
Assert.True(e.Enforce("alice", "data2", "read"));
Assert.True(e.Enforce("alice", "data2", "write"));
// bob still cannot write data2 (not loaded)
Assert.False(e.Enforce("bob", "data2", "write"));
}

[Fact]
public async Task TestLoadIncrementalFilteredPolicyAsync()
{
Enforcer e = new("Examples/rbac_model.conf");
Assert.False(await e.EnforceAsync("alice", "data1", "read"));

FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));

// alice can read data1 (from p policy)
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
// bob cannot write data2 (not loaded yet)
Assert.False(await e.EnforceAsync("bob", "data2", "write"));

// Incrementally load p policies for data2_admin role
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["data2_admin"])));

// alice still can only read data1 (no role link yet)
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
Assert.False(await e.EnforceAsync("alice", "data2", "read"));

// Incrementally load g policies for alice
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultRoleType, 0, Policy.ValuesFrom(["alice"])));

// Now alice can read data2 through role inheritance
Assert.True(await e.EnforceAsync("alice", "data2", "read"));
Assert.True(await e.EnforceAsync("alice", "data2", "write"));
// bob still cannot write data2 (not loaded)
Assert.False(await e.EnforceAsync("bob", "data2", "write"));
}

[Fact]
public void TestLoadIncrementalFilteredPolicyMultiplePTypes()
{
Enforcer e = new("Examples/rbac_model.conf");
Assert.False(e.Enforce("alice", "data1", "read"));

FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));

// alice can read data1
Assert.True(e.Enforce("alice", "data1", "read"));
// bob cannot write data2
Assert.False(e.Enforce("bob", "data2", "write"));

// Incrementally load p policies for bob
e.LoadIncrementalFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));

// alice still can read data1
Assert.True(e.Enforce("alice", "data1", "read"));
// Now bob can write data2
Assert.True(e.Enforce("bob", "data2", "write"));
}

[Fact]
public async Task TestLoadIncrementalFilteredPolicyMultiplePTypesAsync()
{
Enforcer e = new("Examples/rbac_model.conf");
Assert.False(await e.EnforceAsync("alice", "data1", "read"));

FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));

// alice can read data1
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
// bob cannot write data2
Assert.False(await e.EnforceAsync("bob", "data2", "write"));

// Incrementally load p policies for bob
await e.LoadIncrementalFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));

// alice still can read data1
Assert.True(await e.EnforceAsync("alice", "data1", "read"));
// Now bob can write data2
Assert.True(await e.EnforceAsync("bob", "data2", "write"));
}

[Fact]
public void TestLoadFilteredPolicyClearsExistingPolicies()
{
Enforcer e = new("Examples/rbac_model.conf");
FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
Assert.True(e.Enforce("alice", "data1", "read"));

// Load filtered policy again (should clear previous policies)
e.LoadFilteredPolicy(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));

// alice can no longer read data1 (previous policies cleared)
Assert.False(e.Enforce("alice", "data1", "read"));
// bob can write data2 (new filtered policies loaded)
Assert.True(e.Enforce("bob", "data2", "write"));
}

[Fact]
public async Task TestLoadFilteredPolicyClearsExistingPoliciesAsync()
{
Enforcer e = new("Examples/rbac_model.conf");
FileAdapter a = new("Examples/rbac_policy.csv");
e.SetAdapter(a);

// Load only p policies for alice
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["alice"])));
Assert.True(await e.EnforceAsync("alice", "data1", "read"));

// Load filtered policy again (should clear previous policies)
await e.LoadFilteredPolicyAsync(new PolicyFilter(PermConstants.DefaultPolicyType, 0, Policy.ValuesFrom(["bob"])));

// alice can no longer read data1 (previous policies cleared)
Assert.False(await e.EnforceAsync("alice", "data1", "read"));
// bob can write data2 (new filtered policies loaded)
Assert.True(await e.EnforceAsync("bob", "data2", "write"));
}
}
48 changes: 48 additions & 0 deletions Casbin/Extensions/Enforcer/EnforcerExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ public static bool LoadFilteredPolicy(this IEnforcer enforcer, IPolicyFilter fil
return false;
}

enforcer.ClearCache();
if (enforcer.AutoBuildRoleLinks)
{
enforcer.BuildRoleLinks();
Expand All @@ -251,6 +252,7 @@ public static async Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer,
return false;
}

enforcer.ClearCache();
if (enforcer.AutoBuildRoleLinks)
{
enforcer.BuildRoleLinks();
Expand All @@ -269,6 +271,52 @@ public static async Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer,
public static Task<bool> LoadFilteredPolicyAsync(this IEnforcer enforcer, Filter filter) =>
LoadFilteredPolicyAsync(enforcer, filter as IPolicyFilter);

/// <summary>
/// Appends a filtered policy from file/database without clearing the existing policies.
/// </summary>
/// <param name="enforcer"></param>
/// <param name="filter">The filter used to specify which type of policy should be loaded.</param>
/// <returns></returns>
public static bool LoadIncrementalFilteredPolicy(this IEnforcer enforcer, IPolicyFilter filter)
{
bool result = enforcer.Model.LoadIncrementalFilteredPolicy(filter);
if (result is false)
{
return false;
}

enforcer.ClearCache();
if (enforcer.AutoBuildRoleLinks)
{
enforcer.BuildRoleLinks();
}

return true;
}

/// <summary>
/// Appends a filtered policy from file/database without clearing the existing policies.
/// </summary>
/// <param name="enforcer"></param>
/// <param name="filter">The filter used to specify which type of policy should be loaded.</param>
/// <returns></returns>
public static async Task<bool> LoadIncrementalFilteredPolicyAsync(this IEnforcer enforcer, IPolicyFilter filter)
{
bool result = await enforcer.Model.LoadIncrementalFilteredPolicyAsync(filter);
if (result is false)
{
return false;
}

enforcer.ClearCache();
if (enforcer.AutoBuildRoleLinks)
{
enforcer.BuildRoleLinks();
}

return true;
}

/// <summary>
/// Saves the current policy (usually after changed with Casbin API)
/// back to file/database.
Expand Down
73 changes: 51 additions & 22 deletions Casbin/Extensions/Model/ModelExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,28 @@ public static async Task<bool> LoadPolicyAsync(this IModel model)
}

public static bool LoadFilteredPolicy(this IModel model, IPolicyFilter filter)
{
model.PolicyStoreHolder.PolicyStore?.ClearPolicy();
return LoadFilteredPolicyInternal(model, filter);
}

public static async Task<bool> LoadFilteredPolicyAsync(this IModel model, IPolicyFilter filter)
{
model.PolicyStoreHolder.PolicyStore?.ClearPolicy();
return await LoadFilteredPolicyInternalAsync(model, filter);
}

public static bool LoadIncrementalFilteredPolicy(this IModel model, IPolicyFilter filter)
{
return LoadFilteredPolicyInternal(model, filter);
}

public static async Task<bool> LoadIncrementalFilteredPolicyAsync(this IModel model, IPolicyFilter filter)
{
return await LoadFilteredPolicyInternalAsync(model, filter);
}

private static bool LoadFilteredPolicyInternal(IModel model, IPolicyFilter filter)
{
if (model.AdapterHolder.Adapter is null)
{
Expand All @@ -104,26 +126,30 @@ public static bool LoadFilteredPolicy(this IModel model, IPolicyFilter filter)
return false;
}

DefaultPolicyStore policyStore = new();
foreach (KeyValuePair<string, PolicyAssertion> pair in model.Sections.GetPolicyAssertions())
// Initialize policy store if it doesn't exist
if (model.PolicyStoreHolder.PolicyStore is null)
{
policyStore.AddNode(PermConstants.Section.PolicySection, pair.Key, pair.Value);
}
DefaultPolicyStore policyStore = new();
foreach (KeyValuePair<string, PolicyAssertion> pair in model.Sections.GetPolicyAssertions())
{
policyStore.AddNode(PermConstants.Section.PolicySection, pair.Key, pair.Value);
}

if (model.Sections.ContainsSection(PermConstants.Section.RoleSection))
{
foreach (KeyValuePair<string, RoleAssertion> pair in model.Sections.GetRoleAssertions())
if (model.Sections.ContainsSection(PermConstants.Section.RoleSection))
{
policyStore.AddNode(PermConstants.Section.RoleSection, pair.Key, pair.Value);
foreach (KeyValuePair<string, RoleAssertion> pair in model.Sections.GetRoleAssertions())
{
policyStore.AddNode(PermConstants.Section.RoleSection, pair.Key, pair.Value);
}
}
model.PolicyStoreHolder.PolicyStore = policyStore;
}

model.AdapterHolder.FilteredAdapter.LoadFilteredPolicy(policyStore, filter);
model.PolicyStoreHolder.PolicyStore = policyStore;
model.AdapterHolder.FilteredAdapter.LoadFilteredPolicy(model.PolicyStoreHolder.PolicyStore, filter);
return true;
}

public static async Task<bool> LoadFilteredPolicyAsync(this IModel model, IPolicyFilter filter)
private static async Task<bool> LoadFilteredPolicyInternalAsync(IModel model, IPolicyFilter filter)
{
if (model.AdapterHolder.Adapter is null)
{
Expand All @@ -135,23 +161,26 @@ public static async Task<bool> LoadFilteredPolicyAsync(this IModel model, IPolic
return false;
}

DefaultPolicyStore policyStore = new();
foreach (KeyValuePair<string, PolicyAssertion> pair in model.Sections.GetPolicyAssertions())
// Initialize policy store if it doesn't exist
if (model.PolicyStoreHolder.PolicyStore is null)
{
policyStore.AddNode(PermConstants.Section.PolicySection, pair.Key, pair.Value);
}
DefaultPolicyStore policyStore = new();
foreach (KeyValuePair<string, PolicyAssertion> pair in model.Sections.GetPolicyAssertions())
{
policyStore.AddNode(PermConstants.Section.PolicySection, pair.Key, pair.Value);
}

if (model.Sections.ContainsSection(PermConstants.Section.RoleSection))
{
foreach (KeyValuePair<string, RoleAssertion> pair in model.Sections.GetRoleAssertions())
if (model.Sections.ContainsSection(PermConstants.Section.RoleSection))
{
policyStore.AddNode(PermConstants.Section.RoleSection, pair.Key, pair.Value);
foreach (KeyValuePair<string, RoleAssertion> pair in model.Sections.GetRoleAssertions())
{
policyStore.AddNode(PermConstants.Section.RoleSection, pair.Key, pair.Value);
}
}
model.PolicyStoreHolder.PolicyStore = policyStore;
}

await model.AdapterHolder.FilteredAdapter.LoadFilteredPolicyAsync(policyStore,
filter);
model.PolicyStoreHolder.PolicyStore = policyStore;
await model.AdapterHolder.FilteredAdapter.LoadFilteredPolicyAsync(model.PolicyStoreHolder.PolicyStore, filter);
return true;
}

Expand Down
Loading
Loading