Skip to content

Commit 8d5500b

Browse files
committed
feat: support add generic type custom function
1 parent 6be2f80 commit 8d5500b

File tree

6 files changed

+106
-33
lines changed

6 files changed

+106
-33
lines changed

Casbin.UnitTests/Casbin.UnitTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<TargetFrameworks>net9.0;net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;net462;net461;net452</TargetFrameworks>
55
<DebugType>full</DebugType>
66
<IsPackable>false</IsPackable>
7-
<LangVersion>10.0</LangVersion>
7+
<LangVersion>latest</LangVersion>
88
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
99
</PropertyGroup>
1010

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using Casbin.Model;
3+
using Casbin.UnitTests.Util;
4+
using DynamicExpresso;
5+
using Xunit;
6+
7+
namespace Casbin.UnitTests.GenericTests;
8+
9+
public class GenericFunctionTest
10+
{
11+
[Fact]
12+
public void TestGenericFunction()
13+
{
14+
Interpreter interpreter = new();
15+
RequestValues<string, int> r = Request.CreateValues("A", 1);
16+
PolicyValues<string, int> p = Policy.CreateValues("A", 1);
17+
interpreter.SetFunction("equal", new Func<string, string, bool>(
18+
(a, b) => a == b)
19+
);
20+
interpreter.SetFunction("equal", new Func<int, int, bool>(
21+
(a, b) => a == b)
22+
);
23+
24+
Func<RequestValues<string, int>, PolicyValues<string, int>, bool> func1 =
25+
ExpressionUtil.Compile(interpreter, "equal(r.Value2, p.Value2) && equal(r.Value2, p.Value2)",
26+
nameof(r), in r, nameof(p), in p);
27+
28+
Assert.True(func1(Request.CreateValues("A", 1), Policy.CreateValues("A", 1)));
29+
Assert.False(func1(Request.CreateValues("A", 1), Policy.CreateValues("A", 2)));
30+
Assert.False(func1(Request.CreateValues("B", 1), Policy.CreateValues("B", 2)));
31+
}
32+
33+
[Fact]
34+
public void TestGenericFunctionModel()
35+
{
36+
Enforcer e = new Enforcer(DefaultModel.NewModelFromText(
37+
"""
38+
[request_definition]
39+
r = obj1, obj2
40+
41+
[policy_definition]
42+
p = _
43+
44+
[policy_effect]
45+
e = some(where (p.eft == allow))
46+
47+
[matchers]
48+
m = max(r.obj1, r.obj2) > 2
49+
"""));
50+
51+
e.AddFunction("max", new Func<int, int, int>(
52+
// ReSharper disable once ConvertClosureToMethodGroup
53+
(a, b) => Math.Max(a, b)
54+
));
55+
Assert.True(e.Enforce(1, 3));
56+
Assert.False(e.Enforce(1, 2));
57+
Assert.False(e.Enforce("1", "111"));
58+
59+
e.AddFunction("max", new Func<string, string, int>(
60+
(a, b) => Math.Max(a.Length, b.Length)
61+
));
62+
Assert.True(e.Enforce(1, 3));
63+
Assert.False(e.Enforce(1, 2));
64+
Assert.True(e.Enforce("1", "111"));
65+
}
66+
67+
}
Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using Casbin.Evaluation;
23
using Casbin.Model;
4+
using Casbin.UnitTests.Util;
35
using DynamicExpresso;
46
using Xunit;
57

@@ -10,37 +12,28 @@ public class GenericMatcherTest
1012
[Fact]
1113
public void TestGenericMatcher()
1214
{
15+
Interpreter interpreter = new();
1316
RequestValues<string, int> r = Request.CreateValues("A", 1);
1417
PolicyValues<string, int> p = Policy.CreateValues("A", 1);
15-
Func<RequestValues<string, int>, PolicyValues<string, int>, bool> func1 = Compile(
16-
"r.Value1 == p.Value1 && r.Value2 == p.Value2",
17-
nameof(r), in r, nameof(p), in p);
18+
Func<RequestValues<string, int>, PolicyValues<string, int>, bool> func1 =
19+
ExpressionUtil.Compile(interpreter, "r.Value1 == p.Value1 && r.Value2 == p.Value2",
20+
nameof(r), in r, nameof(p), in p);
1821

1922
Assert.True(func1(Request.CreateValues("A", 1), Policy.CreateValues("A", 1)));
2023
Assert.False(func1(Request.CreateValues("A", 1), Policy.CreateValues("A", 2)));
2124
Assert.False(func1(Request.CreateValues("B", 1), Policy.CreateValues("B", 2)));
2225

2326
RequestValues<string, int, string> r2 = Request.CreateValues("A", 1, "read");
2427
PolicyValues<string, int, string> p2 = Policy.CreateValues("A", 1, "read");
25-
Func<RequestValues<string, int, string>, PolicyValues<string, int, string>, bool> func2 = Compile(
26-
"r2.Value1 == p2.Value1 && r2.Value2 == p2.Value2 && r2.Value3 == p2.Value3",
27-
nameof(r2), in r2, nameof(p2), in p2);
28+
Func<RequestValues<string, int, string>, PolicyValues<string, int, string>, bool> func2 =
29+
ExpressionUtil.Compile(interpreter, "r2.Value1 == p2.Value1 && r2.Value2 == p2.Value2 && r2.Value3 == p2.Value3",
30+
nameof(r2), in r2, nameof(p2), in p2);
2831

2932
Assert.True(func2(Request.CreateValues("A", 1, "read"), Policy.CreateValues("A", 1, "read")));
3033
Assert.False(func2(Request.CreateValues("A", 1, "read"), Policy.CreateValues("A", 2, "read")));
3134
Assert.False(func2(Request.CreateValues("B", 1, "read"), Policy.CreateValues("B", 2, "read")));
3235
}
3336

34-
private static Func<TRequest, TPolicy, bool> Compile<TRequest, TPolicy>
35-
(
36-
string expressionText,
37-
string requestType, in TRequest r,
38-
string policyType, in TPolicy p)
39-
where TRequest : struct, IRequestValues where TPolicy : IPolicyValues
40-
{
41-
Interpreter interpreter = new();
42-
return interpreter.ParseAsDelegate<Func<TRequest, TPolicy, bool>>(
43-
expressionText, requestType, policyType
44-
);
45-
}
37+
38+
4639
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using Casbin.Model;
3+
using DynamicExpresso;
4+
5+
namespace Casbin.UnitTests.Util;
6+
7+
public static class ExpressionUtil
8+
{
9+
public static Func<TRequest, TPolicy, bool> Compile<TRequest, TPolicy>
10+
(
11+
Interpreter interpreter, string expressionText,
12+
string requestType, in TRequest r,
13+
string policyType, in TPolicy p)
14+
where TRequest : struct, IRequestValues where TPolicy : IPolicyValues
15+
{
16+
return interpreter.ParseAsDelegate<Func<TRequest, TPolicy, bool>>(
17+
expressionText, requestType, policyType
18+
);
19+
}
20+
}

Casbin/EnforceView.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,11 @@ public static string TransformMatcher(in EnforceView view, string matcher)
127127
Regex reg = new Regex(perfix + $@"{view.RequestType}\.{tokenPair.Key}" + suffix);
128128
matcher = reg.Replace(matcher, $"{view.RequestType}[{tokenPair.Value}]");
129129
}
130-
131130
foreach (KeyValuePair<string, int> tokenPair in view.PolicyAssertion.Tokens)
132131
{
133132
Regex reg = new Regex(perfix + $@"{view.PolicyType}\.{tokenPair.Key}" + suffix);
134133
matcher = reg.Replace(matcher, $"{view.PolicyType}[{tokenPair.Value}]");
135134
}
136-
137135
return matcher;
138136
}
139137

@@ -142,13 +140,11 @@ public static string TransformMatcher(in EnforceView view, string matcher)
142140
Regex reg = new Regex(perfix + $@"{view.RequestType}\.{tokenPair.Key}" + suffix);
143141
matcher = reg.Replace(matcher, $"{view.RequestType}.Value{tokenPair.Value + 1}");
144142
}
145-
146143
foreach (KeyValuePair<string, int> tokenPair in view.PolicyAssertion.Tokens)
147144
{
148145
Regex reg = new Regex(perfix + $@"{view.PolicyType}\.{tokenPair.Key}" + suffix);
149146
matcher = reg.Replace(matcher, $"{view.PolicyType}.Value{tokenPair.Value + 1}");
150147
}
151-
152148
return matcher;
153149
}
154150
}

Casbin/Extensions/Enforcer/ManagementEnforcerExtension.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,14 @@ public static class ManagementEnforcerExtension
1414
/// <param name="enforcer"></param>
1515
/// <param name="name">The name of the new function.</param>
1616
/// <param name="function">The function.</param>
17-
public static void AddFunction(this IEnforcer enforcer, string name, Delegate function) =>
17+
public static void AddFunction<T>(this IEnforcer enforcer, string name, T function) where T : Delegate
18+
{
1819
enforcer.Model.ExpressionHandler.SetFunction(name, function);
19-
20-
/// <summary>
21-
/// Adds a customized function.
22-
/// </summary>
23-
/// <param name="enforcer"></param>
24-
/// <param name="name">The name of the new function.</param>
25-
/// <param name="function">The function.</param>
26-
public static void AddFunction(this IEnforcer enforcer, string name, Func<string, string, bool> function) =>
27-
AddFunction(enforcer, name, (Delegate)function);
20+
if (enforcer.AutoCleanEnforceCache)
21+
{
22+
enforcer.ClearCache();
23+
}
24+
}
2825

2926
#region "p" (Store) Management
3027

0 commit comments

Comments
 (0)