diff --git a/Casbin.UnitTests/Mock/MockLogger.cs b/Casbin.UnitTests/Mock/MockLogger.cs index f9b0f79..51372f6 100644 --- a/Casbin.UnitTests/Mock/MockLogger.cs +++ b/Casbin.UnitTests/Mock/MockLogger.cs @@ -1,5 +1,6 @@ #if !NET452 using System; +using System.Collections.Generic; using Microsoft.Extensions.Logging; using Xunit.Abstractions; @@ -11,11 +12,14 @@ public class MockLogger : ILogger public MockLogger(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper; + public List<(LogLevel Level, Exception Exception, string Message)> Logs { get; } = new(); + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - string outPut = formatter(state, null); + string outPut = formatter(state, exception); _testOutputHelper.WriteLine(outPut); + Logs.Add((logLevel, exception, outPut)); } public bool IsEnabled(LogLevel logLevel) => true; diff --git a/Casbin.UnitTests/ModelTests/EnforcerTest.cs b/Casbin.UnitTests/ModelTests/EnforcerTest.cs index 8a25eb5..578c2c6 100644 --- a/Casbin.UnitTests/ModelTests/EnforcerTest.cs +++ b/Casbin.UnitTests/ModelTests/EnforcerTest.cs @@ -1290,4 +1290,33 @@ public async Task TestEnforceExWithMatcherAsync() } #endregion + +#if !NET452 + #region ExpressionHandler Tests + + [Fact] + public void TestExpressionHandlerSingleQuoteReplacement() + { + Enforcer e = new(TestModelFixture.GetBasicTestModel()); + // Single quotes should be replaced with double quotes to handle DynamicExpresso limitations + string matcherWithSingleQuotes = "r.sub == 'alice' && r.obj == p.obj && r.act == p.act"; + + Assert.True(e.EnforceWithMatcher(matcherWithSingleQuotes, "alice", "data1", "read")); + Assert.False(e.EnforceWithMatcher(matcherWithSingleQuotes, "alice", "data1", "write")); + Assert.False(e.EnforceWithMatcher(matcherWithSingleQuotes, "bob", "data1", "read")); + } + + [Fact] + public void TestExpressionHandlerLogsWarningOnInvalidExpression() + { + var logger = new MockLogger(_testOutputHelper); + Enforcer e = new(TestModelFixture.GetBasicTestModel()) { Logger = logger }; + + // An invalid expression should return false and log a warning + Assert.False(e.EnforceWithMatcher("this_is_not_valid!!!", "alice", "data1", "read")); + Assert.Contains(logger.Logs, log => log.Level == Microsoft.Extensions.Logging.LogLevel.Warning); + } + + #endregion +#endif } diff --git a/Casbin/EnforceView.cs b/Casbin/EnforceView.cs index 74a1cce..0d5bb33 100644 --- a/Casbin/EnforceView.cs +++ b/Casbin/EnforceView.cs @@ -59,6 +59,7 @@ public static EnforceView CreateWithMatcher( string policyType = PermConstants.DefaultPolicyType, string effectType = PermConstants.DefaultPolicyEffectType) { + matcher = matcher.Replace('\'', '"'); IReadOnlyAssertion requestAssertion = model.Sections.GetRequestAssertion(requestType); PolicyAssertion policyAssertion = model.Sections.GetPolicyAssertion(policyType); IReadOnlyAssertion effectAssertion = model.Sections.GetPolicyEffectAssertion(effectType); diff --git a/Casbin/Enforcer.cs b/Casbin/Enforcer.cs index 4cadf7f..32ec800 100644 --- a/Casbin/Enforcer.cs +++ b/Casbin/Enforcer.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Casbin.Caching; using Casbin.Effect; +using Casbin.Evaluation; using Casbin.Model; using Casbin.Persist; using Casbin.Persist.Adapter.File; @@ -84,7 +85,22 @@ public IReadOnlyWatcher Watcher set => Model.WatcherHolder.Watcher = value; } - public IModel Model { get; set; } + private IModel _model; + + public IModel Model + { + get => _model; + set + { + _model = value; +#if !NET452 + if (value?.ExpressionHandler is ExpressionHandler exprHandler) + { + exprHandler.Logger = _logger; + } +#endif + } + } public IReadOnlyAdapter Adapter { @@ -99,7 +115,20 @@ public IEnforceCache EnforceCache } #if !NET452 - public ILogger Logger { get; set; } + private ILogger _logger; + + public ILogger Logger + { + get => _logger; + set + { + _logger = value; + if (_model?.ExpressionHandler is ExpressionHandler exprHandler) + { + exprHandler.Logger = value; + } + } + } #endif #endregion diff --git a/Casbin/Evaluation/ExpressionHandler.cs b/Casbin/Evaluation/ExpressionHandler.cs index 8a49982..37ed65d 100644 --- a/Casbin/Evaluation/ExpressionHandler.cs +++ b/Casbin/Evaluation/ExpressionHandler.cs @@ -5,6 +5,9 @@ using Casbin.Functions; using Casbin.Model; using DynamicExpresso; +#if !NET452 +using Microsoft.Extensions.Logging; +#endif namespace Casbin.Evaluation; @@ -20,6 +23,10 @@ internal class ExpressionHandler : IExpressionHandler private bool TryCompile { get; set; } = true; +#if !NET452 + internal ILogger Logger { get; set; } +#endif + public ExpressionHandler() { _interpreter = CreateInterpreter(); @@ -117,9 +124,12 @@ private bool TryCompileExpression(in EnforceContext context, { func = CompileExpression(in context, expressionString); } - catch (Exception) + catch (Exception e) { func = null; +#if !NET452 + Logger?.LogWarning(e, "Failed to compile the expression \"{ExpressionString}\".", expressionString); +#endif return false; } return true;