Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Feature request: add the `keyMatch5` built-in matcher function

Abierto
#275 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
Medio día
Aptitud para principiantes
74/100
Tipo de issue
Nueva funcionalidad
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
cpp

Línea de trabajo

Start at casbin/model/evaluator.cpp LoadFunctions; follow KeyMatch4 through include/casbin/model/exprtk_config.h, casbin/util/built_in_functions.cpp and include/casbin/util/built_in_functions.h. Add KeyMatch5 by stripping key1 at the first ?, then using KeyMatch3-style wildcard/placeholder matching, and register it in the factory and LoadFunctions. Add a TestKeyMatch5 beside TestKeyMatch4 in tests/built_in_functions_test.cpp; run the gtest suite, and done is the new matcher registered as keyMatch5 with the test passing.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Location (apache/casbin-cpp @ default branch 0ad64a7):

void ExprtkEvaluator::LoadFunctions() {
    AddFunction("keyMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch, 2));
    AddFunction("keyMatch2", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch2, 2));
    AddFunction("keyMatch3", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch3, 2));
    AddFunction("keyMatch4", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch4, 2));
    AddFunction("regexMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::RegexMatch, 2));
    AddFunction("ipMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::IpMatch, 2));
    AddFunction("keyGet", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyGet, 2));
    AddFunction("keyGet2", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyGet2, 3));
    AddFunction("keyGet3", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyGet3, 3));
}

Problem
Casbin's function reference lists keyMatch5: arg1 is "a URL path like /alice_data/123/?status=1", arg2 is "a URL path, a {} or * pattern like /alice_data/{id}/*". The query string of the request path is ignored when matching. casbin-cpp registers keyMatch … keyMatch4, regexMatch, ipMatch and keyGet* as built-ins, but not keyMatch5, so a model that uses it in other Casbin implementations, e.g.

[matchers]
m = r.sub == p.sub && keyMatch5(r.obj, p.obj) && r.act == p.act

with p, alice, /parent/child, GET, can't be used unchanged with casbin-cpp. Users have to strip the query string themselves or register their own function (AddFunction) in each application.

This is a feature request, not a bug: an unknown function is already an error, not an allow.

Reproduction
With the matcher above, the first Enforce call throws, and gtest reports:

unknown file: Failure
Unknown C++ exception thrown in the test body.

Environment: master 0ad64a7.

Already implemented in apache/casbin-node-casbin
PR: https://github.com/apache/casbin-node-casbin/pull/359 — "feat: #357 Support keyMatch5" (merged 2022-04-15T01:43:23Z, merge commit e6a6d8ab253). It adds keyMatch5 to node-casbin's built-in function map, matching after dropping the query string from the request path. node-casbin's version compares the remaining path exactly; the reference implementation (apache/casbin KeyMatch5) additionally supports {name} and /* in the pattern, which is the behaviour the docs describe.

How node-casbin implemented it:

--- a/src/model/functionMap.ts
+++ b/src/model/functionMap.ts
@@ -37,6 +37,7 @@ export class FunctionMap {
     fm.addFunction('keyGet2', util.keyGet2Func);
     fm.addFunction('keyMatch3', util.keyMatch3Func);
     fm.addFunction('keyMatch4', util.keyMatch4Func);
+    fm.addFunction('keyMatch5', util.keyMatch5Func);
     fm.addFunction('regexMatch', util.regexMatchFunc);
     fm.addFunction('ipMatch', util.ipMatchFunc);
     fm.addFunction('globMatch', util.globMatch);
--- a/src/util/builtinOperators.ts
+++ b/src/util/builtinOperators.ts
@@ -233,6 +233,26 @@ function keyMatch4Func(...args: any[]): boolean {
   return keyMatch4(name1, name2);
 }
 
+// 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"
+function KeyMatch5(key1: string, key2: string): boolean {
+  const i: number = key1.indexOf('?');
+  if (i === -1) {
+    return key1 === key2;
+  }
+
+  return key1.slice(0, i) === key2;
+}
+
+// keyMatch5Func is the wrapper for KeyMatch5.
+function keyMatch5Func(...args: any[]): boolean {
+  const [arg0, arg1] = args;
+  const name1: string = (arg0 || '').toString();
+  const name2: string = (arg1 || '').toString();
+
+  return KeyMatch5(name1, name2);
+}
+
 // regexMatchFunc is the wrapper for regexMatch.
 function regexMatchFunc(...args: any[]): boolean {
   const [arg0, arg1] = args;

Suggested fix
Add KeyMatch5 next to KeyMatch4 (drop everything from the first ? in key1, then match like keyMatch3: /* is a wildcard and {name} matches one path segment), add it to the exprtk function factory, and register it as keyMatch5 in LoadFunctions:

diff --git a/casbin/model/evaluator.cpp b/casbin/model/evaluator.cpp
--- a/casbin/model/evaluator.cpp
+++ b/casbin/model/evaluator.cpp
@@ -83,6 +83,7 @@ void ExprtkEvaluator::LoadFunctions() {
     AddFunction("keyMatch2", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch2, 2));
     AddFunction("keyMatch3", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch3, 2));
     AddFunction("keyMatch4", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch4, 2));
+    AddFunction("keyMatch5", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch5, 2));
     AddFunction("regexMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::RegexMatch, 2));
     AddFunction("ipMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::IpMatch, 2));
     AddFunction("keyGet", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyGet, 2));
diff --git a/casbin/util/built_in_functions.cpp b/casbin/util/built_in_functions.cpp
--- a/casbin/util/built_in_functions.cpp
+++ b/casbin/util/built_in_functions.cpp
@@ -204,6 +204,18 @@ bool KeyMatch4(const std::string& key1, const std::string& key2) {
     return true;
 }
 
+// KeyMatch5 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
+// The query string of key1 (everything from the first "?") is ignored.
+// For example, "/foo/bar?status=1&type=2" matches "/foo/bar", "/parent/child1" matches "/parent/*" and "/parent/{id}"
+bool KeyMatch5(const std::string& key1, const std::string& key2) {
+    std::string k1 = key1.substr(0, key1.find('?'));
+    std::string k2 = PrepareWildCardMatching(key2);
+    k2 = std::regex_replace(k2, enclosedPlaceHolderRegex, "$1[^/]+$2");
+    k2 = EscapeCurlyBraces(k2);
+
+    return RegexMatch(k1, "^" + k2 + "$");
+}
+
 // RegexMatch determines whether key1 matches the pattern of key2 in regular expression.
 bool RegexMatch(const std::string& key1, const std::string& key2) {
     std::regex regex_s(key2);
diff --git a/include/casbin/model/exprtk_config.h b/include/casbin/model/exprtk_config.h
--- a/include/casbin/model/exprtk_config.h
+++ b/include/casbin/model/exprtk_config.h
@@ -234,6 +234,7 @@ enum class ExprtkFunctionType {
     KeyMatch2,
     KeyMatch3,
     KeyMatch4,
+    KeyMatch5,
     RegexMatch,
     IpMatch,
     KeyGet,
@@ -276,6 +277,9 @@ public:
             case ExprtkFunctionType::KeyMatch4:
                 func = std::make_shared<ExprtkMatchFunction>(idenfier, KeyMatch4);
                 break;
+            case ExprtkFunctionType::KeyMatch5:
+                func = std::make_shared<ExprtkMatchFunction>(idenfier, KeyMatch5);
+                break;
             case ExprtkFunctionType::IpMatch:
                 func = std::make_shared<ExprtkMatchFunction>(idenfier, IPMatch);
                 break;
diff --git a/include/casbin/util/built_in_functions.h b/include/casbin/util/built_in_functions.h
--- a/include/casbin/util/built_in_functions.h
+++ b/include/casbin/util/built_in_functions.h
@@ -53,6 +53,11 @@ std::string KeyGet3(const std::string& key1, const std::string& key2, const std:
 // But KeyMatch3 will match both.
 bool KeyMatch4(const std::string& key1, const std::string& key2);
 
+// KeyMatch5 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
+// The query string of key1 (everything from the first "?") is ignored.
+// For example, "/foo/bar?status=1&type=2" matches "/foo/bar", "/parent/child1" matches "/parent/*" and "/parent/{id}"
+bool KeyMatch5(const std::string& key1, const std::string& key2);
+
 // RegexMatch determines whether key1 matches the pattern of key2 in regular expression.
 bool RegexMatch(const std::string& key1, const std::string& key2);
 

Verified on 0ad64a7 (GCC 11.4.0, GoogleTest 1.14.0): with the example above added as a TestKeyMatch5 unit test in tests/built_in_functions_test.cpp (via the exprtk evaluator, like TestKeyMatch4), the test does not compile without this patch and passes with it; no other test results change.

I can submit a PR for this.

Related: no issue/PR mentions keyMatch5; #209 / #212 added the then-missing keyGet* / keyMatch4 built-ins in the same way.

Lenguaje dominante
C++
Estrellas
257
Forks
67
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de apache/casbin-cpp

Todos los issues de apache/casbin-cpp

Issues similares

Más issues de C++

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.