This is an automated email from the ASF dual-hosted git repository. hsluoyz pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/casbin-Casbin.NET.git
commit 0e8693ef9c6c7a341b85157436f938664d7880ad Author: Yang Luo <[email protected]> AuthorDate: Tue Jun 16 21:15:08 2026 +0800 feat: correct KeyMatch5 to support wildcards and path parameters --- Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs | 29 ++++++++++++++++++++++- Casbin/Functions/BuiltInFunctions.cs | 23 +++++++++++------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs b/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs index 7619ec2..a311a31 100644 --- a/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs +++ b/Casbin.UnitTests/UtilTests/BuiltInFunctionTest.cs @@ -159,7 +159,34 @@ public class BuiltInFunctionTest ["/parent?status=1&type=2", "/parent/child", false], ["/parent/child/?status=1&type=2", "/parent/child/", true], ["/parent/child/?status=1&type=2", "/parent/child", false], - ["/parent/child?status=1&type=2", "/parent/child/", false] + ["/parent/child?status=1&type=2", "/parent/child/", false], + // wildcard * + ["/foo", "/foo", true], + ["/foo", "/foo*", true], + ["/foo", "/foo/*", false], + ["/foo/bar", "/foo", false], + ["/foo/bar", "/foo*", false], + ["/foo/bar", "/foo/*", true], + ["/foobar", "/foo", false], + ["/foobar", "/foo*", false], + ["/foobar", "/foo/*", false], + // {param} placeholders + ["/", "/{resource}", false], + ["/resource1", "/{resource}", true], + ["/myid", "/{id}/using/{resId}", false], + ["/myid/using/myresid", "/{id}/using/{resId}", true], + // {param} + wildcard + ["/proxy/myid", "/proxy/{id}/*", false], + ["/proxy/myid/", "/proxy/{id}/*", true], + ["/proxy/myid/res", "/proxy/{id}/*", true], + ["/proxy/myid/res/res2", "/proxy/{id}/*", true], + ["/proxy/myid/res/res2/res3", "/proxy/{id}/*", true], + ["/proxy/", "/proxy/{id}/*", false], + // query strings with {param} + wildcard + ["/proxy/myid?status=1&type=2", "/proxy/{id}/*", false], + ["/proxy/myid/res?status=1&type=2", "/proxy/{id}/*", true], + ["/proxy/myid/res/res2?status=1&type=2", "/proxy/{id}/*", true], + ["/proxy/myid/res/res2/res3?status=1&type=2", "/proxy/{id}/*", true] ]; public static IEnumerable<object[]> GlobMatchTestData = diff --git a/Casbin/Functions/BuiltInFunctions.cs b/Casbin/Functions/BuiltInFunctions.cs index 69f3637..6a8720b 100644 --- a/Casbin/Functions/BuiltInFunctions.cs +++ b/Casbin/Functions/BuiltInFunctions.cs @@ -13,6 +13,7 @@ namespace Casbin.Functions private static readonly Regex s_keyMatch2Regex = new(@":[^/]+"); private static readonly Regex s_keyMatch3Regex = new(@"\{[^/]+\}"); private static readonly Regex s_keyMatch4Regex = new(@"\{([^/]+)\}"); + private static readonly Regex s_keyMatch5Regex = new(@"\{[^/]+\}"); private static readonly Regex s_keyGet2Regex = new(@":[^/]+"); private static readonly Regex s_keyGet3Regex = new(@"\{[^/]+?\}"); @@ -223,20 +224,26 @@ namespace Casbin.Functions } /// <summary> - /// KeyMatch determines whether key1 matches the pattern of key2 and ignores the parameters in key2. - /// For example, "/foo/bar?status=1&type=2" matches "/foo/bar" + /// KeyMatch5 determines whether key1 matches the pattern of key2 (similar to RESTful path), + /// key2 can contain a * or {param}. Query strings in key1 are ignored. + /// For example, "/foo/bar?status=1&type=2" matches "/foo/bar", "/foo/bar" matches "/foo/*", + /// "/proxy/myid/res" matches "/proxy/{id}/*". /// </summary> /// <param name="key1"> The first argument. </param> /// <param name="key2"> The second argument. </param> /// <returns></returns> public static bool KeyMatch5(string key1, string key2) { - var key1Span = key1.AsSpan(); - var key2Span = key2.AsSpan(); - int index = key1Span.IndexOf('?'); - return index is -1 - ? key1Span.Equals(key2Span, StringComparison.Ordinal) - : key1Span.Slice(0, index).Equals(key2Span, StringComparison.Ordinal); + int queryIndex = key1.IndexOf('?'); + if (queryIndex != -1) + { + key1 = key1.Substring(0, queryIndex); + } + + key2 = key2.Replace("/*", "/.*"); + key2 = s_keyMatch5Regex.Replace(key2, "([^/]+)"); + + return RegexMatch(key1, $"^{key2}$"); } /// <summary> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
