diff --git a/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs b/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs index 821817b..73a654b 100644 --- a/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs +++ b/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs @@ -32,6 +32,19 @@ public class BuiltInFunctionTest new object[] { "/topic/edit/123s", "/topic/delete/[0-9]+", false } }; + public static IEnumerable KeyGetTestData = new[] + { + new object[] { "/foo", "/foo", "" }, + new object[] { "/foo", "/foo*", "" }, + new object[] { "/foo", "/foo/*", "" }, + new object[] { "/foo/bar", "/foo", "" }, + new object[] { "/foo/bar", "/foo*", "/bar" }, + new object[] { "/foo/bar", "/foo/*", "bar" }, + new object[] { "/foobar", "/foo", "" }, + new object[] { "/foobar", "/foo*", "bar" }, + new object[] { "/foobar", "/foo/*", "" } + }; + public static IEnumerable keyMatchTestData = new[] { new object[] { "/foo", "/foo", true }, new object[] { "/foo", "/foo*", true }, @@ -140,6 +153,12 @@ public void TestRegexMatch(string key1, string key2, bool expectedResult) => Assert.Equal(expectedResult, BuiltInFunctions.RegexMatch(key1, key2)); + [Theory] + [MemberData(nameof(KeyGetTestData))] + public void TestKeyGet(string key1, string key2, string expectedResult) => + Assert.Equal(expectedResult, + BuiltInFunctions.KeyGet(key1, key2)); + [Theory] [MemberData(nameof(keyMatchTestData))] public void TestKeyMatch(string key1, string key2, bool expectedResult) => diff --git a/Casbin/Model/FunctionMap.cs b/Casbin/Model/FunctionMap.cs index af8513c..3bd23b9 100644 --- a/Casbin/Model/FunctionMap.cs +++ b/Casbin/Model/FunctionMap.cs @@ -20,6 +20,7 @@ internal static FunctionMap LoadFunctionMap() FunctionDict = new Dictionary() }; + map.AddFunction("keyGet", BuiltInFunctions.KeyGet); map.AddFunction("keyMatch", BuiltInFunctions.KeyMatch); map.AddFunction("keyMatch2", BuiltInFunctions.KeyMatch2); map.AddFunction("keyMatch3", BuiltInFunctions.KeyMatch3); diff --git a/Casbin/Util/BuiltInFunctions.cs b/Casbin/Util/BuiltInFunctions.cs index 55b0a13..bc84d86 100644 --- a/Casbin/Util/BuiltInFunctions.cs +++ b/Casbin/Util/BuiltInFunctions.cs @@ -15,6 +15,28 @@ public static class BuiltInFunctions private static readonly Regex s_keyMatch4Regex = new(@"\{([^/]+)\}"); private delegate bool GFunction(string subject1, string subject2, string domain = null); + /// + /// KeyGet returns the matched part + /// For example, "/foo/bar/foo" matches "/foo/*" + /// "bar/foo" will been returned + /// + /// The first argument. + /// The second argument. + /// Whether key1 matches key2. + public static string KeyGet(string key1, string key2) + { + int m = key1.Length, n = key2.Length; + if(m < n) return ""; + var span1 = key1.AsSpan(); + var span2 = key2.AsSpan(); + for (int i = 0; i < n; i++) + { + if(span1[i] != span2[i]) + return span2[i] == '*' ? span1.Slice(i, m - i).ToString() : ""; + } + return ""; + } + /// /// Determines whether key1 matches the pattern of key2 (similar to RESTful path), /// key2 can contain a *. For example, "/foo/bar" matches "/foo/*".