![JAR search and dependency download from the Maven repository](/logo.png)
org.apache.juneau.rest.guard.RoleMatcher Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.rest.guard;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import org.apache.juneau.common.internal.*;
import org.apache.juneau.internal.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.StateMachineState.*;
/**
* Utility class for matching JEE user roles against string expressions.
*
*
* Supports the following expression constructs:
*
* "foo" - Single arguments.
* "foo,bar,baz" - Multiple OR'ed arguments.
* "foo | bar | bqz" - Multiple OR'ed arguments, pipe syntax.
* "foo || bar || bqz" - Multiple OR'ed arguments, Java-OR syntax.
* "fo*" - Patterns including '*' and '?' .
* "fo* & *oo" - Multiple AND'ed arguments, ampersand syntax.
* "fo* && *oo" - Multiple AND'ed arguments, Java-AND syntax.
* "fo* || (*oo || bar)" - Parenthesis.
*
*
* Notes:
* - AND operations take precedence over OR operations (as expected).
*
- Whitespace is ignored.
*
null or empty expressions always match as false .
*
*
* See Also:
* - Guards
*
*/
public class RoleMatcher {
private final Exp exp;
private static final AsciiSet
WS = AsciiSet.create(" \t"),
OP = AsciiSet.create(",|&"),
META = AsciiSet.create("*?");
/**
* Constructor.
*
* @param expression The string expression.
* @throws ParseException If the expression is malformed.
*/
public RoleMatcher(String expression) throws ParseException {
this.exp = parse(expression);
}
/**
* Returns true if the specified string matches this expression.
*
* @param roles The user roles.
* @return
* true if the specified string matches this expression.
*
Always false if the string is null .
*/
public boolean matches(Set roles) {
return roles != null && exp.matches(roles);
}
@Override /* Object */
public String toString() {
return exp.toString();
}
/**
* Returns all the tokens used in this expression.
*
* @return All the tokens used in this expression.
*/
public Set getRolesInExpression() {
Set set = new TreeSet<>();
exp.appendTokens(set);
return set;
}
private Exp parse(String expression) throws ParseException {
if (StringUtils.isEmptyOrBlank(expression))
return new Never();
expression = expression.trim();
List ors = list();
List ands = list();
StateMachineState state = S01;
int i = 0, mark = -1;
int pDepth = 0;
boolean error = false;
for (i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (state == S01) {
// S01 = Looking for start
if (! WS.contains(c)) {
if (c == '(') {
state = S02;
pDepth = 0;
mark = i+1;
} else if (OP.contains(c)) {
error = true;
break;
} else {
state = S03;
mark = i;
}
}
} else if (state == S02) {
// S02 = Found [(], looking for [)].
if (c == '(')
pDepth++;
if (c == ')') {
if (pDepth > 0)
pDepth--;
else {
ands.add(parse(expression.substring(mark, i)));
mark = -1;
state = S04;
}
}
} else if (state == S03) {
// S03 = Found [A], looking for end of A.
if (WS.contains(c) || OP.contains(c)) {
ands.add(parseOperand(expression.substring(mark, i)));
mark = -1;
if (WS.contains(c)) {
state = S04;
} else {
i--;
state = S05;
}
}
} else if (state == S04) {
// S04 = Found [A ], looking for & or | or ,.
if (! WS.contains(c)) {
if (OP.contains(c)) {
i--;
state = S05;
} else {
error = true;
break;
}
}
} else if (state == S05) {
// S05 = Found & or | or ,.
if (c == '&') {
//ands.add(operand);
state = S06;
} else /* (c == '|' || c == ',') */ {
if (ands.size() == 1) {
ors.add(ands.get(0));
} else {
ors.add(new And(ands));
}
ands.clear();
if (c == '|') {
state = S07;
} else {
state = S01;
}
}
} else if (state == S06) {
// S06 = Found &, looking for & or other
if (! WS.contains(c)) {
if (c != '&')
i--;
state = S01;
}
} else /* (state == S07) */ {
// S07 = Found |, looking for | or other
if (! WS.contains(c)) {
if (c != '|')
i--;
state = S01;
}
}
}
if (error)
throw new ParseException("Invalid character in expression '"+expression+"' at position " + i + ". state=" + state, i);
if (state == S01)
throw new ParseException("Could not find beginning of clause in '"+expression+"'", i);
if (state == S02)
throw new ParseException("Could not find matching parenthesis in expression '"+expression+"'", i);
if (state == S05 || state == S06 || state == S07)
throw new ParseException("Dangling clause in expression '"+expression+"'", i);
if (mark != -1)
ands.add(parseOperand(expression.substring(mark, expression.length())));
if (ands.size() == 1)
ors.add(ands.get(0));
else
ors.add(new And(ands));
if (ors.size() == 1)
return ors.get(0);
return new Or(ors);
}
private static Exp parseOperand(String operand) {
boolean hasMeta = false;
for (int i = 0; i < operand.length() && ! hasMeta; i++) {
char c = operand.charAt(i);
hasMeta |= META.contains(c);
}
return hasMeta ? new Match(operand) : new Eq(operand);
}
//-----------------------------------------------------------------------------------------------------------------
// Expression classes
//-----------------------------------------------------------------------------------------------------------------
abstract static class Exp {
abstract boolean matches(Set roles);
void appendTokens(Set set) {}
}
static class Never extends Exp {
@Override
boolean matches(Set roles) {
return false;
}
@Override /* Object */
public String toString() {
return "(NEVER)";
}
}
static class And extends Exp {
Exp[] clauses;
And(List clauses) {
this.clauses = clauses.toArray(new Exp[clauses.size()]);
}
@Override /* Exp */
boolean matches(Set roles) {
for (Exp e : clauses)
if (! e.matches(roles))
return false;
return true;
}
@Override /* Exp */
void appendTokens(Set set) {
for (Exp clause : clauses)
clause.appendTokens(set);
}
@Override /* Object */
public String toString() {
return "(& " + StringUtils.join(clauses, " ") + ')';
}
}
static class Or extends Exp {
Exp[] clauses;
Or(List clauses) {
this.clauses = clauses.toArray(new Exp[clauses.size()]);
}
@Override
boolean matches(Set roles) {
for (Exp e : clauses)
if (e.matches(roles))
return true;
return false;
}
@Override /* Exp */
void appendTokens(Set set) {
for (Exp clause : clauses)
clause.appendTokens(set);
}
@Override /* Object */
public String toString() {
return "(| " + StringUtils.join(clauses, " ") + ')';
}
}
static class Eq extends Exp {
final String operand;
Eq(String operand) {
this.operand = operand;
}
@Override /* Exp */
boolean matches(Set roles) {
for (String role : roles)
if (operand.equals(role))
return true;
return false;
}
@Override /* Exp */
void appendTokens(Set set) {
set.add(operand);
}
@Override /* Object */
public String toString() {
return "[= " + operand + "]";
}
}
static class Match extends Exp {
final Pattern p;
final String operand;
Match(String operand) {
this.operand = operand;
p = StringUtils.getMatchPattern(operand);
}
@Override /* Exp */
boolean matches(Set roles) {
for (String role : roles)
if (p.matcher(role).matches())
return true;
return false;
}
@Override /* Exp */
void appendTokens(Set set) {
set.add(operand);
}
@Override /* Object */
public String toString() {
return "[* " + p.pattern().replaceAll("\\\\[QE]", "") + "]";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy