Skip to content

Commit 7df0d46

Browse files
authored
fix: add KeyGet for built_in_functions (#212)
1 parent bb228e5 commit 7df0d46

File tree

5 files changed

+279
-150
lines changed

5 files changed

+279
-150
lines changed

casbin/model/evaluator.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ void ExprtkEvaluator::LoadFunctions() {
5454
AddFunction("keyMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch, 2));
5555
AddFunction("keyMatch2", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch2, 2));
5656
AddFunction("keyMatch3", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch3, 2));
57+
AddFunction("keyMatch4", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch4, 2));
5758
AddFunction("regexMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::RegexMatch, 2));
5859
AddFunction("ipMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::IpMatch, 2));
5960
}

casbin/util/built_in_functions.cpp

Lines changed: 108 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -48,96 +48,103 @@ bool KeyMatch(const std::string& key1, const std::string& key2) {
4848
return key1 == key2.substr(0, pos);
4949
}
5050

51+
// KeyGet returns the matched part
52+
// For example, "/foo/bar/foo" matches "/foo/*"
53+
// "bar/foo" will been returned
54+
std::string KeyGet(const std::string& key1, const std::string& key2) {
55+
size_t pos = key2.find("*");
56+
57+
if (pos == std::string ::npos)
58+
return "";
59+
60+
if (key1.length() > pos)
61+
if (key1.substr(0, pos) == key2.substr(0, pos))
62+
return key1.substr(pos, key1.length() - pos);
63+
64+
return "";
65+
}
66+
5167
// KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
5268
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource"
5369
bool KeyMatch2(const std::string& key1, const std::string& key2) {
54-
std::vector<std::string> key1_arr = Split(key1, "/");
55-
std::vector<std::string> key2_arr = Split(key2, "/");
56-
57-
bool res = true;
58-
for (int i = 0; i < key2_arr.size(); i++) {
59-
if (i >= key1_arr.size()) {
60-
res = false;
61-
break;
62-
}
63-
if (key1_arr[i] != key2_arr[i]) {
64-
size_t index1 = key2_arr[i].find("*");
65-
size_t index2 = key2_arr[i].find(":");
66-
if (index1 != std::string::npos) {
67-
if (index1 == 0) {
68-
res = true;
69-
break;
70-
} else if (key1_arr[i].compare(key2_arr[i].substr(0, index1))) {
71-
res = false;
72-
break;
73-
} else
74-
continue;
75-
}
76-
if (index2 == 0) {
77-
if (key1_arr[i] == "" || !key2_arr[i].substr(1).compare("")) {
78-
res = false;
79-
break;
80-
} else
81-
continue;
82-
}
83-
res = false;
84-
break;
85-
} else
86-
continue;
70+
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
71+
k2 = regex_replace(k2, std::regex("(.*?):[^/]+(.*?)"), "$1[^/]+$2");
72+
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
73+
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
74+
75+
if (!k2.compare("*"))
76+
k2 = "(.*)";
77+
78+
return RegexMatch(key1, "^" + k2 + "$");
79+
}
80+
81+
// KeyGet2 returns value matched pattern
82+
// For example, "/resource1" matches "/:resource"
83+
// if the path_var == "resource", then "resource1" will be returned
84+
std::string KeyGet2(const std::string& key1, const std::string& key2, const std::string& path_var) {
85+
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
86+
87+
std::vector<std::string> keys;
88+
std::regex keys_regex(":[^/]+");
89+
for (std::sregex_iterator it(k2.begin(), k2.end(), keys_regex), end_it; it != end_it; ++it) {
90+
keys.push_back(it->str());
8791
}
8892

89-
if (key2_arr.size() < key1_arr.size())
90-
if (key2_arr[key2_arr.size() - 1] != "*")
91-
res = false;
93+
k2 = regex_replace(k2, std::regex("(.*?):[^/]+(.*?)"), "$1([^/]+)$2");
94+
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
95+
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
96+
if (!k2.compare("*"))
97+
k2 = "(.*)";
98+
k2 = "^" + k2 + "$";
99+
100+
std::smatch values;
101+
std::regex_match(key1.begin(), key1.end(), values, std::regex(k2));
92102

93-
return res;
103+
for (int i = 0; i < keys.size(); i++)
104+
if (!path_var.compare(keys.at(i).substr(1)))
105+
return values[i + 1];
106+
107+
return "";
94108
}
95109

96110
// KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
97111
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}"
98-
99112
bool KeyMatch3(const std::string& key1, const std::string& key2) {
100-
std::vector<std::string> key1_arr = Split(key1, "/");
101-
std::vector<std::string> key2_arr = Split(key2, "/");
102-
103-
bool res = true;
104-
for (int i = 0; i < key2_arr.size(); i++) {
105-
if (i >= key1_arr.size()) {
106-
res = false;
107-
break;
108-
}
109-
if (key1_arr[i] != key2_arr[i]) {
110-
size_t index1 = key2_arr[i].find("*");
111-
size_t index2 = key2_arr[i].find("{");
112-
size_t index3 = key2_arr[i].find("}");
113-
if (index1 != std::string::npos) {
114-
if (index1 == 0) {
115-
res = true;
116-
break;
117-
} else if (key1_arr[i].compare(key2_arr[i].substr(0, index1))) {
118-
res = false;
119-
break;
120-
} else
121-
continue;
122-
}
123-
if (index2 == 0 && index3 > 0 && index3 != std::string::npos) {
124-
if (key1_arr[i] == "" || !key2_arr[i].substr(1, key2_arr[i].length() - 2).compare("")) {
125-
res = false;
126-
break;
127-
} else
128-
continue;
129-
}
130-
res = false;
131-
break;
132-
} else
133-
continue;
113+
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
114+
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1[^/]+$2");
115+
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
116+
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
117+
118+
return RegexMatch(key1, "^" + k2 + "$");
119+
}
120+
121+
// KeyGet3 returns value matched pattern
122+
// For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/"
123+
// if the pathVar == "project", then "project1" will be returned
124+
std::string KeyGet3(const std::string& key1, const std::string& key2, const std::string& path_var) {
125+
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
126+
127+
std::vector<std::string> keys;
128+
std::regex keys_regex("\\{[^/]+?\\}");
129+
for (std::sregex_iterator it(k2.begin(), k2.end(), keys_regex), end_it; it != end_it; ++it) {
130+
keys.push_back(it->str());
134131
}
135132

136-
if (key2_arr.size() < key1_arr.size())
137-
if (key2_arr[key2_arr.size() - 1] != "*")
138-
res = false;
133+
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1([^/]+?)$2");
134+
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
135+
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
136+
if (!k2.compare("*"))
137+
k2 = "(.*)";
138+
k2 = "^" + k2 + "$";
139139

140-
return res;
140+
std::smatch values;
141+
std::regex_match(key1.begin(), key1.end(), values, std::regex(k2));
142+
143+
for (int i = 0; i < keys.size(); i++)
144+
if (!path_var.compare(keys.at(i).substr(1, keys.at(i).length() - 2)))
145+
return values[i + 1];
146+
147+
return "";
141148
}
142149

143150
// KeyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
@@ -146,56 +153,35 @@ bool KeyMatch3(const std::string& key1, const std::string& key2) {
146153
// "/parent/123/child/456" does not match "/parent/{id}/child/{id}"
147154
// But KeyMatch3 will match both.
148155
bool KeyMatch4(const std::string& key1, const std::string& key2) {
149-
std::vector<std::string> key1_arr = Split(key1, "/");
150-
std::vector<std::string> key2_arr = Split(key2, "/");
156+
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
157+
158+
std::vector<std::string> tokens;
159+
std::regex tokens_regex("\\{([^/]+)\\}");
160+
for (std::sregex_iterator it(k2.begin(), k2.end(), tokens_regex), end_it; it != end_it; ++it)
161+
tokens.push_back(it->str());
162+
163+
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1([^/]+)$2");
164+
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
165+
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
166+
k2 = "^" + k2 + "$";
167+
std::smatch matches;
168+
std::regex_match(key1.begin(), key1.end(), matches, std::regex(k2));
169+
if (matches.empty())
170+
return false;
171+
if (tokens.size() != matches.size() - 1)
172+
throw "KeyMatch4: number of tokens is not equal to number of values";
173+
151174
std::map<std::string, std::string> tokens_matches;
152175

153-
bool res = true;
154-
for (int i = 0; i < key2_arr.size(); i++) {
155-
if (i >= key1_arr.size()) {
156-
res = false;
157-
break;
158-
}
159-
if (key1_arr[i] != key2_arr[i]) {
160-
size_t index1 = key2_arr[i].find("*");
161-
size_t index2 = key2_arr[i].find("{");
162-
size_t index3 = key2_arr[i].find("}");
163-
std::string token = key2_arr[i].substr(1, key2_arr[i].length() - 2);
164-
if (index1 != std::string::npos) {
165-
if (index1 == 0) {
166-
res = true;
167-
break;
168-
} else if (key1_arr[i].compare(key2_arr[i].substr(0, index1))) {
169-
res = false;
170-
break;
171-
} else
172-
continue;
173-
}
174-
if (index2 == 0 && index3 > 0 && index3 != std::string::npos) {
175-
if (key1_arr[i] == "" || !token.compare("")) {
176-
res = false;
177-
break;
178-
}
179-
if (tokens_matches.find(token) == tokens_matches.end()) {
180-
tokens_matches.insert(std::pair<std::string, std::string>(token, key1_arr[i]));
181-
continue;
182-
} else if (tokens_matches.at(token).compare(key1_arr[i])) {
183-
res = false;
184-
break;
185-
} else
186-
continue;
187-
}
188-
res = false;
189-
break;
190-
} else
176+
for (int i = 0; i < tokens.size(); i++) {
177+
if (tokens_matches.find(tokens[i]) == tokens_matches.end()) {
178+
tokens_matches.insert(std::pair<std::string, std::string>(tokens[i], matches[i + 1]));
191179
continue;
180+
} else if (tokens_matches.at(tokens[i]).compare(matches[i + 1])) {
181+
return false;
182+
}
192183
}
193-
194-
if (key2_arr.size() < key1_arr.size())
195-
if (key2_arr[key2_arr.size() - 1] != "*")
196-
res = false;
197-
198-
return res;
184+
return true;
199185
}
200186

201187
// RegexMatch determines whether key1 matches the pattern of key2 in regular expression.

include/casbin/model/exprtk_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ enum class ExprtkFunctionType {
149149
KeyMatch,
150150
KeyMatch2,
151151
KeyMatch3,
152+
KeyMatch4,
152153
RegexMatch,
153154
IpMatch,
154155
};
@@ -171,6 +172,9 @@ class ExprtkFunctionFactory {
171172
case ExprtkFunctionType::KeyMatch3:
172173
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch3));
173174
break;
175+
case ExprtkFunctionType::KeyMatch4:
176+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch4));
177+
break;
174178
case ExprtkFunctionType::IpMatch:
175179
func.reset(new ExprtkOtherFunction(idenfier, IPMatch));
176180
break;

include/casbin/util/built_in_functions.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,29 @@ namespace casbin {
2323
// For example, "/foo/bar" matches "/foo/*"
2424
bool KeyMatch(const std::string& key1, const std::string& key2);
2525

26+
// KeyGet returns the matched part
27+
// For example, "/foo/bar/foo" matches "/foo/*"
28+
// "bar/foo" will been returned
29+
std::string KeyGet(const std::string& key1, const std::string& key2);
30+
2631
// KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
2732
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource"
2833
bool KeyMatch2(const std::string& key1, const std::string& key2);
2934

35+
// KeyGet2 returns value matched pattern
36+
// For example, "/resource1" matches "/:resource"
37+
// if the path_var == "resource", then "resource1" will be returned
38+
std::string KeyGet2(const std::string& key1, const std::string& key2, const std::string& path_var);
39+
3040
// KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
3141
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}"
3242
bool KeyMatch3(const std::string& key1, const std::string& key2);
3343

44+
// KeyGet3 returns value matched pattern
45+
// For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/"
46+
// if the pathVar == "project", then "project1" will be returned
47+
std::string KeyGet3(const std::string& key1, const std::string& key2, const std::string& path_var);
48+
3449
// KeyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
3550
// Besides what KeyMatch3 does, KeyMatch4 can also match repeated patterns:
3651
// "/parent/123/child/123" matches "/parent/{id}/child/{id}"

0 commit comments

Comments
 (0)