How do you unit-test controllers that oauth? - java

I like Spring MVC because you can unit test your controllers.
But testing controllers that oauth is another thing. For instance if I want to get the authorization url because I want to Oauth to GData, I would have to deploy the web-app because Google will only accept authorization requests from my domain (the url of my web app), not my development environment whose domain is localhost:8080.
So right now the only way I am testing if my code works is deploying the code and printing out the data that I need to have printed.
My Controller, which is a multi-action controller
public ModelAndView authorize(HttpServletRequest request,
HttpServletResponse response) {
Provider provider = getProvider(request.getAttribute("provider"));
String authUrl = provider.getAuthUrl();
page.put("authUrl", authUrl);
return new ModelAndView("setup","model",page);
}
The provider code, all my dependencies are injected
public String getAuthUrl()
{
oAuthParameters.setScope("http://docs.google.com/feeds/");
try {
oAuthHelper.getUnauthorizedRequestToken(oAuthParameters);
} catch (OAuthException e) {
page.put("authUrl", CANNOT_CONNECT_TO_GOOGLE);
}
String oAuth_Callback="[callback url]";
try {
oAuth_Callback.concat("?oauth_token_secret=").concat(
java.net.URLEncoder.encode
(oAuthParameters.getOAuthTokenSecret(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
page.put("authUrl",INTERNAL_ERROR);
}
oAuthParameters.setOAuthCallback(oAuth_Callback);
String authUrl = oAuthHelper.createUserAuthorizationUrl(oAuthParameters);
return authUrl;
}

It sounds like you have one component (a controller) doing multiple things.
I would break this into
The controller
The OAuth service that communicates with Google
The latter should be injected into your controller, as with just about everything else in Spring.
This allows you, in a unit test, to mock out how your controller behaves when the OAuth component returns different values.
For actually testing integration with Google, you could do two things:
Unit testing of the service that parses the Google OAuth response - mock out the code that does the actual message transport so that you can test how your message parser behaves when google returns a certain type of XML (I'm assuming this is done with XML, but same principle applies regardless of technology) vs another type.
Actual integration tests of the component that sends and receives to google - this might be harder because of the limitations you mentioned.
So, even if they restrict access to certain domains, then you can unit test most of the pieces of the puzzle, and hopefully only have one small segment that has to be "in the wild" to be tested.
Or, could you register a different account for a domain in your test environment? Either way, you should still break up this code into smaller components.

Related

Lost authentication when using GraphQL subscriptions with Spring Security

I'm using spring-boot-starter-graphql for my GraphQL server application. I protected the application with basic authentication using Spring security.
GraphQL subscription are used to return a type with a subtype. As soon as subscription returns a type, authentication gets lost from security context.
Here is short version of my configuration:
type Post {
id: String!
title: String!
author: Author!
}
type Author {
id: String!
name: String!
}
type Subscription {
getNewPost: Post
}
#SchemaMapping(typeName = "Post", field = "author")
public Author getAuthor(Post post) {
return this.appService.getAuthorById(post.getAuthorId());
}
#Bean
Many<Post> publisher() {
return Sinks.many().multicast().directBestEffort();
}
#SubscriptionMapping("getNewPost")
public Publisher<Post> getNewPost() {
return this.publisher.asFlux();
}
public Post createPost(CreatePostInput postInput) {
...
this.publisher.tryEmitNext(post);
...
}
Everything works as expected until subscription returns first object. After that security context becomes empty and any following request gets redirected to login page.
I noticed two things:
In case that object that subscription returns (Post in this example) doesn't contain nested object (Author), everything works as it should
With Java Kickstart everything works as it should
I created an example application for reproducing this issue: https://github.com/stojsavljevic/graphql-security-issue
Any help is very much appreciated!
According to Rossen Stoyanchev's presentation at Spring IO last May (which you can watch here), Spring for GraphQL has a layered architecture that has a Web transport layer at the top (be it HTTP or Websocket), then comes the GraphQL engine, and then the Data Controllers.
It's at this last level that we find the Components, like a mutation or a query service, or also one of this "service" operations invoked by a Controller method.
Spring for GraphQL makes sure that the Security context (e.g. the SecurityFilterChain) gets propagated through the GraphQL engine layer and remains available.
For that to work in your example, according to what I saw in your Github repository I believe you should use a #Secured("ROLE_ADMIN") annotation in your methods.
#Secured("ROLE_ADMIN")
public Post createPost(CreatePostInput postInput) {
Post post = new Post("1", postInput.getTitle(), postInput.getAuthorId());
// not actually saving anything
// publish newly created Post
this.publisher.tryEmitNext(post);
return post;
}
Make sure to enable method level security as well.
Hope it helps.
Note: For the Security part of Rossen's talk go to 24:05 onwards.

Mocking REST API calls with SpringBoot profiles

I have recently started out with Spring and am unsure about how to approach this issue. I have a Spring boot program which makes calls to remote REST APIs. For example an AddressService class with getAddress(String user) method, which makes a HTTP call and returns a JSON response. I would like to set up Spring profiles for development purposes local, dev, uat, prod.
When the program is running with the local profile, I would like to "mock" these external API calls with an expected JSON response, so I can just test logic, but when it is run in any of the other profiles I would like to make the actual calls. How can I go about doing this? From what I read, there's many ways people approach this, using WireMock, RestTemplate, Mockito etc. I'm confused about which is the way to go.
Any advice would be greatly appreciated. Thanks.
WireMock,Mockit is for unittest, to mock the real request. Example here:
How do I mock a REST template exchange?
When you need a running implementation with a mock, i think the easiest way is that you have a interface
public interface AdressAdapter {
public List<Adress> getAddress(String name);
}
And two different implementations depending on the profile.
#Profile("local")
public class DummyAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Mock here something
return null;
}
}
! means NOT locale profile in this case.
#Profile("!local")
public class RealAdress implements AdressAdapter{
#Override
public List<Adress> getAddress(String name) {
//Make Restcall
return null;
}
}
What you could do is use different application.properties files depending on your profile. That way, you just change the url to a mock server for your local profile.
So what you have to do is :
Create another application.properties in your resources folder named : application-local.properties.
Change the url of the desired service.
Start your application with the VM option -Dspring.profiles.active=local.
Here is a link that describe well what you want to achieve.
For your mock server, you could use Wiremock, Mountebank, Postman,... that can be start separately and mock specific endpoints to return what you want.

How to start testing a RESTful web service using Java ? [duplicate]

This question already has an answer here:
How to invoke a REST service
(1 answer)
Closed 7 years ago.
I have this web service http://qa-takehome-creyzna.dev.aetion.com:4440 that I would like to test. I have the authentication details (username and password) and the service has the following end points: /login, /user/, /user/{id} and /user/search. For all endpoints other than /login an authorization token needs to be passed as an HTTP Header.
The service exposes a login endpoint ( /login) which takes a POST request with two parameters: username and password. Following a successful login, an authentication token will be returned which must be used to make additional requests to the service. For example,if the request is as following,
{
"username": "admin",
"password": "admin"
}
It may return { "token": "1234-0009-999" } and this token will required for making additional request.
I will need to authenticate the web service, create 10 users and then, retrieve that information to validate the users was created correctly. I would like to develop a test plan and implement in Eclipse. How can I get started ?
A web service is basically an extension of the Java Servlet, where the input is processed a bit more and the output is rarely an HTML page.
Netbeans has an excellent tutorial on how to stand up a web service, and if you follow it, you can have a basic web service running within the hour.
https://netbeans.org/features/java-on-server/web-services.html
Don't be fooled by thinking that you must use one IDE (I like netbeans, but others don't) or another. The fancy GUI tools are just writing plain Java classes that might use other plain Java facilities (like JAXB if using XML, etc).
A web service is not much more than a web server that accepts particular kinds of requests, and responds with particular kinds of responses. In Java, web servers are made easier to use by leveraging Servlets. The internal contents of the Servlet will look like
Unpack the request
Validate the request is complete, report an error response if not
Act on the reqeust
Generate a response in the appropriate format
Send the response back as the reply.
--- Edited in response to request ---
Sorry, It seemed too obvious to me. Let me fill in the gaps. Sorry for glossing over the details.
public class MockHttpServletRequest implements HttpServletRequest {
#Override
public String getAuthType() {
throw new UnsupportedOpertationException("unexpected method use");
}
#Override
public String getContextPath() {
throw new UnsupportedOpertationException("unexpected method use");
}
... repeat for all methods ....
}
public class ItemRequestWithBadEncoding extends MockHttpServletRequest {
#Override
public String getMethod() {
return "GET";
}
#Override
public String getHeader(String name) {
if ("content-type".equals(name)) {
return "text/plain-ish"; // this is not a mime-type
}
throw new IllegalArgumentException(String.format("this mock doesn't support %s", name);
}
... fill out the rest of the required request details ...
}
public class CapturingServletResponse implements HttpServletRespose {
private final ArrayList<Cookie> cookies = new ArrayList<Cookie>();
#Override
public void addCookie(Cookie cookie) {
cookies.add(cookie);
}
public List<Cookie> getCookies() {
return Collections.unmodifiableList(cookies);
}
... override other methods and capture them into per-instance fields
with ability to return unmodifiable references or copies to them ...
}
Now back in the testing framework
#Test
public void testItemFetch() {
try {
MockRequest request= ItemRequestWithBadEncoding();
CapturingServletResponse response = new CapturingServletResponse();
Servlet itemRequestServlet = new ItemRequestServlet();
itemRequestServlet.service(request, response);
Assert.assertEquals("unexpected cookies in response", 0, response.getCookies().size());
... other asssertations ....
} catch (Exception e) {
Assert.assertFail(String.format("unexpected exception: %s", e.getMessage());
}
}
Depending on what items you care about, and how much work you need to put into it, you can then flesh out the needed parts of the capture and perhaps parameterize and refine the way you construct your input handling.
Look into spring frameworks.
They go well with other testing frameworks like Mockito and Junits.
Use something like SpringMVCTest or SpringRestAssured, note RestAssured would let you write integration tests.
Read this and this

Unit testing Spring MVC app with tests based on actual View DOM

We have a Spring MVC webapp, and we're currently able to unit test the controllers with MockHttpServletRequest objects.
Our tests look similar to:
request = new MockHttpServletRequest(_mockServletContext, "PUT", "/createNewUser.action");
request.setParameter("username", "bob123");
response = new MockHttpServletResponse();
userController.createNewUser(request, response);
// Ensure the user was created, etc.
Then, we query the underlying database (and other controllers) to ensure that things worked properly.
However, this does not test the actual JSP pages that we're shipping. Just the fair-weather mocked request.
What we would like to accomplish, is being able to mock the entire process from the perspective of an actual user viewing the page in a browser, like:
request = new MockHttpServletRequest(_mockServletContext, "GET", "/newUser.html");
response = new MockHttpServletResponse();
userController.newUser(request, response);
//Pseudo-code!
DOM thePage = SomeBuilder.fromResponse(response);
thePage.getTextField("usernameField").setText("bob123");
thePage.getButton("submitUserButton").click();
// Then, we test the same assertions
...
This helps us establish unit tests based on the view as the client will actually see it, and would involve ensuring that the proper HTML components exist, and the Javascript backing the button click is also sending the proper parameters to the server/controller, in the event it someday requires a user's first name, for example. Or, in the case there is a typo and the HTML form actually sends the parameter "userUsername" instead of "username". We'd like to remove the manual testing requirement in this case, as there are different code paths/form layouts depending on the role of the currently-authenticated user.
Are there Java/Spring MVC based unit testing frameworks to accomplish this task? Or other code patterns that would provide similar reliability?

GWT RequestFactory client scenarios

My understanding is that the GWT RequestFactory (RF) API is for building data-oriented services whereby a client-side entity can communicate directly with it's server-side DAO.
My understanding is that when you fire a RF method from the client-side, a RequestFactoryServlet living on the server is what first receives the request. This servlet acts like a DispatchServlet and routes the request on to the correct service, which is tied to a single entity (model) in the data store.
I'm used to writing servlets that might pass the request on to some business logic (like an EJB), and then compute some response to send back. This might be a JSP view, some complicated JSON (Jackson) object, or anything else.
In all the RF examples, I see no such existence of these servlets, and I'm wondering if they even exist in GWT-RF land. If the RequestFactoryServlet is automagically routing requests to the correct DAO and method, and the DAO method is what is returned in the response, then I can see a scenario where GWT RF doesn't even utilize traditional servlets. (1) Is this the case?
Regardless, there are times in my GWT application where I want to hit a specific url, such as http://www.example.com?foo=bar. (2) Can I use RF for this, and if so, how?
I think if I could see two specific examples, side-by-side of GWT RF in action, I'd be able to connect all the dots:
Scenario #1 : I have a Person entity with methods like isHappy(), isSad(), etc. that would require interaction with a server-side DAO; and
Scenario #2 : I want to fire an HTTP request to http://www.example.com?foo=bar and manually inspect the HTTP response
If it's possible to accomplish both with the RF API, that would be my first preference. If the latter scenario can't be accomplished with RF, then please explain why and what is the GWT-preferred alternative. Thanks in advance!
1.- Request factory not only works for Entities but Services, so you could define any service in server-side with methods which you call from client. Of course when you use RF services they are able to deal with certain types (primitive, boxed primitives, sets, lists and RF proxies)
#Service(value=RfService.class, locator=RfServiceLocator.class)
public interface TwService extends RequestContext {
Request<String> parse(String value);
}
public class RfService {
public String parse(String value) {
return value.replace("a", "b");
}
2.- RF is not thought to receive other message payloads than the RF servlet produces, and the most you can do in client side with RF is ask for that services hosted in a different site (when you deploy your server and client sides in different hosts).
You can use other mechanisms in gwt world to get data from other urls, take a look to gwtquery Ajax and data-binding or this article

Categories