Skip to content

Commit

Permalink
feat: Add auto clear cache option
Browse files Browse the repository at this point in the history
Signed-off-by: Sagilio <Sagilio@outlook.com>
  • Loading branch information
sagilio committed Mar 30, 2021
1 parent edeeed6 commit ed292e6
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 27 deletions.
29 changes: 29 additions & 0 deletions NetCasbin.UnitTest/EnforcerCacheTest.cs
Expand Up @@ -30,6 +30,7 @@ public void TestEnforceWithCache()
var e = new Enforcer(_testModelFixture.GetBasicTestModel());
#endif
e.EnableCache(true);
e.EnableAutoCleanEnforceCache(false);

TestEnforce(e, "alice", "data1", "read", true);
TestEnforce(e, "alice", "data1", "write", false);
Expand All @@ -54,5 +55,33 @@ public void TestEnforceWithCache()
TestEnforce(e, "alice", "data2", "read", false);
TestEnforce(e, "alice", "data2", "write", false);
}

[Fact]
public void TestAutoCleanCache()
{
#if !NET452
var e = new Enforcer(_testModelFixture.GetBasicTestModel())
{
Logger = new MockLogger<Enforcer>(_testOutputHelper)
};
#else
var e = new Enforcer(_testModelFixture.GetBasicTestModel());
#endif
e.EnableCache(true);

TestEnforce(e, "alice", "data1", "read", true);
TestEnforce(e, "alice", "data1", "write", false);
TestEnforce(e, "alice", "data2", "read", false);
TestEnforce(e, "alice", "data2", "write", false);

// The cache is enabled, so even if we remove a policy rule, the decision
// for ("alice", "data1", "read") will still be true, as it uses the cached result.
_ = e.RemovePolicy("alice", "data1", "read");

TestEnforce(e, "alice", "data1", "read", false);
TestEnforce(e, "alice", "data1", "write", false);
TestEnforce(e, "alice", "data2", "read", false);
TestEnforce(e, "alice", "data2", "write", false);
}
}
}
99 changes: 75 additions & 24 deletions NetCasbin/CoreEnforcer.cs
Expand Up @@ -36,12 +36,12 @@ public class CoreEnforcer : ICoreEnforcer
protected bool autoSave;
protected bool autoBuildRoleLinks;
protected bool autoNotifyWatcher;
protected bool autoCleanEnforceCache = true;

internal IExpressionHandler ExpressionHandler { get; private set; }

private bool _enableCache;
public IEnforceCache EnforceCache { get; set; }

public IEnforceCache EnforceCache { get; private set; }
#if !NET45
public ILogger Logger { get; set; }
#endif
Expand Down Expand Up @@ -192,7 +192,13 @@ public void SetEnforceCache(IEnforceCache enforceCache)
public void ClearPolicy()
{
model.ClearPolicy();

if (autoCleanEnforceCache)
{
EnforceCache?.Clear();
#if !NET45
Logger?.LogInformation("Enforcer Cache, Cleared all enforce cache.");
#endif
}
#if !NET45
Logger?.LogInformation("Policy Management, Cleared all policy.");
#endif
Expand All @@ -208,7 +214,7 @@ public void LoadPolicy()
return;
}

model.ClearPolicy();
ClearPolicy();
adapter.LoadPolicy(model);
model.RefreshPolicyStringSet();
if (autoBuildRoleLinks)
Expand All @@ -227,7 +233,7 @@ public async Task LoadPolicyAsync()
return;
}

model.ClearPolicy();
ClearPolicy();
await adapter.LoadPolicyAsync(model);
if (autoBuildRoleLinks)
{
Expand All @@ -242,7 +248,7 @@ public async Task LoadPolicyAsync()
/// <returns></returns>
public bool LoadFilteredPolicy(Filter filter)
{
model.ClearPolicy();
ClearPolicy();
if (adapter is not IFilteredAdapter filteredAdapter)
{
throw new NotSupportedException("Filtered policies are not supported by this adapter.");
Expand All @@ -264,7 +270,7 @@ public bool LoadFilteredPolicy(Filter filter)
/// <returns></returns>
public async Task<bool> LoadFilteredPolicyAsync(Filter filter)
{
model.ClearPolicy();
ClearPolicy();
if (adapter is not IFilteredAdapter filteredAdapter)
{
throw new NotSupportedException("Filtered policies are not supported by this adapter.");
Expand Down Expand Up @@ -373,6 +379,11 @@ public void EnableCache(bool enableCache)
_enableCache = enableCache;
}

public void EnableAutoCleanEnforceCache(bool autoCleanEnforceCache)
{
this.autoCleanEnforceCache = autoCleanEnforceCache;
}

/// <summary>
/// Manually rebuilds the role inheritance relations.
/// </summary>
Expand All @@ -389,7 +400,7 @@ public void BuildRoleLinks()
/// <param name="requestValues">The request needs to be mediated, usually an array of strings,
/// can be class instances if ABAC is used.</param>
/// <returns>Whether to allow the request.</returns>
public async Task<bool> EnforceAsync(params object[] requestValues)
public bool Enforce(params object[] requestValues)
{
if (_enableCache is false)
{
Expand All @@ -403,20 +414,17 @@ public async Task<bool> EnforceAsync(params object[] requestValues)

string key = string.Join("$$", requestValues);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
bool? tryGetCachedResult = await EnforceCache.TryGetResultAsync(requestValues, key);
if (tryGetCachedResult.HasValue)
if (EnforceCache.TryGetResult(requestValues, key, out bool cachedResult))
{
bool cachedResult = tryGetCachedResult.Value;
#if !NET45
Logger?.LogEnforceCachedResult(requestValues, cachedResult);
#endif
return cachedResult;
}

bool result = InternalEnforce(requestValues);

bool result = InternalEnforce(requestValues);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
await EnforceCache.TrySetResultAsync(requestValues, key, result);
EnforceCache.TrySetResult(requestValues, key, result);
return result;
}

Expand All @@ -427,31 +435,34 @@ public async Task<bool> EnforceAsync(params object[] requestValues)
/// <param name="requestValues">The request needs to be mediated, usually an array of strings,
/// can be class instances if ABAC is used.</param>
/// <returns>Whether to allow the request.</returns>
public bool Enforce(params object[] requestValues)
public async Task<bool> EnforceAsync(params object[] requestValues)
{
if (_enableCache is false)
{
return InternalEnforce(requestValues);
return await InternalEnforceAsync(requestValues);
}

if (requestValues.Any(requestValue => requestValue is not string))
{
return InternalEnforce(requestValues);
return await InternalEnforceAsync(requestValues);
}

string key = string.Join("$$", requestValues);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
if (EnforceCache.TryGetResult(requestValues, key, out bool cachedResult))
bool? tryGetCachedResult = await EnforceCache.TryGetResultAsync(requestValues, key);
if (tryGetCachedResult.HasValue)
{
bool cachedResult = tryGetCachedResult.Value;
#if !NET45
Logger?.LogEnforceCachedResult(requestValues, cachedResult);
#endif
return cachedResult;
}

bool result = InternalEnforce(requestValues);
bool result = await InternalEnforceAsync(requestValues);

EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
EnforceCache.TrySetResult(requestValues, key, result);
await EnforceCache.TrySetResultAsync(requestValues, key, result);
return result;
}

Expand All @@ -466,7 +477,27 @@ public bool Enforce(params object[] requestValues)
EnforceEx(params object[] requestValues)
{
var explains = new List<IEnumerable<string>>();
bool result = InternalEnforce(requestValues, explains);
if (_enableCache is false)
{
return (InternalEnforce(requestValues, explains), explains);
}

if (requestValues.Any(requestValue => requestValue is not string))
{
return (InternalEnforce(requestValues, explains), explains);
}

string key = string.Join("$$", requestValues);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
if (EnforceCache.TryGetResult(requestValues, key, out bool cachedResult))
{
Logger?.LogEnforceCachedResult(requestValues, cachedResult);
return (cachedResult, explains);
}

bool result = InternalEnforce(requestValues, explains);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
EnforceCache.TrySetResult(requestValues, key, result);
return (result, explains);
}
#else
Expand Down Expand Up @@ -496,15 +527,35 @@ public bool Enforce(params object[] requestValues)
EnforceExAsync(params object[] requestValues)
{
var explains = new List<IEnumerable<string>>();
bool result = await EnforceAsync(requestValues, explains);
if (_enableCache is false)
{
return (await InternalEnforceAsync(requestValues, explains), explains);
}

if (requestValues.Any(requestValue => requestValue is not string))
{
return (await InternalEnforceAsync(requestValues, explains), explains);
}

string key = string.Join("$$", requestValues);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
if (EnforceCache.TryGetResult(requestValues, key, out bool cachedResult))
{
Logger?.LogEnforceCachedResult(requestValues, cachedResult);
return (cachedResult, explains);
}

bool result = await InternalEnforceAsync(requestValues, explains);
EnforceCache ??= new ReaderWriterEnforceCache(new ReaderWriterEnforceCacheOptions());
await EnforceCache.TrySetResultAsync(requestValues, key, result);
return (result, explains);
}
#else
public async Task<Tuple<bool, IEnumerable<IEnumerable<string>>>>
EnforceExAsync(params object[] requestValues)
{
var explains = new List<IEnumerable<string>>();
bool result = await EnforceAsync(requestValues, explains);
bool result = await InternalEnforceAsync(requestValues, explains);
return new Tuple<bool, IEnumerable<IEnumerable<string>>>(result, explains);
}
#endif
Expand All @@ -517,7 +568,7 @@ public bool Enforce(params object[] requestValues)
/// can be class instances if ABAC is used.</param>
/// <param name="explains"></param>
/// <returns>Whether to allow the request.</returns>
private Task<bool> EnforceAsync(IReadOnlyList<object> requestValues, ICollection<IEnumerable<string>> explains = null)
private Task<bool> InternalEnforceAsync(IReadOnlyList<object> requestValues, ICollection<IEnumerable<string>> explains = null)
{
return Task.FromResult(InternalEnforce(requestValues, explains));
}
Expand Down
22 changes: 19 additions & 3 deletions NetCasbin/InternalEnforcer.cs
@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Schema;
#if !NET45
using Microsoft.Extensions.Logging;
#endif
using NetCasbin.Model;

namespace NetCasbin
Expand Down Expand Up @@ -455,6 +455,14 @@ protected async Task<bool> InternalRemoveFilteredPolicyAsync(string sec, string

private void NotifyPolicyChanged()
{
if (autoCleanEnforceCache)
{
EnforceCache?.Clear();
#if !NET45
Logger?.LogInformation("Enforcer Cache, Cleared all enforce cache.");
#endif
}

if (autoNotifyWatcher)
{
watcher?.Update();
Expand All @@ -463,6 +471,14 @@ private void NotifyPolicyChanged()

private async Task NotifyPolicyChangedAsync()
{
if (autoCleanEnforceCache && EnforceCache is not null)
{
await EnforceCache.ClearAsync();
#if !NET45
Logger?.LogInformation("Enforcer Cache, Cleared all enforce cache.");
#endif
}

if (autoNotifyWatcher && watcher is not null)
{
await watcher.UpdateAsync();
Expand Down

0 comments on commit ed292e6

Please sign in to comment.