How to check security acess (#Secured or #PreAuthorize) before validation (#Valid) in my Controller? - spring

here is my Controller code :
#PreAuthorize("hasRole('CREATE_USER')")
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public UserReturnRO createUser(#Valid #RequestBody UserRO userRO) throws BadParameterException{
return userService.createUser(userRO);
}
My need is when a client without the appropriate role tries to create a user, the controller responds "Not authorized" even if the data sent are not valid. Instead of that, if the client (without the appropriate role) tries to create a user with wrong data, my controller responds with the #Valid message (ex : "password cannot be empty"), while I want it responds "not authorized".
In the PreAuthorized Interface we can find this sentence :
Annotation for specifying a method access-control expression which will be evaluated to decide whether a method invocation is allowed or not.
but it seems that it's not the case.

You can not do this directly, since #Valid is processed before an actual method call and as a result before #PreAuthorize.
But what you can do instead is to inject BindingResult just right after your model (userRO) and in doing so - take control of validation process. Then check if BindingResult has some errors and if so return bad request response (similar to what spring does).
Example:
#ResponseBody
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
#PreAuthorize("hasRole('CREATE_USER')")
public ResponseEntity<?> createUser(#RequestBody #Valid UserRO userRO, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
return ResponseEntity.ok(userService.createUser(userRO));
}

As already stated, Spring Security's #PreAuthorize is method advice, which means that it does not get to participate until the method and its arguments have already been resolved.
Aside from the answer already given, there are a few ways to move authorization before argument resolution, instead.
Filter Security
First, Spring Security checks URLs before the request is mapped to a method. And since this is a #Controller, it's reasonable to suppose that you could instead map the request to the role at that level instead of #PreAuthorize:
http
.authorizeRequests()
.mvcMatchers(POST, "/somepath").hasRole("CREATE_USER")
Handler Interceptor
Second, Spring MVC does ship with limited support for checking authorities before parsing method arguments. For example, you can do:
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
UserRoleAuthorizationInterceptor userRole =
new UserRoleAuthorizationInterceptor();
userRole.setAuthorizedRoles("CREATE_USER");
registry.addInterceptor(userRole);
}
}
This is much more basic than #PreAuthorize since it's a global setting, but I've included it for completeness.
Handler Interceptor, Part 2
Third (warning, some inelegance ahead), you can create your own HandlerInterceptor.
The flow is:
FilterSecurityInterceptor <== where .mvcMatchers(...).hasRole(...) lives
Then HandlerInterceptors
Then argument validation
Then MethodSecurityInterceptor <== where #PreAuthorize lives
So, your HandlerInterceptor would check before arguments are resolved. It doesn't have to be as involved as MethodSecurityInterceptor, though. It could, for example, simply be:
static class AuthorizationInterceptor extends HandlerInterceptorAdapter {
SecurityMetadataSource securityMetadataSource;
AccessDecisionManager accessDecisionManager;
#Override
public void preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
Authentication authenticated = (Authentication) request.getUserPrincipal();
MethodInvocation mi = convert(handler);
Collection<ConfigAttribute> attributes =
this.securityMetadataSource.getAttributes(mi);
// throws AccessDeniedException
this.accessDecisionManager.decide(authenticated, mi, attributes);
return true;
}
}
Then you wire it together with:
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodConfig extends GlobalMethodSecurityConfiguration {
#Bean
HandlerInterceptor preAuthorize() throws Exception {
return new AuthorizationInterceptor(
accessDecisionManager(), methodSecurityMetadataSource());
}
}
#EnableWebMvc
public static class MvcConfig implements WebMvcConfigurer {
#Autowired
AuthorizationInterceptor authorizationInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor);
}
}
It's inelegant because MethodSecurityInterceptor would still participate in authorized requests, which would ostensibly be the majority.

Related

Spring custom validator class with MultipartFile class support does not get called/fired

Sorry i couldn't find a better way to construct the question.
I am trying to validate on server side an array of multipartfile received with other information (e.g email, phone number etc) from an Ajax multipart/form-data post request.
I created 2 custom validator classes by implementing Spring Validator interface, UserFilesValidator to check that all received files are image files and are not above 2MB
and UserInfosValidator to check other user information e.g. phone number validate, email avalibility etc.
I registered the custom validator classes using #InitBinder.
Now when request is made, UserInfosValidator gets called and works fine but UserFilesValidator doesn't get called
Here is my code
Controller class
#Controller
public class UserController{
private final UserFilesValidator userFilesValidator;
private final UserInfosValidator userInfosValidator;
#Autowired
public UserController(UserFilesValidator userFilesValidator, UserInfosValidator userInfosValidator){
this.userFilesValidator = userFilesValidator;
this.userInfosValidator = userInfosValidator;
}
#InitBinder("userfiles")
public void formDataBinder1(WebDataBinder binder){
binder.addValidators(userFilesValidator);
}
#InitBinder("userinfos")
public void formDataBinder2(WebDataBinder binder){
binder.addValidators(userInfosValidator);
}
#RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseBody
public ResponseEntity<UserEntity> addUser(#RequestParam("userfiles") #Valid MultipartFile[] files,
#RequestPart("userinfos") #Valid UserEntity user){
//codes ...
}
}
Validator class
#Component
public class UserFilesValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return MultipartFile[].class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MultipartFile[] files = (MultipartFile[]) target;
//validation codes ...
}
}
I excluded UserInfosValidator class since it works fine.
I also did not included global exception handler class (that's #ControllerAdvice annotated class)
since errors has to be registered in UserFilesValidator class which is not even getting called.
So please how can i get this to work? I know i can validate it in addUser method but I don't want to have validation codes in controller class
request handler method.

Control #RestController availability programmatically

Is it possible to control a #RestController programmatically to enable it or disable it? I don't want to just write code in each #RequestMapping method to do some kind of if (!enabled) { return 404Exception; }
I've seen this question but that works only at startup time. What I need is really something that would allow me to enable or disable the controller multiple times.
I've thought of different ways but don't know which are doable in spring.
Actually control the container (jetty in my case) so requests to that particular endpoint are disabled
Somehow control RequestMappingHandlerMapping since it seems to be that class that does the mapping between urls and controllers
control the lifecycle of the #RestController component so that i can create it and destroy it at will, but then i'm not sure how to trigger the mapping to the endpoint
If the end result is that you want to respond with a 404 when you decide that a specific endpoint should be disabled then you could write an interceptor which checks whether your enabled condition is false and, if so, sets the response accordingly.
For example:
#Component
public class ConditionalRejectionInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String requestUri = request.getRequestURI();
if (shouldReject(requestUri)) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return false;
}
return super.preHandle(request, response, handler);
}
private boolean shouldReject(String requestUri) {
// presumably you have some mechanism of inferring or discovering whether
// the endpoint represented by requestUri should be allowed or disallowed
return ...;
}
}
In Spring Boot, registering your own interceptor just involves implementing a WebMvcConfigurerAdapter. For example:
#Configuration
public class CustomWebMvcConfigurer extends WebMvcConfigurerAdapter {
#Autowired
private HandlerInterceptor conditionalRejectionInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
// you can use .addPathPatterns(...) here to limit this interceptor to specific endpoints
// this could be used to replace any 'conditional on the value of requestUri' code in the interceptor
registry.addInterceptor(conditionalRejectionInterceptor);
}
}

Spring MVC accessing Spring Security ConfigAttributes?

I want to produce HTTP Response Body with an error message referencing something like _"missing ... 'CUSTOM_AUTHORITY'"_ in addition to a 403 Forbidden HTTP Status code.
My application is Spring Boot with a Spring-Security-Secured #PreAuthorize method within a Spring-MVC-REST #Controller:
MyController
#Controller
#RequestMapping("/foo")
public FooController{
#PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')")
public Object getSomething(){ ... }
}
GlobalExceptionHandlerResolver
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(AccessDeniedException.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public Object forbidden(AccessDeniedException exception){ ... }
}
What I want is to expose/inject Collection<ConfigAttribute>. The Spring Security docs reference it.
There doesn't seem to be a straightforward way of accomplishing this. The AccessDecisionManager (which is AffirmativeBased) throws the AccessDeniedException with none of the information you want. So if you want to "expose/inject" the Collection<ConfigAttribute>, you'll want to provide your own AccessDecisionManager that throws a custom exception that holds the ConfigAttributes.
The easiest way to do this could be to wrap the default AccessDecisionManager with your own and delegate method calls to it:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled=true)
CustomMethodSecurityConfig extends GlobalMethodSecurityConfiguration
#Override
protected AccessDecisionManager accessDecisionManager() {
AccessDecisionManager default = super.accessDecisionManager();
MyCustomDecisionManager custom = new CustomDecisionManager(default);
}
}
You could define your custom AccessDecisionManager as follows:
public class MyCustomDecisionManager implements AccessDecisionManager {
private AccessDecisionManager default;
public MyCustomDecisionManager(AccessDecisionManager acm) {
this.default = acm;
}
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException{
try {
default.decide(authentication, object, configAttributes)
} catch(AccessDeniedException ex) {
throw new CustomAccessDeniedException(ex.getMessage(), configAttributes);
}
}
// other methods delegate to default
}
Now whenever access is denied, you will get an exception that holds the Collection<ConfigAttribute>.
Your custom exception could look like this:
public class CustomAccessDeniedException extends AccessDeniedException {
private Collection<ConfigAttribute> attributes;
public CustomAccessDeniedException(String message, Collection<ConfigAttribute> attr) {
super(message);
this.attributes = attr;
}
public Collection<ConfigAttribute> getAttributes() {
return this.attributes;
}
}
Now your #ExceptionHandler could handle your CustomAccessDeniedException and have access to the ConfigAttributes.
HOWEVER...
I am not sure that will provide you with the error message you wanted. The ConfigAttribute interface only has one method:
String getAttribute();
And the javadoc states:
If the ConfigAttribute cannot be expressed with sufficient precision as a String, null should be returned.
Since we can't rely on the interface method, how you deal with each ConfigAttribute will be heavily dependent on the type of the particular object you're dealing with.
For example, the ConfigAttribute that corresponds to #PreAuthorize("hasAuthority('CUSTOM_AUTHORITY')") is PreInvocationExpressionAttribute, and to print something that resembles what you want, you could do:
PreInvocationExpressionAttribute attr = (PreInvocationExpressionAttribute)configAttribute;
String expressionString = attr.getAuthorizeExpression().getExpressionString();
System.out.println(expressionString); // "hasAuthority('CUSTOM_AUTHORITY')"
That's the major drawback. Also, you would get ALL the ConfigAttributes, not necessarily the ones that failed.

Injecting Custom Principal to Controllers by Spring Security

servletApi() support of Spring Security is great.
I want to inject custom Principal as this:
public interface UserPrincipal extends Principal {
public Integer getId();
}
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipal user){
// implementation
}
or
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipalImpl user){
// implementation
}
Spring has support for injecting Principal instances with the help of ServletRequestMethodArgumentResolver.
It is injecting principal as this:
else if (Principal.class.isAssignableFrom(paramType)) {
return request.getUserPrincipal();
}
Here is the place where the problem begins. request is here an instance of SecurityContextHolderAwareRequestWrapper. It has an implementation of:
#Override
public Principal getUserPrincipal() {
Authentication auth = getAuthentication();
if ((auth == null) || (auth.getPrincipal() == null)) {
return null;
}
return auth;
}
Because an Authentication is also an Principal. (The only part of spring security I did not like so far. I will ask this a separate question as well.)
This is causing a problem. Because Authentication is a Principal not a UserPrincipal.
How can I resolve this problem? Do I need to implement an authentication which is a UserPrincipal as well? Or should I change HandlerMethodArgumentResolver order a create a custom resolver? (This is not easy for Spring MVC because internal handlers has higher priority.)
As a extra information:
I am using Spring Security M2 and my configuration for AuthenticationManagerBuilder is simply:
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(detailsService);
}
Any help?
Fundamentally this seems like trouble integrating with Spring MVC and not a Spring Security issue. Spring Security has no way of knowing that Authentication#getPrinicpal() implements Principal since the API returns an Object.
I see a few options for you. Each has some pros and cons, but I think the best is using #ModelAttribute and #ControllerAdvice
#ModelAttribute and #ControllerAdvice
The easiest option is annotate a method with #ModelAttribute on custom #ControllerAdvice. You can find details in the Spring Reference.
#ControllerAdvice
public class SecurityControllerAdvice {
#ModelAttribute
public UserPrincipal customPrincipal(Authentication a) {
return (UserPrincipal) a == null ? null : a.getPrincipal();
}
}
Now in your controller you can do something like this:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(#ModelAttribute UserPrincipal user){
// implementation
}
Note that the #ModelAttribute is necessary only to ensure the #ModelAttribute is used over the HttpServletRequest#getPrincipal(). If it did not implement Principal, #ModelAttribute is not required.
#Value and ExpressionValueMethodArgumentResolver
You can also do something like this:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(
#Value("#{request.userPrincipal.principal}") UserPrincipal user){
// implementation
}
This works because the HttpServletRequest is available as an attribute to the ExpressionValueMethodArgumentResolver (added by default by Spring MVC) which allows accessing things via SpEL. I find this less attractive than #ModelAttribute due to the constant that must be in the #Value annotation. It will be nicer when SPR-10760 is resolved which would allow for your own custom annotation to be used like:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Value("#{request.userPrincipal.principal}")
public #interface CurrentUser { }
#Autowire RequestMappingHandlerAdapter
This is a bit sloppy because the RequestMappingHandlerAdapter has already been initialized, but you can change the ordering of the HandlerMethodArgumentResolvers as shown here:
#EnableWebMvc
#Configuration
public class WebMvcConfiguration
extends WebMvcConfigurerAdapter {
...
#Autowired
public void setArgumentResolvers(RequestMappingHandlerAdapter adapter) {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
resolvers.add(new CustomPrincipalArgumentResolver());
resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
adapter.setArgumentResolvers(resolvers);
}
}
Subclass WebMvcConfigurationSupport
You can also extend WebMvcConfigurationSupport instead of using #EnableWebMvc to ensure your HandlerMethodArgumentResolver is used first. For example:
#Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
...
#Bean
#Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter()();
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
resolvers.add(new CustomPrincipalArgumentResolver());
resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
adapter.setArgumentResolvers(resolvers);
return adapter;
}
}
I know this is an old question, but as it does come up on top on Google when searching for injecting a Principal, I'll post a 2020 update:
Since Spring Security 4.0 you can just simply inject an #AuthenticationPrincipal into your controller methods:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(#AuthenticationPrincipal UserPrincipal user){
// implementation
}
This will work out of the box, no additional config required.

Spring Securing #RequestBody

What is the proper way to secure the #RequestBody with Spring Security?
For example: A User can have multiple Blogs and each Blog can have multiple Entrys. A user goes to save an entry to a certain blog and the request would come in like this:
#RequestMapping(value="/api/entry", method=RequestMethod.POST)
#ResponseBody
public Entry save(#Valid #RequestBody Entry entry) {
this.entryService.save(entry);
return entry;
}
Now, the incoming entry has a Blog, the user could have doctored up the request and chosen someone else's blog, effectively posting the entry to their blog. Though I could catch this in validation (query the persistence layer to verify that the Blog belongs to the logged in User) I feel that this should be handled by Spring Security. If so, how do I go about doing this?
We had this kind of situation.
Here is the two solution. I did not like much
#RequestMapping(value="/api/entry", method=RequestMethod.POST)
#ResponseBody
#PreAuthorize("#entry.author.name == principal.name)"
public Entry save(#Valid #RequestBody Entry entry, Principal principal) {
this.entryService.save(entry);
return entry;
}
or
#RequestMapping(value="/api/entry", method=RequestMethod.POST)
#ResponseBody
#PreAuthorize("Decision.isOK(entry, principal)")
public Entry save(#Valid #RequestBody Entry entry, Principal principal) {
this.entryService.save(entry);
return entry;
}
//In that case Spring will call your static isOk() method from Decision class. It should return boolean.
Spring injects Principal principal authorized object for the method, you do not have to worry about it.
Enable #PreAuthorize annotation with
<security:global-method-security pre-post-annotations="enabled" />
Second Using Aspect. Create aspect.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Protector {
}
#Aspect
#Component
public class MyAspect {
#Before("#annotation(com.xyz.Protector)")
public void before(JoinPoint joinPoint) throws Throwable {
//u can get method object from joinPoint object,
Method method = ((MethodSignature)joinPoint.getMethodSignature()).getMethod();
//As long as you have Method object you can read the parameter objects (Entry and Principal) with reflection.
//So Compare here: If entry.getOwner().getId().equal(principal.getName()) blah blah blah
}
}
#RequestMapping(value="/api/entry", method=RequestMethod.POST)
#ResponseBody
#Protector
public Entry save(#Valid #RequestBody Entry entry, Principal principal) {
this.entryService.save(entry);
return entry;
}
If you have aspect you can have more owning on runtime
Also refer to this ulr

Resources