Skip to content

Commit 14b68c7

Browse files
authored
feat: fix 'in' operator parsing for tuple literals in matcher expressions (#499)
1 parent b334732 commit 14b68c7

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class Util {
3535
public static boolean enableLog = true;
3636
private static Pattern evalReg = Pattern.compile("\\beval\\(([^),]*)\\)");
3737

38-
private static final Pattern IN_SYNTAX_PATTERN = Pattern.compile("([a-zA-Z0-9_.()\"]*) +in +([a-zA-Z0-9_.()\"]*)");
38+
private static final Pattern IN_SYNTAX_PATTERN = Pattern.compile("([^\\s]+?) +in +(\\([^)]*\\)|\\S+)");
3939

4040
private static Pattern escapeAssertionRegex = Pattern.compile("\\b(r|p)[0-9]*\\.");
4141

@@ -181,7 +181,16 @@ public static String convertInSyntax(String expString) {
181181
private static String replaceInSyntaxWithInclude(Matcher matcher) {
182182
StringBuffer sb = new StringBuffer();
183183
do {
184-
matcher.appendReplacement(sb, "include($2, $1)");
184+
String leftOperand = matcher.group(1);
185+
String rightOperand = matcher.group(2);
186+
187+
// If right operand is a parenthesized expression with commas (tuple literal),
188+
// convert to tuple() function. Otherwise, keep parentheses as-is for grouping.
189+
if (rightOperand.startsWith("(") && rightOperand.endsWith(")") && rightOperand.contains(",")) {
190+
rightOperand = "tuple" + rightOperand;
191+
}
192+
193+
matcher.appendReplacement(sb, Matcher.quoteReplacement("include(" + rightOperand + ", " + leftOperand + ")"));
185194
} while (matcher.find());
186195
matcher.appendTail(sb);
187196
return sb.toString();

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,28 @@ public void testEscapeAssertion(){
5555

5656
@Test
5757
public void testConvertInSyntax(){
58+
// Basic cases
5859
assertEquals("include(r_obj, r_sub)", Util.convertInSyntax("r_sub in r_obj"));
5960
assertEquals("include(r_obj, r_sub.name)", Util.convertInSyntax("r_sub.name in r_obj"));
6061
assertEquals("include(r_obj.name, r_sub.name)", Util.convertInSyntax("r_sub.name in r_obj.name"));
6162
assertEquals("include(r_obj, r_sub) && r.obj == p.obj", Util.convertInSyntax("r_sub in r_obj && r.obj == p.obj"));
63+
64+
// Tuple/array with single quotes - should convert to tuple() function
65+
assertEquals("include(tuple('data2', 'data3'), r.obj)", Util.convertInSyntax("r.obj in ('data2', 'data3')"));
66+
67+
// Tuple/array with double quotes - should convert to tuple() function
68+
assertEquals("include(tuple(\"data2\", \"data3\"), r.obj)", Util.convertInSyntax("r.obj in (\"data2\", \"data3\")"));
69+
70+
// Tuple/array without quotes - should convert to tuple() function
71+
assertEquals("include(tuple(data2, data3), r.obj)", Util.convertInSyntax("r.obj in (data2, data3)"));
72+
73+
// Complex expression with 'in' operator and tuple (the original issue scenario)
74+
assertEquals("g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || include(tuple('data2', 'data3'), r.obj)",
75+
Util.convertInSyntax("g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3')"));
76+
77+
// Multiple 'in' operators
78+
assertEquals("include(r.obj.admins, r.sub.name) && include(r.obj.admins, (\"bob\"))",
79+
Util.convertInSyntax("r.sub.name in r.obj.admins && (\"bob\") in r.obj.admins"));
6280
}
6381

6482
@Test

0 commit comments

Comments
 (0)