Repository: casbin/casbin
Pull request #1177 (2023-01-19 13:19:37)

fix: add support to use '*' in keymatch5 utils
Fix: https://github.com/casbin/casbin/issues/1176

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/casbin/casbin?pullRequest=1177) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/casbin/casbin?pullRequest=1177) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/casbin/casbin?pullRequest=1177) it.</sub>
hsluoyz: @PokIsemaine plz review
PokIsemaine: @MkDierz Thanks for your contribution, the basic implementation and testing are fine, it would be even better if you can help improve the comments of KeyMatch5.
``` golang
// 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"
func KeyMatch5(key1 string, key2 string) bool {
}
```
For example：
* fix `KeyMatch` -> `KeyMatch5`
* add more examples
  * You can choose some examples from your test
  * Add an example of matching failure

You can refer to the comments of KeyMatch4
```golang
// KeyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
// Besides what KeyMatch3 does, KeyMatch4 can also match repeated patterns:
// "/parent/123/child/123" matches "/parent/{id}/child/{id}"
// "/parent/123/child/456" does not match "/parent/{id}/child/{id}"
// But KeyMatch3 will match both.
```

MkDierz: Hi thanks for the review, ill do it ASAP.
PokIsemaine: @MkDierz You can try to use `push -f` to remove commits that do not meet the requirements
![image](https://user-images.githubusercontent.com/46661603/217172798-4508bb2f-eb78-45f1-8182-9f51d17e3bc3.png)


Patch:
diff --git a/util/builtin_operators.go b/util/builtin_operators.go
index ee091ce..878a4e1 100644
--- a/util/builtin_operators.go
+++ b/util/builtin_operators.go
@@ -272,15 +272,24 @@ func KeyMatch4Func(args ...interface{}) (interface{}, error) {
 	return bool(KeyMatch4(name1, name2)), nil
 }
 
-// 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 *
+// For example, 
+// - "/foo/bar?status=1&type=2" matches "/foo/bar"
+// - "/parent/child1" and "/parent/child1" matches "/parent/*"
+// - "/parent/child1?status=1" matches "/parent/*"
 func KeyMatch5(key1 string, key2 string) bool {
 	i := strings.Index(key1, "?")
-	if i == -1 {
-		return key1 == key2
+
+	if i != -1 {
+		key1 = key1[:i]
 	}
 
-	return key1[:i] == key2
+	key2 = strings.Replace(key2, "/*", "/.*", -1)
+
+	re := regexp.MustCompile(`\{[^/]+\}`)
+	key2 = re.ReplaceAllString(key2, "$1[^/]+$2")
+
+	return RegexMatch(key1, "^"+key2+"$")
 }
 
 // KeyMatch5Func is the wrapper for KeyMatch5.


Test patch:
diff --git a/util/builtin_operators_test.go b/util/builtin_operators_test.go
index 5ef3fa8..22ae2e7 100644
--- a/util/builtin_operators_test.go
+++ b/util/builtin_operators_test.go
@@ -494,6 +494,35 @@ func TestKeyMatch5Func(t *testing.T) {
 	testKeyMatch5Func(t, false, "", "/parent/child/?status=1&type=2", "/parent/child")
 	testKeyMatch5Func(t, false, "", "/parent/child?status=1&type=2", "/parent/child/")
 
+	testKeyMatch5Func(t, true, "", "/foo", "/foo")
+	testKeyMatch5Func(t, true, "", "/foo", "/foo*")
+	testKeyMatch5Func(t, false, "", "/foo", "/foo/*")
+	testKeyMatch5Func(t, false, "", "/foo/bar", "/foo")
+	testKeyMatch5Func(t, false, "", "/foo/bar", "/foo*")
+	testKeyMatch5Func(t, true, "", "/foo/bar", "/foo/*")
+	testKeyMatch5Func(t, false, "", "/foobar", "/foo")
+	testKeyMatch5Func(t, false, "", "/foobar", "/foo*")
+	testKeyMatch5Func(t, false, "", "/foobar", "/foo/*")
+
+	testKeyMatch5Func(t, false, "", "/", "/{resource}")
+	testKeyMatch5Func(t, true, "", "/resource1", "/{resource}")
+	testKeyMatch5Func(t, false, "", "/myid", "/{id}/using/{resId}")
+	testKeyMatch5Func(t, true, "", "/myid/using/myresid", "/{id}/using/{resId}")
+
+	testKeyMatch5Func(t, false, "", "/proxy/myid", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2/res3", "/proxy/{id}/*")
+	testKeyMatch5Func(t, false, "", "/proxy/", "/proxy/{id}/*")
+
+	testKeyMatch5Func(t, false, "", "/proxy/myid?status=1&type=2", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res?status=1&type=2", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2?status=1&type=2", "/proxy/{id}/*")
+	testKeyMatch5Func(t, true, "", "/proxy/myid/res/res2/res3?status=1&type=2", "/proxy/{id}/*")
+	testKeyMatch5Func(t, false, "", "/proxy/", "/proxy/{id}/*")
+
 }
 
 func TestIPMatchFunc(t *testing.T) {