I would like to test my rest controller in spring. In my the service, a user requires to pass an authentication token. I use a token utils class that decodes the token and get an organization id. I then use the id for several processes. How do I do a mock of this in Spring? I have followed this documentation but am getting a 404 on running the test.
TodoController
#RequestMapping("todos")
class TodoController {
#Autowired
lateinit var todoService: TodoService
#Autowired
lateinit var tokenUtils: TokenUtils
#GetMapping
fun getAllTodos(authentication: OAuth2Authentication): ResponseEntity<List<Todo>> {
tokenUtils.init(authentication);
val organizationId = tokenUtils.organizationId
return ResponseEntity.ok(todoService.findAll(organizationId))
}```
I am a bit new to spring tests and will appreciate any help accorded
First you need to create a mock Authentication class that implements Authentication interface. Most of following code is auto generated. Override any method you you need (in this case, I only override getPrincipal method).
import java.util.Collection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class MockPrincipal implements Authentication {
public MockPrincipal() {}
#Override
public String getName() {
// TODO Auto-generated method stub
return null;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return null;
}
#Override
public Object getCredentials() {
// TODO Auto-generated method stub
return null;
}
#Override
public Object getDetails() {
// TODO Auto-generated method stub
return null;
}
#Override
public Object getPrincipal() {
// TODO Auto-generated method stub
return "mock user";
}
#Override
public boolean isAuthenticated() {
// TODO Auto-generated method stub
return false;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
// TODO Auto-generated method stub
}
}
After that you can mock up an OAuth2Authentication object like this:
MockPrincipal mockUser = new MockPrincipal();
OAuth2Authentication mockOAuth2Object = new OAuth2Authentication(null, mockUser);
Related
I understand that in order to keep the context, the reactive chain of methods must not be broken. However, I need to get access to the context from the ExceptionResolver (after an exception has been thrown.)
My exception resolver is extending AbstractErrorWebExceptionHandler and when I try to get the context via ReactiveSecurityContextHolder.getContext() it returns empty. Obviously because the reactive chain has been broken.
How can I get access to the authentication object?
You can get access to the authentication object by overriding the handle method:
public class TestHandler extends AbstractErrorWebExceptionHandler {
public TestHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
ApplicationContext applicationContext) {
super(errorAttributes, resourceProperties, applicationContext);
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return null;
}
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
Mono<Principal> authObject = exchange.getPrincipal();
//Handle exception here
return exchange.getResponse().setComplete();
}
}
Another approach is to implement the ErrorWebExceptionHandler directly:
public class TestHandler implements ErrorWebExceptionHandler {
#Override
public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable throwable) {
Mono<Principal> authObject = serverWebExchange.getPrincipal();
//Handle exception here
return serverWebExchange.getResponse().setComplete();
}
}
Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}
I'm writing Grails application with Spring Security plugin.
I have enabled queries generated by GORM into console and I have noticed that every request Security query the database about users, selecting them by it's username.
My intention is load user's by it's ID's, not usernames for increase performance.
I am aware that there is a possibility to overwrite UserDetailsService method loadUserByUsername(String username), but this method is used both for refreshing user's credential during the session and in login form, where in fact I want to authenticate user by it's username.
I have three questions:
How to load user by id? Should I inject user ID instead of username in GrailsUser (implementation of UserDetails) instead of regular username and use long selectById = Long.valueOf(String username)?
How to create different user provider for refreshing session to grab user by ID and different for user login (when I want to grab user by it's username/email)?
There is possibility to fetch user credentials not every request, but every X seconds?
Finally i managed to solve this problem. The queries are generated by:
springSecurityService.getCurrentUser()
Unfortunatelly, this method fetches User model class by username (from Principal object) and maps it into database field, at most configured by:
grails.plugin.springsecurity.userLookup.usernamePropertyName
as mentioned in documentation.
I have tried
grails.plugin.springsecurity.userLookup.usernamePropertyName = 'id'
but i received class-cast exception from String to Long.
Workaround is simple - create own Principle with username field typed as Long.
See PrincipalProxy in my solution:
package com.selly.util.security
import java.security.Principal;
import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority
class AppMetadataAuthenticationToken implements Authentication, Principal {
private boolean authenticated
private GrailsUser userDetails
private Principal principal
public AppMetadataAuthenticationToken(GrailsUser userDetails) {
this.userDetails = userDetails
this.principal = new PrincipalProxy(userDetails)
}
public GrailsUser getUser() {
return userDetails
}
public String getUsername() {
return userDetails.getUsername()
}
#Override
public String getName() {
return userDetails.getUsername()
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userDetails.getAuthorities()
}
#Override
public Object getCredentials() {
return userDetails.password
}
#Override
public Object getDetails() {
return getUser()
}
#Override
public Object getPrincipal() {
return principal
}
#Override
public boolean isAuthenticated() {
return authenticated
}
#Override
public void setAuthenticated(boolean authenticated) throws IllegalArgumentException {
this.authenticated = authenticated
}
static class PrincipalProxy implements Principal {
GrailsUser grailsUser
Long username
public PrincipalProxy(GrailsUser grailsUser) {
this.grailsUser = grailsUser
this.username = grailsUser.id
}
#Override
public String getName() {
return grailsUser.id
}
}
}
To return this Token, just register your own AuthenticationProvider:
package com.selly.util.security;
import grails.plugin.springsecurity.SpringSecurityService
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
public class AppUsernamePasswordAuthenticationProvider extends DaoAuthenticationProvider implements AuthenticationProvider {
SpringSecurityService springSecurityService
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
def token = (UsernamePasswordAuthenticationToken) authentication
def user = userDetailsService.loadUserByUsername(authentication.principal)
if(!user)
throw new UsernameNotFoundException("Cannot find user", authentication.principal)
if(!passwordEncoder.isPasswordValid(user.password, authentication.credentials, null))
throw new BadCredentialsException("Invalid password")
return new AppMetadataAuthenticationToken(user)
}
#Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
package com.selly.util.security;
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
public class AppMetadataAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// TODO Auto-generated method stub
return authentication;
}
#Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub
return AppMetadataAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Register it in resources.groovy
appUsernamePasswordAuthenticationProvider(AppUsernamePasswordAuthenticationProvider) {
userDetailsService = ref('userDetailsService')
passwordEncoder = ref('passwordEncoder')
userCache = ref('userCache')
saltSource = ref('saltSource')
preAuthenticationChecks = ref('preAuthenticationChecks')
postAuthenticationChecks = ref('postAuthenticationChecks')
springSecurityService = ref('springSecurityService')
}
And in Config.groovy:
grails.plugin.springsecurity.providerNames = [
'appMetadataAuthenticationProvider',
'appUsernamePasswordAuthenticationProvider',
// 'daoAuthenticationProvider',
// 'anonymousAuthenticationProvider',
// 'rememberMeAuthenticationProvider'
]
Now all works perfectly:
Hibernate: select this_.id as id13_0_, this_.account_expired as account2_13_0_, this_.account_locked as account3_13_0_, this_.enabled as enabled13_0_, this_."password" as password5_13_0_, this_.password_expired as password6_13_0_, this_.username as username13_0_, this_.workspace as workspace13_0_ from users this_ where (**this_.id=?**) limit ?
Instead of using getCurrentUser() you can also getPrincipal() and cast to your previously populated object with more data than Principal interface offers.
How to generate "intercept-url" dynamically. My user name and roles are stored in database,
I want to map all these users in to spring security.Is there any way to do this?
You'll have to provide your own implementation of com.icod.solapCore.spring.security.FilterInvocationSecurityMetadataSource.
This could look like this :
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
#Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) object;
HttpServletRequest request = filterInvocation.getHttpRequest();
Collection<ConfigAttribute> result = new ArrayList<ConfigAttribute>();
// Find roles in database that secures the specified request
// ...
// For any role found, create a SecurityConfig object prefixed with "ROLE_" ex :
// for(String role : roles) {
// ConfigAttribute attribute = new SecurityConfig("ROLE_"+roleFound);
// result.add(attribute);
// }
return result;
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
#Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
And then you'll have to replace the default FilterInvocationSecurityMetadataSource with your own. I do it with a BeanPostProcessor, called after spring read the configuration file but before it makes the configuration official. Looks like this :
public class MyFilterInvocationSecurityMetadataSourceBeanPostProcessor implements BeanPostProcessor {
private FilterInvocationSecurityMetadataSource metadataSource = new MyFilterInvocationSecurityMetadataSource();
#Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (bean instanceof FilterInvocationSecurityMetadataSource) {
return metadataSource;
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
}
Then you just have to configure the bean post processor :
<bean id="solapcoreFilterInvocationSecurityMetadataSourceBeanPostProcessor" class="foo.bar.MyFilterInvocationSecurityMetadataSourceBeanPostProcessor"/>
Hope this help.
Give all your users same role and operate with this role in config.
You can read abour roles here
I have a hard time understanding the injection mechanism of Jersey. The JAX-RS Specification (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) states that injection via #Context is possible in Application subclasses, root resource classes and providers.
I now have a class that is instantiated at startup and has a method which is called on every request. Inside the method I need access to the current UriInfo object. The problem is, that this method is not called from my code. So I can't pass UriInfo directly to the method.
I actually want to do something like this:
public class MyClass implements ThirdPartyInterface {
// not possible because class is no Application subclass, root resource class or provider
#Context
private UriInfo uriInfo;
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
I tried this. Obviously with no success:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public MyClass(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
// uriInfo is null at this time :(
myClass = new MyClass(uriInfo);
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
}
}
The only workaround I can think of is this. I don't think it's very clean:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public void setUriInfo(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
myClass = new MyClass();
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
myClass.setUriInfo(uriInfo);
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
myClass.setUriInfo(null);
}
}
I hope there is a better solution, but maybe I'm completely on the wrong track.
Thanks!
Late answer, but a good question ... so lets go:
You can use a org.glassfish.hk2.api.Factory and javax.inject.Provider for injections. I don't know since which version this is available, so maybe you have to upgrade your jersery version. For the following samples i used jersey 2.12.
First you have to implement and register/bind a Factory for your MyClass:
MyClassFactory:
import javax.inject.Inject;
import javax.ws.rs.core.UriInfo;
import org.glassfish.hk2.api.Factory;
// ...
public class MyClassFactory implements Factory<MyClass> {
private final UriInfo uriInfo;
// we will bind MyClassFactory per lookup later, so
// the constructor will be called everytime we need the factory
// meaning, uriInfo is also per lookup
#Inject
public MyClassFactory(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public MyClass provide() {
return new MyClass(uriInfo)
}
#Override
public void dispose(UriInfo uriInfo) {
// ignore
}
}
Registration via ResourceConfig:
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
// ...
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class);
// ... bind additional factories here
}
});
// ...
}
}
Now you are able to inject MyClass per lookup to providers, resources etc.
But Attention: Afaig there are two approaches and only one will work as eventually aspected for providers ...
import javax.inject.Inject;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
// ...
#Provider
#Produces("application/foo-bar")
public class MyBodyWriter implements MessageBodyWriter<MyView> {
// first approache - don't do it!
// will only injected once, cause MyBodyWriter is only instantiated once
#Inject
private MyClass myClass;
// second approache - works fine!
private final javax.inject.Provider<MyClass> provider;
// MyBodyWriter instantiate once
// get an inject provider here
#Inject
public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) {
this.provider = myClassProvider;
}
#Override
public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) {
return t == MyView.class;
}
#Override
public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) {
// deprecated by JAX-RS 2.0 and ignored by Jersey runtime
return 0;
}
#Override
public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException {
// attention: its not per lookup !!!
MyClass myClassDirectInjected = myClass;
System.out.println(myClassDirectInjected); // same instance everytime
// but this is ;)
MyClass myClassFromProvider = provider.get();
System.out.println(myClassFromProvider); // it's a new instance everytime
// ...
}
}
Hope this was somehow helpfull.