Skip to content

Commit 62afab5

Browse files
authored
feat: add keyGet3 function (#522)
1 parent a7903ab commit 62afab5

5 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/main/java/org/casbin/jcasbin/model/FunctionMap.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public static FunctionMap loadFunctionMap() {
8484
fm.addFunction("keyMatch5", new KeyMatch5Func());
8585
fm.addFunction("keyGet", new KeyGetFunc());
8686
fm.addFunction("keyGet2", new KeyGet2Func());
87+
fm.addFunction("keyGet3", new KeyGet3Func());
8788
fm.addFunction("regexMatch", new RegexMatchFunc());
8889
fm.addFunction("ipMatch", new IPMatchFunc());
8990
fm.addFunction("eval", new EvalFunc());

src/main/java/org/casbin/jcasbin/util/BuiltInFunctions.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,47 @@ public static String keyGet2Func(String key1, String key2, String pathVar) {
275275
return "";
276276
}
277277

278+
/**
279+
* KeyGet3 returns value matched pattern. For example, "/resource1" matches "/{resource}", if the pathVar == "resource", then "resource1" will be returned.
280+
* This is similar with KeyGet2(), except using "/proxy/{id}" instead of "/proxy/:id".
281+
*
282+
* @param key1 the first argument.
283+
* @param key2 the second argument.
284+
* @param pathVar the name of the variable to retrieve from the matched pattern.
285+
* @return the matched part.
286+
*/
287+
public static String keyGet3Func(String key1, String key2, String pathVar) {
288+
key2 = key2.replace("/*", "/.*");
289+
String regexp = "\\{[^/]+?\\}";
290+
Pattern re = Pattern.compile(regexp);
291+
Matcher keys = re.matcher(key2);
292+
List<String> keysList = new ArrayList<>();
293+
while (keys.find()) {
294+
keysList.add(keys.group());
295+
}
296+
key2 = re.matcher(key2).replaceAll("([^/]+?)");
297+
// Escape { and } for Java regex compatibility since { is treated as repetition quantifier
298+
key2 = key2.replace("{", "\\{").replace("}", "\\}");
299+
key2 = "^" + key2 + "$";
300+
Pattern re2 = Pattern.compile(key2);
301+
Matcher values = re2.matcher(key1);
302+
List<String> valuesList = new ArrayList<>();
303+
while (values.find()) {
304+
for (int i = 0; i <= values.groupCount(); i++) {
305+
valuesList.add(values.group(i));
306+
}
307+
}
308+
if (valuesList.isEmpty()) {
309+
return "";
310+
}
311+
for (int i = 0; i < keysList.size(); i++) {
312+
if (pathVar.equals(keysList.get(i).substring(1, keysList.get(i).length() - 1))) {
313+
return valuesList.get(i + 1);
314+
}
315+
}
316+
return "";
317+
}
318+
278319
/**
279320
* regexMatch determines whether key1 matches the pattern of key2 in regular expression.
280321
*
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2021 The casbin Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.casbin.jcasbin.util.function;
18+
19+
import com.googlecode.aviator.runtime.function.AbstractFunction;
20+
import com.googlecode.aviator.runtime.function.FunctionUtils;
21+
import com.googlecode.aviator.runtime.type.AviatorObject;
22+
import com.googlecode.aviator.runtime.type.AviatorString;
23+
import org.casbin.jcasbin.util.BuiltInFunctions;
24+
25+
import java.util.Map;
26+
27+
/**
28+
* KeyGet3Func is the wrapper for keyGet3.
29+
*
30+
* @author yanglif
31+
* @since 2021/02/09
32+
*/
33+
public class KeyGet3Func extends AbstractFunction {
34+
@Override
35+
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
36+
String key1 = FunctionUtils.getStringValue(arg1, env);
37+
String key2 = FunctionUtils.getStringValue(arg2, env);
38+
String pathVar = FunctionUtils.getStringValue(arg3, env);
39+
40+
return new AviatorString(BuiltInFunctions.keyGet3Func(key1, key2, pathVar));
41+
}
42+
43+
@Override
44+
public String getName() {
45+
return "keyGet3";
46+
}
47+
}

src/test/java/org/casbin/jcasbin/main/BuiltInFunctionsUnitTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,46 @@ public void TestKeyGet2() {
214214
testKeyGet2("/alice/all", "/:/all", "", "");
215215
}
216216

217+
@Test
218+
public void testKeyGet3Func() {
219+
// KeyGet3() is similar with KeyGet2(), except using "/proxy/{id}" instead of "/proxy/:id".
220+
testKeyGet3("/foo", "/foo", "id", "");
221+
testKeyGet3("/foo", "/foo*", "id", "");
222+
testKeyGet3("/foo", "/foo/*", "id", "");
223+
testKeyGet3("/foo/bar", "/foo", "id", "");
224+
testKeyGet3("/foo/bar", "/foo*", "id", "");
225+
testKeyGet3("/foo/bar", "/foo/*", "id", "");
226+
testKeyGet3("/foobar", "/foo", "id", "");
227+
testKeyGet3("/foobar", "/foo*", "id", "");
228+
testKeyGet3("/foobar", "/foo/*", "id", "");
229+
230+
testKeyGet3("/", "/{resource}", "resource", "");
231+
testKeyGet3("/resource1", "/{resource}", "resource", "resource1");
232+
testKeyGet3("/myid", "/{id}/using/{resId}", "id", "");
233+
testKeyGet3("/myid/using/myresid", "/{id}/using/{resId}", "id", "myid");
234+
testKeyGet3("/myid/using/myresid", "/{id}/using/{resId}", "resId", "myresid");
235+
236+
testKeyGet3("/proxy/myid", "/proxy/{id}/*", "id", "");
237+
testKeyGet3("/proxy/myid/", "/proxy/{id}/*", "id", "myid");
238+
testKeyGet3("/proxy/myid/res", "/proxy/{id}/*", "id", "myid");
239+
testKeyGet3("/proxy/myid/res/res2", "/proxy/{id}/*", "id", "myid");
240+
testKeyGet3("/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id", "myid");
241+
testKeyGet3("/proxy/", "/proxy/{id}/*", "id", "");
242+
243+
testKeyGet3("/api/group1_group_name/project1_admin/info", "/api/{proj}_admin/info", "proj", "");
244+
testKeyGet3("/{id/using/myresid", "/{id/using/{resId}", "resId", "myresid");
245+
testKeyGet3("/{id/using/myresid/status}", "/{id/using/{resId}/status}", "resId", "myresid");
246+
247+
testKeyGet3("/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res", "res3");
248+
testKeyGet3("/api/project1_admin/info", "/api/{proj}_admin/info", "proj", "project1");
249+
testKeyGet3("/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
250+
"g", "group1");
251+
testKeyGet3("/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
252+
"gn", "group_name");
253+
testKeyGet3("/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
254+
"proj", "project1");
255+
}
256+
217257
@Test
218258
public void testRegexMatchFunc() {
219259
testRegexMatch("/topic/create", "/topic/create", true);

src/test/java/org/casbin/jcasbin/main/TestUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ static void testKeyGet2(String key1, String key2, String pathVar, String res) {
273273
assertEquals(res, BuiltInFunctions.keyGet2Func(key1, key2, pathVar));
274274
}
275275

276+
static void testKeyGet3(String key1, String key2, String pathVar, String res) {
277+
assertEquals(res, BuiltInFunctions.keyGet3Func(key1, key2, pathVar));
278+
}
279+
276280
static void testEval(String eval, Map<String, Object> env, AviatorEvaluatorInstance aviatorEval, boolean res) {
277281
assertEquals(res, BuiltInFunctions.eval(eval, env, aviatorEval));
278282
}

0 commit comments

Comments
 (0)