Unit Tests for Authenticated Resources in Dropwizard 0.8.0 - java

I want to test my Jersey resources in a Dropwizard 0.8.0-rc2 application. An additional obstacle is that I use TestNG instead of JUnit, so I have to do some things by hand that I copied from DropwizardClientRule.
Now I have some resources that are secured by #Auth. In Dropwizard 0.7.1 I added the authenticator to the test application as follows:
DropwizardResourceConfig resourceConfig = DropwizardResourceConfig.forTesting(…);
Authenticator<BasicCredentials, Identity> authenticator =
credentials -> getAuthorizedIdentity();
resourceConfig.getSingletons().add(
new BasicAuthProvider<>(authenticator, "TEST"));
Here, getAuthorizedIdentity() would fetch some test authorization. This would be passed to the #Auth annotated injected parameter. Now, of course, with Jersey 2 things have changed a bit. I tried:
DropwizardResourceConfig resourceConfig = DropwizardResourceConfig.forTesting(…);
Authenticator<BasicCredentials, Identity> authenticator =
credentials -> getAuthorizedIdentity();
resourceConfig.register(AuthFactory.binder(
new BasicAuthFactory<>(authenticator, "TEST", Identity.class)));
The effect is that I get 401 on all secured resources. Debugging shows that my authenticate function is never called at all! If I remove the registration, then I will not get 401s anymore (so the registration is not without effect), but now the secured resources are unsecured and get null for the #Auth annotated parameter.
So, how do I add the authenticator to the test context so it will work? The production code works fine with almost the same line.

This issue was fixed recently.
See https://github.com/dropwizard/dropwizard/pull/966

I just had the same problem, after looking at the tests in the dropwizard-auth project I came to this solution, hope it helps.
public class ExamplesResourceTest extends JerseyTest
{
#Override
protected TestContainerFactory getTestContainerFactory()
throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
#Override
protected DeploymentContext configureDeployment() {
return ServletDeploymentContext.builder(new ExampleTestResourceConfig())
.initParam( ServletProperties.JAXRS_APPLICATION_CLASS, ExampleTestResourceConfig.class.getName() )
.initParam( ServerProperties.PROVIDER_CLASSNAMES, ExampleResource.class.getName() )
.build();
}
public static class ExampleTestResourceConfig extends DropwizardResourceConfig {
private ObjectMapper mapper = Jackson.newObjectMapper();
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public ExampleTestResourceConfig() {
super(true, new MetricRegistry());
register( new JacksonMessageBodyProvider( mapper, validator ) );
register( AuthFactory.binder( new BasicAuthFactory<>( new ExampleAuthenticator(), "realm", String.class ) ) );
}
}
#Test
public void exampleTest() throws AuthenticationException
{
Response response = target( "/api/example" )
.request( MediaType.APPLICATION_JSON )
.header( HttpHeaders.AUTHORIZATION, "Basic QmlsYm86VGhlU2hpcmU=" )
.get();
assertThat( response.getStatus() ).isEqualTo( 200 );
}
}

Related

Play Framework 1.x Functional Test with #AllowFeature Controller Method

I do want to write some functional tests for our project. Techstack: Play Framework 1.5, Java 16, Junit 3.
I found following documentation:
test - 1.5.x
security - 1.5.x
So the Controller looks something like this.
#AllowFeature(Feature.SEARCH_BOX)
public static void search(String term) {
//implementation omitted
TablePage<MyAwesomeType> page = search(term);
render(..., page, term);
}
And my test looks like this
public class SearchTest extends FunctionalTest {
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
}
However, the TablePage<MyAwesomeType> page is null when it really should not be, and i am unable to step into the controller method with the debugger. So it looks like the controller method search(...) is not called at all.
The response Code is 302 - Found but I think this might be play suggestion it found the path /foo/search
My guess is that i need to setup some UserContext or send a authenticityToken along with the request. So play can check the required feature #AllowFeature(Feature.A_SEARCH_BOX).
Does anybody know how I would setup such a functional test?
Any help is appreciated. Thanks!
I was able to figure this out.
I need to log into the application and then the play FunctionalTest.class takes care of the cookie.
Add #NoAuthenticityto the login method
#NoAuthenticity // <-- This enables execution without authenticityToken
public static void login(#Required String username, #Required String password) {
...
}
Post a request to login before the test.
#Test
public void search_withResults() {
// 1. login
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
// Note: session info / authenticityToken is stored in a cookie
// FunctionalTest.class makes sure to use this cookie for subsequent requests
// This request now works like a charm
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
Note: One can use the JUnit #Before Annotation to simplify the test class.
#Before
public void login(){
Map<String, String> credentials = Map.of("username", "MyUsername", "password", "MyPassword");
POST("/login", credentials);
}
#Test
public void search_withResults() {
String term = "ABC";
Http.Response response = GET("/foo/search?term=" + term);
assertStatus(302, response);
assertThat(renderArgs("page"), is(notNullValue()));
TablePage<MyAwesomeType> page = (TablePage<MyAwesomeType>) renderArgs("page");
assertTrue(page.getTotalRecords() >= 1);
}
#Test
public void anotherTest() { ... }
#Test
public void yetAnotherTest() { ... }

How do I configure a client Java application which must request a token from an authorization server using a 'password' grant type?

I need to implement a client Java application with Maven which makes requests from a resource server. In order for a request to be authorized a bearer token has to be provided in the header of the request. To obtain the token, another request has to be done before. The server provides me all parameters needed to make the token request: client_id, client_secret, username, password, etc. The grant_type of the token request is 'password'.
The server is based on GraphQL so I'm using the following Maven plugin in order to generate the Java classes which perform the GraphQL queries:
https://github.com/graphql-java-generator/graphql-maven-plugin-project
They provide a tutorial on how to "Access to an OAuth2 GraphQL server":
https://graphql-maven-plugin-project.graphql-java-generator.com/client_oauth2.html
As they mention in the tutorial, in order to connect to the OAuth server, I must use Spring. The tutorial explains how to access a server which uses 'client_credentials' grant type. I am not familiar with spring and spring-security so I didn't manage to configure my Spring client for 'password' grant type (all I did was to change in the application.properties the grant_type from 'client_credentials' to 'password'). Where the username and password need to be configured? What additional configurations do I need to make? Is there any way to do this without Spring? What I'm trying to obtain is way which requests the token from the authorization server and (under the hood) adds the token to the resource requests.
Following is the piece of code which I have following the tutorial:
#SpringBootApplication(scanBasePackageClasses = { MinimalSpringApp.class, GraphQLConfiguration.class})
public class MinimalSpringApp implements CommandLineRunner
{
/**
* The executor, that allows to execute GraphQL queries. The class name is the one defined in the GraphQL schema.
*/
#Autowired
QueryExecutor queryExecutor;
public static void main( String[] args )
{
SpringApplication.run( MinimalSpringApp.class, args );
}
/**
* This method is started by Spring, once the Spring context has been loaded. This is run, as this class implements
* {#link CommandLineRunner}
*/
#Override
public void run( String... args ) throws Exception
{
// Create query
GraphQLRequest softwareTechnologyRequest = queryExecutor.getSoftwareTechnologyGraphQLRequest( "{ id name }" );
SoftwareTechnologyFilter filter = new SoftwareTechnologyFilter();
// Execute query
List<SoftwareTechnology> technologies =
queryExecutor.softwareTechnology( softwareTechnologyRequest, filter, 0, "", 1000, "", 0,
Collections.emptyList() );
System.out.println( technologies );
}
#Bean
ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("provider_test");
return oauth;
}
When I run the application it throws an IllegalStateException caused by '401 Unauthorized from POST'.
SOLVED
I needed to create my own client manager bean, where the username and password are set. There is an example in the Spring official documentation at the "Resource Owner Password Credentials" section:
https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle/#oauth2client
#Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager( ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService authorizedClientService)
{
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password().refreshToken().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService );
authorizedClientManager.setAuthorizedClientProvider( authorizedClientProvider );
authorizedClientManager.setContextAttributesMapper( contextAttributesMapper() );
return authorizedClientManager;
}
private Function<OAuth2AuthorizeRequest, Mono<Map<String, Object>>> contextAttributesMapper()
{
return authorizeRequest -> {
Map<String, Object> contextAttributes = new HashMap<>();
contextAttributes.put( OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, "username" );
contextAttributes.put( OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, "password" );
return Mono.just(contextAttributes);
};
}
Then the bean from the graphql java generator plugin tutorial needed to be adapted in order to use the provided client manager:
#Bean
ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction(
ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
authorizedClientManager);
oauth.setDefaultClientRegistrationId("provider_test");
return oauth;
}

Void Method in Service is not Mocking in Integration Testing

In my spring boot project, one of my Service depends on external service like Amazon. I am writing the integration testing of the Controller classes. So, I want to mock the method in the AmazonService class(as it depends on third party API). The method is void with a single Long argument and can throw a custom application-specific exceptions.
The method is as follows:-
class AmazonService{
public void deleteMultipleObjects(Long enterpriseId) {
String key = formApplicationLogokey(enterpriseId,null);
List<S3ObjectSummary> objects = getAllObjectSummaryByFolder(key);
List<DeleteObjectsRequest.KeyVersion> keys = new ArrayList<>();
objects.stream().forEach(object->keys.add(new DeleteObjectsRequest.KeyVersion(object.getKey())));
try{
DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(this.bucket).withKeys(keys);
this.s3client.deleteObjects(deleteObjectsRequest);
log.debug("All the Application logos deleted from AWS for the Enterprise id: {}",enterpriseId);
}
catch(AmazonServiceException e){
throw new AppScoreException(AppScoreErrorCode.OBJECT_NOT_DELETED_FROM_AWS);
}
}}
class Test
class Test
{
#Autowired
AmazonServiceImpl amazonService;
#Autowired
EnterpriseService enterpriseService;
#Before
public void init()
{
amazonService = Mockito.mock(AmazonServiceImpl.class);
Mockito.doNothing().when(amazonService).deleteMultipleObjects(isA(Long.class));
}
#Test
public void testDeleteEnterprise(){
setHeaders();
EnterpriseDTO enterpriseDTO = createEnterpriseEntity(null,"testDeleteEnterpriseName3",null,null,null);
String postUrl = TestUrlUtil.createURLWithPort(TestConstants.ADD_ENTERPRISE,port);
HttpEntity<EnterpriseDTO> request1 = new HttpEntity<>(enterpriseDTO,headers);
ResponseEntity<EnterpriseDTO> response1 = restTemplate.postForEntity(postUrl,request1,EnterpriseDTO.class);
assert response1 != null;
Long enterpriseId = Objects.requireNonNull(response1.getBody()).getId();
String url = TestUrlUtil.createURLWithPort(TestConstants.DELETE_ENTERPRISE,port)+File.separator+enterpriseId;
HttpEntity<EnterpriseDTO> request = new HttpEntity<>(null, headers);
ResponseEntity<Object> response = restTemplate.exchange(url,HttpMethod.DELETE,request,Object.class);
Assert.assertEquals(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE,response.getBody());
}
}
class EnterpriseResource
class EnterpriseResource
{
#DeleteMapping("/enterprises/{enterpriseId}")
public ResponseEntity<Object> deleteEnterprise(#PathVariable Long enterpriseId) {
log.debug("REST request to delete Enterprise : {}", enterpriseId);
enterpriseService.delete(enterpriseId);
return ResponseEntity.badRequest().body(Constants.ENTERPRISE_DELETION_SUCCESS_MESSAGE);
}
}
class EnterpriseServiceImpl
class EnterpriseServiceImpl
{
#Override
public void delete(Long enterpriseId) {
log.debug("Request to delete Enterprise : {}", enterpriseId);
enterpriseRepository.deleteById(enterpriseId);
amazonService.deleteMultipleObjects(enterpriseId);
}
}
I have tried various approaches to Mock this method but it didn't work and control is going inside this method during debugging. I want to do nothing in this method during testing.
I have tried the various approaches like throw(), doNothing(), spy() etc.
Please help what is missing here?
Thanks

Feign Registration - Spring Cloud - Change Target without ribbon over-ride

Introduction
I would like to be able to have two different spring profiles, and depending on the profile to change to a hardcoded address for our feign builders.
Currently was have the following:
return builder.target(cls, "http://" + serviceName);
But I would actually like to do the following and over-ride the address:
return builder.target(cls, "http://our-server:8009/" + serviceName);
Why
Sometimes we don't want to run all the services within our development environment. Additionally, some of the services are only available through a zuul gateway sometimes.
So we run the same code in different situations and conditions.
Technical Details
We have the following code that we use for building our Feign Clients.
We had been using the #FeignClient annotation in the past, but lately we decided to start building our feignClients manually.
Example below:
#FeignClient(name = "ab-document-store", configuration = MultiPartSupportConfiguration.class, fallback = DocumentStoreFallback.class)
We call the feignRegistrar class with the following command:
return registerFeignClient(DocumentStoreClient.class, true);
#RequiredArgsConstructor
//#Component
#Slf4j
public class FeignRegistrar {
#Autowired
private Decoder decoder;
#Autowired
private Encoder encoder;
#Autowired
private Client client;
#Autowired
private Contract feignContract;
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Autowired
private List<RequestInterceptor> interceptors;
public <T> T register(Class<T> cls, String serviceName, boolean isDocumentStore) {
if(isDocumentStore){
encoder = new MultipartFormEncoder(new SpringEncoder(messageConverters));
}
//Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(encoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
for(RequestInterceptor interceptor : interceptors) {
builder.requestInterceptor(interceptor);
}
log.debug("Registering {} - as feign proxy ", serviceName);
return builder.target(cls, "http://" + serviceName);
}
public static class Slf4Logger extends Logger {
#Override
protected void log(String configKey, String format, Object... args) {
log.info("{} - {}", configKey, args);
}
}
}
Spring Cloud Property Over-ride
We have also been using property files such as application-ENV.property with entries such as the following:
ab-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
ab-document-store.ribbon.listOfServers: localhost:8025
Unfortunately, listOfServers is not enough for us. We would like to be able to assign a directory/path as well. Something like:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
Otherworkaround
I have thought about sneaking in a header into all requests such as X-SERVICE-NAME using a feign interceptor. Then we could point all services to an address (e.g. localhost:9001) , and forward/proxy those requests to localhost:9001/X-SERVICE-NAME.
However, I would prefer a much easier solution such as:
ab-document-store.ribbon.listOfServers: localhost:8025/ab-document-store
But this doesn't work :(
Introduction
I found a solution for this using a proxy that detects a header.
So, I have a feign interceptor on the java-side that attaches a header x-service-name to every feign-request.
I also have a NodeJS proxy, that analyzes requests, finds x-service-name, and re-writes the requests to become: x-service-name/originalRequestPath.
This allows me to have all the microservices behind a zuul gateway but also access them using a eureka-over-ride.
Java-Feign-Interceptor
Feign.Builder builder = Feign.builder()
.client(client)
.encoder(usedEncoder)
.decoder(decoder)
.contract(feignContract)
.logger(new Slf4Logger())
.logLevel(Logger.Level.HEADERS);
builder.requestInterceptor(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("X-Service-Name", serviceName);
}
});
NodeJS proxy
In the example, my zuul gateway ( or another proxy ) is on localhost:9001.
I'm listening on localhost:1200 .
let enableProxyForJava = process.env.ENABLE_PROXY_FOR_JAVA;
if (enableProxyForJava != undefined && enableProxyForJava.toLowerCase() === 'true') {
var httpProxyJava = require('http-proxy');
var proxyJava = httpProxyJava.createProxy();
gutil.log( gutil.colors.green('Enabling Proxy for Java. Set your Eureka overrides to localhost:1200.') );
require('http').createServer(function(req, res) {
console.log("req.headers['x-service-name'] = " + req.headers['x-service-name']);
console.log("Before req.url:"+ req.url);
if( req.headers['x-service-name'] != undefined){
let change = req.headers['x-service-name'] +req.url;
console.log("After req.url:"+ change);
req.url = change;
}
proxyJava.web(req, res, {
target: 'http://localhost:9001/'
});
}).listen(1200);
}
Property file inside Java Application that has feign clients
mbak-microservice1.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice1.ribbon.listOfServers: localhost:1200
mbak-microservice2.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-microservice2.ribbon.listOfServers: localhost:1200
mbak-document-store.ribbon.NIWSServerListClassName:com.netflix.loadbalancer.ConfigurationBasedServerList
mbak-document-store.ribbon.listOfServers: localhost:1200

Jersey Test Framework 2.5 - test POST method

I'm trying to find some manual how to test POST methods using jersey framework, only got examples for GET method.
Here's example:
#POST
#Path("add")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response addUser(JAXBElement<User> user) {
int code = userService.addUser(user.getValue());
if (code == 500) {
return Response.status(500).build();
}
return Response.status(code).entity(user).build();
}
Could you please post some POST method test example?
Thank you in advance.
After research I did it!
Here's my solution, it works just fine.
And it's rather integration test, but we can write unit tests in similar manner.
public class RestTest extends JerseyTest{
#Override
protected Application configure() {
return new Your_Resource_Config(); //Your resource config with registered classes
}
//#Before and/or #After for db preparing etc. - if you want integration tests
#Test
public void addUserTest() {
User user = new User();
user.setEmail("user2#mail.com");
user.setName("Jane Doe");
user.getUserRoles().getRoles().add("supertester");
Entity<User> userEntity = Entity.entity(user, MediaType.APPLICATION_XML_TYPE);
target("users/add").request().post(userEntity); //Here we send POST request
Response response = target("users/find").queryParam("email", "user2#mail.com").request().get(); //Here we send GET request for retrieving results
Assert.assertEquals("user2#mail.com", response.readEntity(User.class).getEmail());
}

Categories

Resources