Spring Boot Security OAuth2 + Custom Permission Evaluator - spring

I have a Spring boot (1.5.3) oauth2 application that has secured URL's and methods. Method security is currently working via:
#PreAuthorize("hasRole('ROLE_NAME')")
I'm now trying to add a custom PermissionEvaluator so that I can secure methods with
#PreAuthorize("hasPermission(#id, 'typeName', 'permissionName')").
I'm enabling this functionality by extending GlobalMethodSecurityConfiguration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, mode =
AdviceMode.ASPECTJ, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
{
#Autowired
private MatterRepository matterRepository;
#Autowired
private MatterTeamMemberRepository matterTeamMemberRepository;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
Map<String, Permission> permissionsMap = new HashMap<>();
permissionsMap.put(ReadMatterPermission.class.getSimpleName(),
new ReadMatterPermission(matterRepository, matterTeamMemberRepository));
OAuth2MethodSecurityExpressionHandler handler = new
OAuth2MethodSecurityExpressionHandler();
handler.setPermissionEvaluator(new
EntityPermissionEvaluator(permissionsMap));
return handler;
}
}
Note: PermissionEvaluator class omitted for brevity.
The problem I'm having is that hasPermission is sometimes invoked - most often it is not invoked. I suspect this is something to do with:
*Auto-configure an expression handler for method-level security (if the user
* already has
* {#code #EnableGlobalMethodSecurity}).
o.s.b.autoconfigure.security.oauth2.method.OAuth2MethodSecurityConfiguration
creating an instance of OAuth2MethodSecurityExpressionHandler before my configuration is processed.
Has anyone successfully injected a PermissionEvaluator into a spring boot oauth2 applicaion?
Thanks.

Turned out not to be a spring security configuration issue. The issue was related to aspectj compiler configuration in the pom. Long story short, fully qualified class name of AnnotationSecurityAspect was incorrect:
org.springframework.security.access.intercept.aspectj.AnnotationSecurityAspect
instead of
org.springframework.security.access.intercept.aspectj.aspect.AnnotationSecurityAspect
causing #PreAuthorize annotations to be ignored. My understanding that #PreAuthorize("hasRole...") was working was wrong - confused by the fact that tests sometimes succeeded when run through Eclipse but never when run via mvn on the command line.

Related

Overriding a class defined in Spring Boot \ Spring Security

We are in the process of migrating a legacy application to Spring Boot. In order to continue with testing until we have assigned roles to users, I would like to override the following:
class: SecurityContextHolderAwareRequestWrapper
method: public boolean isUserInRole(String role)
I have created a new class which extends SecurityContextHolderAwareRequestWrapper and overrides isUserInRole(), as follows:
#Component
public class MySecurityContextHolderAwareRequestWrapper extends org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper {
public MySecurityContextHolderAwareRequestWrapper(HttpServletRequest request,
AuthenticationTrustResolver trustResolver, String rolePrefix) {
super(request, trustResolver, rolePrefix);
}
#Override
public boolean isUserInRole(String role) {
return true;
}
When the application is run, the new bean does not take the place of the existing SecurityContextHolderAwareRequestWrapper class. This is clear because when the new class is instantiated, the constructor is not injected with the beans being injected into SecurityContextHolderAwareRequestWrapper. The application fails to start because parameters of type AuthenticationTrustResolver and String to the new class MySecurityContextHolderAwareRequestWrappercould could not be found
What is the correct way to override SecurityContextHolderAwareRequestWrapper, or for that matter any class in the Spring Boot framework?
Thanks
The SecurityContextHolderAwareRequestWrapper class is ultimately used by the SecurityContextHolderAwareRequestFilter configured with http.servletApi(). Some information about this feature is available in the Spring Security reference docs.
This feature shields you from direct dependence on Spring Security and provides very high level integration with Spring Security through the Servlet API. You cannot directly influence the class used to wrap the request.
However, if you wish to temporarily modify the result of role checks, you can influence what roles are available in the Authentication object during authentication itself. See info in the docs on GrantedAuthority, and note that you will want to customize roles during authentication by providing a custom UserDetailsService.

Spring Boot 2.1.4: #Autowired does not work in custom Jackson Serializers/Deserializers, how to enable it?

I am struggeling to get a Spring Component #Autowired into my custom Deserializer.
Example:
#JsonDeserialize (using = SomeClassJsonDeserializer.class)
SomeClass {
[...]
}
#JsonComponent
SomeClassJsonDeserializer extends JsonDeserializer<SomeClass> {
#Autowired
private SomeService service;
#Override
public SomeClass deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
[...]
// this.service is null
}
}
I found mainly two possible solutions which didn't work for me at all:
use SpringBeanAutowiringSupport in default constructor of Deserializer
use HandlerInstantiator (via config / custom implementation)
I am using only those Jackson annotations shown in the example above to 'configure' the Jackson parsing.
There is no additional custom configuration affecting Jackson in any way besides the default SpringBoot auto configuration. When using #EnableWebMvc (which breaks Spring-Boot auto configuration so I don't want to use it), the Component-wiring does work as expected.
Is there any official / recommended solution for plain Spring-Boot with default auto configuration ?
The problem was with how I used Spring's RestTemplate.
For a remote call, I created a new Instance of RestTemplate by contructor call (new RestTemplate()).
This way, Spring wasn't able to configure the RestTemplate - bean correctly (so that SpringBoot autoconfigure and Jackson autoconfigure 'connect' together, resulting in working Spring-DI in custom Jackson components).
I simply had to #Autowire the RestTemplateBuilder bean instance provided by Spring, and then call RestTemplateBuilder.build() to aqquire a RestTemplate bean instance created by Spring.

How to Inject custom method argument in Spring WebFlux using HandlerMethodArgumentResolver?

I want to create an custom method argument Resolver using Spring WebFlux. I am following link but its seem to be not working.
I am able to create the custom argument resolver using WebMvc.
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
public class MyContextArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return MyCustomeObject.class.isAssignableFrom(parameter.getParameterType())
}
#Override
public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bindingContext,
ServerWebExchange exchange) {
.....
return Mono.just(new MyCustomeObject())
}
Please note that i am using HandlerMethodArgumentResolver from .web.reactive. package.
My AutoConfiguration file look like
#Configuration
#ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the class-path
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)//checks that the app is a reactive web-app
public class RandomWebFluxConfig implements WebFluxConfigurer {
#Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
MyContextArgumentResolver[] myContextArgumentResolverArray = {contextArgumentResolver()};
configurer.addCustomResolver(myContextArgumentResolverArray );
}
#Bean
public MyContextArgumentResolver contextArgumentResolver() {
return new MyContextArgumentResolver ();
}
My spring.factories looks like
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.XXXX.XXX.XXX.RandomWebFluxConfig
Please note that above configuration is part of the jar which is added in Spring WebFlux Boot project enabled using #EnableWebFlux .
It seems you're conflating two different problems here.
First, you should make sure that your method argument resolver works in a regular project.
For that, you need a #Configuration class that implements the relevant method in WebFluxConfigurer. Your code snippet is doing that but with two flaws:
Your configuration is using #EnableWebFlux, which is disabling the WebFlux auto-configuration in Spring Boot. You should remove that
it seems you're trying to cast a list of MethodArgumentResolver into a single instance and that's probably why things aren't working here. I believe your code snippet could be just:
configurer.addCustomResolver(contextArgumentResolver());
Now the second part of this question is about setting this up as a Spring Boot auto-configuration. I guess that you'd like WebFlux applications to automatically get that custom argument resolvers if they depend on your library.
If you want to achieve that, you should first make sure to read up a bit about auto-configurations in the reference documentation. After that, you'll realize that your configuration class is not really an auto-configuration since it will be applied in all cases.
You should probably add a few conditions on that configuration like:
#ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the classpath
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) // checks that the app is a reactive web app

Java bean validation on Spring MVC Controller PathVariables

I am trying to get Java Bean validation annotations to work with path variables and query parameters in Spring MVC controller. (Environment: Spring Boot v1.3.5, Springxxx 4.2.6, programming language Kotlin 1.0.3)
e.g.
#RequestMapping(value = "/{someId}" ...)
fun getSomething(**#SomeValidId** #PathVariable("someId") someId: String):...
I have added org.springframework.validation.beanvalidation.MethodValidationPostProcessor as described in https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/ and also added org.springframework.validation.beanvalidation.LocalValidatorFactoryBean as the validatorFactory to the above.
#Configuration
...class .... {
...
#Bean
open fun localValidatorFactoryBean() = LocalValidatorFactoryBean()
#Bean
open fun methodValidationPostProcessor() : MethodValidationPostProcessor {
val methodValidationPostProcessor = MethodValidationPostProcessor()
methodValidationPostProcessor.setValidator(localValidatorFactoryBean())
return methodValidationPostProcessor
}
}
But when I annotate the Controller class (or the interface it implements) with
org.springframework.validation.annotation.Validated as suggested looks like the controller class is proxied (which seems to be as expected - https://github.com/spring-projects/spring-security/issues/3215).
#Validated
interface SomeResource {
....
#RestController
#RequestMapping("/somepath")
class SomeController ......: SomeResource ....
But this causes the Spring mvc request mapping setup to ignore the SomeController. Debugging through the Spring framework code looked like org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods() goes through the list of beans and tries to detect handler methods but the above controller is ignored by the looks of it as it encounters a proxy instance and it doesn't carry the #Controller or #RequestMapping annotations.
Does anyone have any idea what's missing? There seems to be a lot of information out there that seem to suggest this should be possible, but couldn't find a working example .
Well I found the issue - it was because the proxy created for the controller was a JDK Dynamic proxy. When I forced it to be a CGLIB proxy it started working alright.
By default Kotlin classes are final and therefore forced to use JDK Dynamic proxies, but marking the controller as 'open' was not sufficient to coerce it to use CGLIB. Had to add #Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) to the controller class
#RestController
#RequestMapping("/somepath")
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class SomeController ......: SomeResource ....

Using spring security annotations with keycloak

I'm just a beginner in Spring Security, but I would like to know is it possible to configure keycloak in a way that I can use #PreAuthorize, #PostAuthorize, #Secured and other annotations.
For example, I've configured the keycloak-spring-security-adapter and Spring Security in my simple Spring Rest webapp so that I have access to Principal object in my controller, like this:
#RestController
public class TMSRestController {
#RequestMapping("/greeting")
public Greeting greeting(Principal principal, #RequestParam(value="name") String name) {
return new Greeting(String.format(template, name));
}
...
}
But when I try this (just an example, actually I want to execute custom EL expression before authorization):
#RestController
public class TMSRestController {
#RequestMapping("/greeting")
#PreAuthorize("hasRole('ADMIN')")
public Greeting greeting(Principal principal, #RequestParam(value="name") String name) {
return new Greeting(String.format(template, name));
}
...
}
I get exception:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
In my spring security config I enabled global method security:
What do I need to make this spring security annotations work? Is it possible to use this annotation in this context at all?
here is example code:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
#ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
}
and
#PreAuthorize("hasRole('ROLE_ADMIN')")
Apart from this code. you need to do the role mapping for realm roles and client(application roles). the application roles will be put in #PreAuthorize
You still have to configure Spring Security using Keycloak. Take a look at the adapter documentation for an annotation based configuration. Once that's set up your Spring Security annotations will work on authorized calls.

Resources