diff --git a/Casbin.UnitTests/Fixtures/TestModelFixture.cs b/Casbin.UnitTests/Fixtures/TestModelFixture.cs index 6c9e66a..6018d10 100644 --- a/Casbin.UnitTests/Fixtures/TestModelFixture.cs +++ b/Casbin.UnitTests/Fixtures/TestModelFixture.cs @@ -95,6 +95,10 @@ public class TestModelFixture public static readonly string RbacWithIndexMatcherModelText = ReadTestFile("rbac_with_index_matcher_model.conf"); public static readonly string RbacWithIndexMatcherPolicyText = ReadTestFile("rbac_with_index_matcher_policy.csv"); + // https://github.com/casbin/Casbin.NET/issues/354 + public static readonly string AbacWithDynamicValueTypeModelText = ReadTestFile("abac_with_dynamic_value_type_model.conf"); + public static readonly string AbacWithDynamicValueTypePolicyText = ReadTestFile("abac_with_dynamic_value_type_policy.csv"); + public static IModel GetNewAbacModel() => GetNewTestModel(AbacModelText); public static IModel GetNewAbacWithEvalModel() => GetNewTestModel(AbacWithEvalModelText, AbacWithEvalPolicyText); diff --git a/Casbin.UnitTests/ModelTests/ModelTest.cs b/Casbin.UnitTests/ModelTests/ModelTest.cs index 7ff6c68..f77c9fd 100644 --- a/Casbin.UnitTests/ModelTests/ModelTest.cs +++ b/Casbin.UnitTests/ModelTests/ModelTest.cs @@ -752,6 +752,25 @@ public void TestRbacWithIndexMatcher() Assert.True(e.Enforce(rule, "Admin", "/api/transactions/getTransactions", "POST")); } + [Fact] + public void TestAbacWithDynamicValueType() + { + Enforcer e = new(TestModelFixture.GetNewTestModel( + TestModelFixture.AbacWithDynamicValueTypeModelText, + TestModelFixture.AbacWithDynamicValueTypePolicyText)); + var sub = new { Name = "bob" }; + var obj1 = new { Object = "/data1", Property1 = "prop-1" }; + var obj2 = new { Object = "/data2", Property2 = "prop-2" }; + var obj3 = new { Object = "/data2", Property3 = "prop-3" }; + Assert.True(e.Enforce(sub, obj1, "read")); + Assert.True(e.Enforce(sub, obj2, "read")); + Assert.False(e.Enforce(sub, obj3, "read")); + // Request again to test the cache hit logic. + Assert.True(e.Enforce(sub, obj1, "read")); + Assert.True(e.Enforce(sub, obj2, "read")); + Assert.False(e.Enforce(sub, obj3, "read")); + } + public class TestResource { public TestResource(string name, string owner) diff --git a/Casbin.UnitTests/examples/abac_with_dynamic_value_type_model.conf b/Casbin.UnitTests/examples/abac_with_dynamic_value_type_model.conf new file mode 100644 index 0000000..7e2a5b7 --- /dev/null +++ b/Casbin.UnitTests/examples/abac_with_dynamic_value_type_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act, rule + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = r.sub.Name == p.sub && \ + r.obj.Object == p.obj && \ + r.act == p.act && \ + eval(p.rule) diff --git a/Casbin.UnitTests/examples/abac_with_dynamic_value_type_policy.csv b/Casbin.UnitTests/examples/abac_with_dynamic_value_type_policy.csv new file mode 100644 index 0000000..b341671 --- /dev/null +++ b/Casbin.UnitTests/examples/abac_with_dynamic_value_type_policy.csv @@ -0,0 +1,2 @@ +p, bob, /data1, read, r.obj.Property1 == "prop-1" +p, bob, /data2, read, r.obj.Property2 == "prop-2" diff --git a/Casbin/Evaluation/ExpressionHandler.cs b/Casbin/Evaluation/ExpressionHandler.cs index 3206eb1..8a49982 100644 --- a/Casbin/Evaluation/ExpressionHandler.cs +++ b/Casbin/Evaluation/ExpressionHandler.cs @@ -18,6 +18,8 @@ internal class ExpressionHandler : IExpressionHandler private Interpreter _interpreter; #endif + private bool TryCompile { get; set; } = true; + public ExpressionHandler() { _interpreter = CreateInterpreter(); @@ -76,13 +78,23 @@ public bool Invoke(in EnforceContext context, string expressi return func(request, policy); } - if (_cachePool.TryGetFunc(expressionString, - out Func genericFunc) is not false) + if (_cachePool.TryGetFunc(expressionString, out Func genericFunc)) + { + return genericFunc is not null && genericFunc(request, policy); + } + + if (TryCompile is false) { + genericFunc = CompileExpression(in context, expressionString); + _cachePool.SetFunc(expressionString, genericFunc); return genericFunc(request, policy); } - genericFunc = CompileExpression(in context, expressionString); + if (TryCompileExpression(in context, expressionString, out genericFunc) is false) + { + _cachePool.SetFunc(expressionString, genericFunc); + return false; + } _cachePool.SetFunc(expressionString, genericFunc); return genericFunc(request, policy); } @@ -96,6 +108,23 @@ private Func CompileExpression(in En context.View.RequestType, context.View.PolicyType); } + private bool TryCompileExpression(in EnforceContext context, + string expressionString, out Func func) + where TRequest : IRequestValues + where TPolicy : IPolicyValues + { + try + { + func = CompileExpression(in context, expressionString); + } + catch (Exception) + { + func = null; + return false; + } + return true; + } + private Interpreter CreateInterpreter() { var interpreter = new Interpreter();