1

I have a custom requirement where I want to decide if an API can be accessed depending on certain roles. I am using Spring framework. I want to support something like this:

1. (R1 || R2) && (R3 || R4)  
2. (R1) || (R2 && R3) 

where R represents a role. || and && are logical operators denoting or and and respectively. This expression should be evaluated against an input array of roles. So if the input array is [R2, R4], then the first expression evaluates to true and second expression evaluates to false.

I found something similar using SPEL but instead of R which can be any String like customer, employee, etc, they are using boolean expressions value like true or 6 == 6, etc

rohanagarwal
  • 771
  • 9
  • 30

2 Answers2

2

You can use method security based on roles with SpEL.

@PreAuthorize("hasRole('ROLE_A') or hasRole('ROLE_B')")
public void yourMethod() {
    // ...
}
Ugur Artun
  • 1,744
  • 2
  • 23
  • 41
0

I solved the above problem using the following:

  1. I used SpEL as provided by Spring.
  2. SpEL supports property replacement.

Code for replacement:

Inventor tesla = new Inventor("Nikola Tesla");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name == Nikola Tesla");
String name = (String) exp.getValue(tesla);

Here, the name property will be replaced by Nikola Tesla. This should be used when the name property has different value each time the expression is evaluated. If the value of name property is the same every time, consider using EvaluationContext.

Now talking about the boolean expressions, you will have to compulsorily substitute values for properties, because in above example, property name can take null as the default value, but a string role can't take true or false without substitution.
And let's suppose that the SpEL contains some roles which I am not aware of, I won't be able to replace them with true and false. To solve this, I used something similar to @PreAuthorize which has the method hasRole().

Code for reference:

String roles =  "(hasRole('admin') or hasRole('superAdmin')) and hasRole('modifier')"
Expression roleExpression = parser.parseExpression(roles);
StandardEvaluationContext roleContext = new StandardEvaluationContext(new SpelHelper());
roleContext.addPropertyAccessor(new MapAccessor()); // this line might be useless
Boolean hasCorrectRole = roleExpression.getValue(roleContext, Boolean.class);


class SpelHelper {
     public boolean hasRole(String role) {
          // some logic like current user roles have the role passed as argument
          return true;
     }
}

Full documentation at: https://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/expressions.html

Also, refer Boolean Expression Evaluation in Java
This answer suggests using JEXL and the answer will give you a fair idea what to do to replace properties.

rohanagarwal
  • 771
  • 9
  • 30