Skip to content

Commit f6fa6e4

Browse files
Copilothsluoyzsagilio
authored
feat: log expression compilation errors and replace single quotes in ExpressionHandler (#397)
* Initial plan * Log expression compilation errors and replace single quotes in expressions Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> * Propagate logger via property setters instead of per-call in InternalEnforce Co-authored-by: sagilio <42855245+sagilio@users.noreply.github.com> * fix: pass exception to log formatter in MockLogger Co-authored-by: sagilio <42855245+sagilio@users.noreply.github.com> * refactor: move single-quote normalization to EnforceView.CreateWithMatcher Co-authored-by: sagilio <42855245+sagilio@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hsluoyz <3787410+hsluoyz@users.noreply.github.com> Co-authored-by: sagilio <42855245+sagilio@users.noreply.github.com>
1 parent 6acec87 commit f6fa6e4

File tree

5 files changed

+77
-4
lines changed

5 files changed

+77
-4
lines changed

Casbin.UnitTests/Mock/MockLogger.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if !NET452
22
using System;
3+
using System.Collections.Generic;
34
using Microsoft.Extensions.Logging;
45
using Xunit.Abstractions;
56

@@ -11,11 +12,14 @@ public class MockLogger<T> : ILogger<T>
1112

1213
public MockLogger(ITestOutputHelper testOutputHelper) => _testOutputHelper = testOutputHelper;
1314

15+
public List<(LogLevel Level, Exception Exception, string Message)> Logs { get; } = new();
16+
1417
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
1518
Func<TState, Exception, string> formatter)
1619
{
17-
string outPut = formatter(state, null);
20+
string outPut = formatter(state, exception);
1821
_testOutputHelper.WriteLine(outPut);
22+
Logs.Add((logLevel, exception, outPut));
1923
}
2024

2125
public bool IsEnabled(LogLevel logLevel) => true;

Casbin.UnitTests/ModelTests/EnforcerTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,4 +1290,33 @@ public async Task TestEnforceExWithMatcherAsync()
12901290
}
12911291

12921292
#endregion
1293+
1294+
#if !NET452
1295+
#region ExpressionHandler Tests
1296+
1297+
[Fact]
1298+
public void TestExpressionHandlerSingleQuoteReplacement()
1299+
{
1300+
Enforcer e = new(TestModelFixture.GetBasicTestModel());
1301+
// Single quotes should be replaced with double quotes to handle DynamicExpresso limitations
1302+
string matcherWithSingleQuotes = "r.sub == 'alice' && r.obj == p.obj && r.act == p.act";
1303+
1304+
Assert.True(e.EnforceWithMatcher(matcherWithSingleQuotes, "alice", "data1", "read"));
1305+
Assert.False(e.EnforceWithMatcher(matcherWithSingleQuotes, "alice", "data1", "write"));
1306+
Assert.False(e.EnforceWithMatcher(matcherWithSingleQuotes, "bob", "data1", "read"));
1307+
}
1308+
1309+
[Fact]
1310+
public void TestExpressionHandlerLogsWarningOnInvalidExpression()
1311+
{
1312+
var logger = new MockLogger<Enforcer>(_testOutputHelper);
1313+
Enforcer e = new(TestModelFixture.GetBasicTestModel()) { Logger = logger };
1314+
1315+
// An invalid expression should return false and log a warning
1316+
Assert.False(e.EnforceWithMatcher("this_is_not_valid!!!", "alice", "data1", "read"));
1317+
Assert.Contains(logger.Logs, log => log.Level == Microsoft.Extensions.Logging.LogLevel.Warning);
1318+
}
1319+
1320+
#endregion
1321+
#endif
12931322
}

Casbin/EnforceView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public static EnforceView CreateWithMatcher(
5959
string policyType = PermConstants.DefaultPolicyType,
6060
string effectType = PermConstants.DefaultPolicyEffectType)
6161
{
62+
matcher = matcher.Replace('\'', '"');
6263
IReadOnlyAssertion requestAssertion = model.Sections.GetRequestAssertion(requestType);
6364
PolicyAssertion policyAssertion = model.Sections.GetPolicyAssertion(policyType);
6465
IReadOnlyAssertion effectAssertion = model.Sections.GetPolicyEffectAssertion(effectType);

Casbin/Enforcer.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Threading.Tasks;
33
using Casbin.Caching;
44
using Casbin.Effect;
5+
using Casbin.Evaluation;
56
using Casbin.Model;
67
using Casbin.Persist;
78
using Casbin.Persist.Adapter.File;
@@ -84,7 +85,22 @@ public IReadOnlyWatcher Watcher
8485
set => Model.WatcherHolder.Watcher = value;
8586
}
8687

87-
public IModel Model { get; set; }
88+
private IModel _model;
89+
90+
public IModel Model
91+
{
92+
get => _model;
93+
set
94+
{
95+
_model = value;
96+
#if !NET452
97+
if (value?.ExpressionHandler is ExpressionHandler exprHandler)
98+
{
99+
exprHandler.Logger = _logger;
100+
}
101+
#endif
102+
}
103+
}
88104

89105
public IReadOnlyAdapter Adapter
90106
{
@@ -99,7 +115,20 @@ public IEnforceCache EnforceCache
99115
}
100116

101117
#if !NET452
102-
public ILogger Logger { get; set; }
118+
private ILogger _logger;
119+
120+
public ILogger Logger
121+
{
122+
get => _logger;
123+
set
124+
{
125+
_logger = value;
126+
if (_model?.ExpressionHandler is ExpressionHandler exprHandler)
127+
{
128+
exprHandler.Logger = value;
129+
}
130+
}
131+
}
103132
#endif
104133

105134
#endregion

Casbin/Evaluation/ExpressionHandler.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
using Casbin.Functions;
66
using Casbin.Model;
77
using DynamicExpresso;
8+
#if !NET452
9+
using Microsoft.Extensions.Logging;
10+
#endif
811

912
namespace Casbin.Evaluation;
1013

@@ -20,6 +23,10 @@ internal class ExpressionHandler : IExpressionHandler
2023

2124
private bool TryCompile { get; set; } = true;
2225

26+
#if !NET452
27+
internal ILogger Logger { get; set; }
28+
#endif
29+
2330
public ExpressionHandler()
2431
{
2532
_interpreter = CreateInterpreter();
@@ -117,9 +124,12 @@ private bool TryCompileExpression<TRequest, TPolicy>(in EnforceContext context,
117124
{
118125
func = CompileExpression<TRequest, TPolicy>(in context, expressionString);
119126
}
120-
catch (Exception)
127+
catch (Exception e)
121128
{
122129
func = null;
130+
#if !NET452
131+
Logger?.LogWarning(e, "Failed to compile the expression \"{ExpressionString}\".", expressionString);
132+
#endif
123133
return false;
124134
}
125135
return true;

0 commit comments

Comments
 (0)