spring security - How can I modify the return object of #PostAuthorize in a custom SPEL method? - spring

I wish to filter a collection returned by a service method. This filtering would involve calling another service method and hence I am not going for #PostFilter as it calls the SPEL expression inside the annotation for each element of the collection. Instead, I am using #PostAuthorize as follows :
#PostAuthorize("canAssignToUser(returnObject)")
List<UserInfo> getUsers(int userId);
In the canAssignToUser, I am trying to remove users from the return list passed to it. Apparently, this is a problem, and there is some restriction on modifying the returned list. How can I modify the returned list from a method using method-level annotations and without the SPEL expression being called for each element of the list.

The #PostAuthorize is used for evaluating a boolean value, so the interceptor does not pass the return value to the expression, (look here).
What's your method of removing the elements without checking every element? (like the #PostFilter does)
I think you don't have a choice, if you want to use Spring Security for the filtering

You can modify the returned object as you like. Even though the interceptor does not return the object you passed, it gets notified with the changes to the object. Eventually the returnedObject is modified. (Note: custom security method inside #PostAuthorize can only return boolean.)
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
public boolean canAssignToUser(Object returnObject) {
// do modifications to returnObject
return true;
}
getUsers() method returns the modified list after evaluating#PostAuthorize("canAssignToUser(returnObject)") .

Related

Access function parameter by annotation in AOP (AspectJ)

I have a function under class MyController:
#RestController
#RequestMapping(value = "/api/service")
public class MyController {
#PostMapping(value = "add_person")
public MyResponse addPerson(#RequestBody Person person) {
// ...
}
#PostMapping(value = "add_person_2")
public MyResponse addPerson(#PathVariable(value = "person_age") Int age, #RequestBody Person person) {
// ...
}
}
I have setup AspectJ in my project to have a AOP logic to run whenever those two addPerson(...) method above is called:
#Around("execution(public MyResponse addPerson(..))")
public void around(ProceedingJoinPoint joinPoint) {
// NO matter which addPerson(...) is executing, I am only interested in the
// parameter value annotated with #RequestBody.
// How can I access the parameter that passed in addPerson(...) & is annotated with
// #RequestBody through ProceedingJoinPoint ?
}
My question is mentioned in above code comment. I wonder how can I access the parameter annotated with #RequestBody in my AOP function? I don't want to check parameter type or name, but interested to know how to access parameter by checking the annotation through ProceedingJoinPoint. Is it possible?
I do not want to mark this question as a duplicate because it is no exact duplicate, but my answer here should answer the question about how to
match an annotated parameter at any position,
get the annotation + the parameter value itself.
The linked answer uses a #Before advice. If you want to somehow replace the value by another one in an #Around advice when calling proceed() this is also possible, but was not asked here and my request for seeing more of the advice method body was also ignored.
If you want to limit to annotated Person parameters, you would have to use the fully qualified class name my.package.Person instead of the * inside (*) and do the corresponding cast after accessing the parameter in the advice body.
In my comment I also asked if the parameter has a fixed relative position in the parameter list such as first, last or second/third from left/right. If the OP would have confirmed such a fixed relative position, reflection would not be necessary and the corresponding parameter could be bound to an advice method parameter directly via args() pointcut designator. This would be quite elegant and eliminate the need to loop over getArgs() or over a two-dimensional array of parameter annotations.

Obtaining handle to Collection/Array object in #PreFilter and #PostFilter in Spring Security

In Spring Security, #PreFilter and #PostFilter can be used to trim/prune the argument/return object and filterObject references each element in the object and is used to loop through the argument/return Collection/Array.
However, I need to get a handle to the actual Collection/Array as a whole and not specific elements in the context. Is there any way to do this?
The reason is that I am creating an externalized authorization service that is used by Spring Security to query and prune the collection/array and this service supports querying for multiple answers in a single question. Once I get a reference to the object as a whole, I can iterate though the elements myself to create this request to the externalized service.
Can this be done in Spring Security? I am implementing this as an custom express handler.
Assuming the return value is modifiable, you can use #PostAuthorize. For example:
#PostAuthorize("#mySecurityFilter.filter(authentication, returnObject)")
List<String> findAllMessages();
This assumes you created a Bean by the name of "mySecurityFilter" that looks something like this:
#Component
public class MySecurityFilter {
public boolean filter(Authentication authentication, List<String> domain) {
// submit to service and get back all allowed values
List<String> allowed = Arrays.asList("Hello");
Iterator<String> iValues = domain.iterator();
while(iValues.hasNext()) {
String value = iValues.next();
if(!allowed.contains(value)) {
iValues.remove();
}
}
return true;
}
}

spring security : Why can't we access Hibernate entitiy parameters in #PreAuthorize?

I have the following interface method on which I am applying #PreAuthorize :
#PreAuthorize("doSomething(#user.id)")
void something(User user, List<User> accessList);
where User is a Hibernate entity object. It gives me an error :
org.springframework.expression.spel.SpelEvaluationException:
EL1007E:(pos 13): Field or property 'id' cannot be found on null at
org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:204)
There is no way that the user parameter is null, as if I remove the annotation, and inspect the value of user in the method that implements this interface method, there is a valid User object present there. Additionally, just before calling this method, I have made sure that the user object is correctly constructed.
I really can't figure out why would the user field be considered null by the SPEL parser
You can check with the debugger what's going on in MethodSecurityEvaluationContext, inside Object lookupVariable(String name) method:
#Override
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!argumentsAdded) {
addArgumentsAsVariables();
argumentsAdded = true;
}
and so you can see what's really going on in the addArgumentsAsVariables() method as the convertion of method arguments to SPEL variables is implemented very clearly in Spring.
Spring Security has a better answer for this problem now:
http://docs.spring.io/spring-security/site/docs/3.2.5.RELEASE/reference/htmlsingle/#access-control-using-preauthorize-and-postauthorize
Basically, you can use the #P annotation or #Param annotation if you are using < JDK 8.
You can check LazyParamAwareEvaluationContext,inside loadArgsAsVariables() method, version 3.1.0.
The same key for different Entity, because of implementing interface.
I need to add something to this as the title indicates that we cannot access hibernate properties.
There are two editions of hasPermission, the loaded object and the serialized object. Here is some code from a test case:
#PreAuthorize("isAuthenticated() and hasPermission(#organization, 'edit')")
public long protectedMethod(Organization organization)
{
return organization.getId();
}
And for the latter here we see that we can infact access the id proprty of the organization (which is a hibernate entity):
#PreAuthorize("isAuthenticated() and hasPermission(#organization.getId(), 'organization', 'edit')")
public long protectedMethodSerializableEdtion(Organization organization)
{
return organization.getId();
}

Using EJB interceptors after a method call

I know one can use interceptors before a method call by using the #AroundInvoke annotation.
What I would like to do is execute certain code after the method call, so that I can for example create a log entry before and after a method execution.
Is this possible with EJB3, or do I need to use AOP?
#AroundInvoke interceptor is passed InvocationContext, and proceed() must be called to advance the method. Thus:
#AroundInvoke
public Object log(InvocationContext ic) throws Exception {
logEntry();
try {
return ic.proceed();
} finally {
logExit();
}
}
Depending on your needs, you could also log the return value or exceptions, filter the methods being logged, etc.

Spring Framework validate request parameter or path variable

I know I can validate forms in Spring, but can I apply similar validate to URL parameters? For example, I have a method in my controller as follows:
public String edit(#PathVariable("system") String system,
#RequestParam(value="group") String group,
ModelMap model) throws DAOException {
Can I validate the values of system and group before the method is called, to ensure they are of a certain value or match a certain regex?
Thanks
You may be able to use Spring Asserts for this. The Assert api (http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/util/Assert.html) runs a supplied expression against the specified parameters and if the expression equates to false then it throws an exception.
Ex:
Assert.isTrue(system.equals("ValidSystemName"), "You must supply a valid system");
It also contains functions to check that parameters are not null or are not empty strings, etc.
Create an annotation that marks parameters that should be validated. This annotation needs a #Retention of RUNTIME and a #Target of ElementType.PARAMETER.
Create a validator implemented as an AspectJ Aspect.
Wrap calls to controllers with this validator.
A sample annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#Documented
public #interface ValidSystemParameter {
}
A sample validator:
#Aspect
public class ValidSystemParameterValidator {
#Pointcut("TODO: write your pointcut expression")
public void controllerMethodWithValidSystemParameter();
#Before(pointcut = "controllerMethodWithValidSystemParameter()")
public void validateSystemParameter(String systemParameter) {
// validate the parameter (throwing an exception)
}
}
To learn about the AspectJ pointcut expression language see: http://www.eclipse.org/aspectj/doc/released/progguide/language-joinPoints.html
To learn about AspectJ integration in Spring see: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj
I might be a little late, but with Spring 3.0 you have the option of using JSR-303 validation with the #Valid annotation. There are also some more specific annotations as #DateTimeFormat and #NumberFormat. More details here: http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/validation.html#validation-mvc
As I see it you have two options:
Define your request parameters as objects and user JSR-303
validation.
Use the Assert api as mentioned above.
If you just want to make a simple validation on a single value, I would go with the latter (that's what I did when I had simple int values to check for max value).

Resources