What is wrong with my PACT Test and how to fix it? - java

I am writing a Pact Consumer and Provider Test .
Basically I have 3 problems at the moment.
#Pact(provider = PROVIDER, consumer = CONSUMER)
public RequestResponsePact getAddress(PactDslWithProvider builder) {
PactDslJsonBody body = new PactDslJsonBody()
.stringType("key1", "73501")
.stringType("key2", "value");
final Map<String,String> headers = new HashMap<>();
headers.put("Authorization","Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1");
return builder
.given("Something.")
.uponReceiving("Dto")
.path("/amz/ags")
.query("code=9999")
.method("GET")
.headers(headers)
.willRespondWith()
.status(200)
.body(body)
.toPact();
}
1) If I add a header in my Consumer Test I get a 500 status Error back
2) As you just saw in the Consumer I tried to add Authorisation Header , but in the Provider it will be expired, so I have to find a way to hack something and provide a valid token.
3) In the provider REST Api other services are getting called but it seems I have to mock them at least whenever they are called an Exception gets thrown. Honestly I also don't know how to do this. In which method I need to do it. Why at all do I have to mock them because the external Apis are running.
for 2) I found a solution
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context, HttpRequest httpRequest) {
httpRequest.addHeader("Authorization", "Bearer " + "eyJ0eXAiOiJKV1Qi");
context.verifyInteraction();
}
But now I get in context.verifyInteraction() an Exception. Very strange.

I can't answer the JVM specific question, however.
If I add a header in my Consumer Test I get a 500 status Error back
Usually, this means that you're told Pact you are going to do something but did not do it. In this case, my guess is the bearer token didn't match or it didn't receive the correct header. There should be logs or a junit report with the details.
As you just saw in the Consumer I tried to add Authorisation Header , but in the Provider it will be expired, so I have to find a way to hack something and provide a valid token.
For dealing with authentication/authorization, you may want to read the strategies here:
https://docs.pact.io/provider/handling_auth
In the provider REST Api other services are getting called but it seems I have to mock them at least whenever they are called an Exception gets thrown. Honestly I also don't know how to do this. In which method I need to do it. Why at all do I have to mock them because the external Apis are running.
Pact is intended to be closer to a unit test, running external services during Pact tests is not recommended, because it makes tests less deterministic. See also this section which discusses the provider test coverage: https://docs.pact.io/5-minute-getting-started-guide#scope-of-a-provider-pact-test.

On the question of how to handle token expiry, I did like this.
The given:
JWT token contains an expire_at claim
I wrote a small library to share within all the projects where I have a class like JwtTestHelper which generates any JWT with token started_at date as a constant date and expire_at is started_at plus like 100 years.
It's important here to make it generate the same token for the same input parameters from run-to-run or the header will be different which will cause a new pact to be considered by the broker. This dramatically changes the approach - you basically need to verify every new pact each time.
On the question of handling exceptions. Do not cheat like this
httpRequest.addHeader("Authorization", "Bearer " + "eyJ0eXAiOiJKV1Qi");
Give your client class code a chance to pass the values naturally, pass it somehow or mock using the same JwtTestHelper

Related

How to short-circuit from Spring WebGraphQlInterceptor without executing controller handler?

I have a Spring WebFlux application and am trying to write a WebGraphQlInterceptor to enforce authorization. The authorization requires access to HTTP headers and GraphQL variables, both of which are easily accessible from a WebGraphQlInterceptor. However, if the request fails authorization, I do not want to execute the controller handler and instead exit early with an error response. I have the custom error response working OK, but I cannot figure out how to bypass the controller -- it seems like I'm required to proceed down the original chain, execute the controller, and only then return the error response.
I'm hoping there's an easy solution I'm missing. The documentation seems sparse on this topic with very few examples online. The WebGraphQlInterceptor documentation lists some methods like apply() that sound like they might be helpful for altering the chain, but it's not clear how to use them. The interceptor interface requires a WebGraphQlResponse to be returned, and I can't find a way to return it without continuing down the original chain with chain.next(request) below:
#Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
return chain.next(request).map(response -> {
// Custom logic...
});
}
I also tried constructing my own custom WebGraphQlResponse from scratch to return from the interceptor, but this felt like a hack and a lot of unnecessary overhead.
I don't want the controller to be executed at all if authorization fails. Is this even possible with the WebGraphQlInterceptor? And what would the simplest implementation look like?
P.S. The WebFilter doesn't help me here because I need easy access to GraphQL variables, which isn't possible with WebFilters.

Is it possible to include a request body in a GET request using Spring WebClient?

I know sending a body with a GET request isn't the best idea but I'm trying to consume an existing API which requires it.
Sending a body with POST is straight-forward:
webClient.post()
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
It won't work with webClient.get() though, because while the post() method returns a WebClient.RequestBodyUriSpec, the get() method returns WebClient.RequestHeadersUriSpec<?>, which doesn't seem to allow any body definitions.
I've found a workaround for Spring RestTemplate here: RestTemplate get with body,
but had no luck finding any for the new WebClient.
While the other responses are correct that you shouldn't use a body with a GET request, that is not helpful when you do not own, or cannot change the already existing method you are calling.
The problems is WebClient#get returns a WebClient.RequestHeadersUriSpec which does not provide a way for us to set the body.
WebClient#post returns a WebClient.RequestBodyUriSpec which does provide us a way to set the body but will cause us to use the wrong HTTP method, POST instead of GET.
Thankfully for us stuck in this situation there is WebClient#method which returns a WebClient.RequestBodyUriSpec and allows us to set the HTTP method.
webClient.method(HttpMethod.GET)
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
You may still run into issues in your testing libraries though...
A GET reques has no body. It is forbidden (well, not forbidden, but not used at all) by the HTTP specification. You have two approaches here:
Do a POST. It is there just for that.
Use a query string and pass the data in that part of the URL.
Of course, you can attach the needed fields and pass a payload to the GET request, but it will probably be ignored, or worse, identified as an error and rejected by the server, before your served code has access to it. But if you are passing data to the server to do some processing with it, then POST is what you need to use.
Extracted from RFC-7231. HTTP 1.1. Semantics and code:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
(markup is mine)
Reasons for this are, mainly, that a GET method must be idempotent, producing the same output for the same URL, if repeated. POST doesn't have these requirements, so POST is your friend.

Problems with WebClient returning empty response

I am working on a project which until now has been using the org.springframework.web.client.RestTemplate
I have recently understood that this class is to be deprecated in favour of the Asynch org.springframework.web.reactive.function.client.WebClient framework.
Now I am massively in favour of this, as my application is suffering from long delays on waiting from
responses from RestTemplate (GET) calls (in which time I could be doing database stuff etc.).
The problem that I have now is that if I make a call like:
final Mono<String> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
when I make a subsequent call like:
System.out.println(call.block());
I get the expected output (a String version of a populated Json Object).
however if I change the earlier call to (which I want to do!):
final Mono<JsonObject> call = webClient
.get()
.uri("/base/recordPath/1?format=json")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(JsonObject.class)
when I do:
System.out.println(call.block());
I just get {} instead of a populated JsonObject
So it looks like the bodyToMono() hasn't done what 'I' expected
When I used RestTemplate, there was a way to register serialisers with the Template (though this wasn't necessary for JsonObject),
is this also necessary with WebClient? If so how do you do it?
I would be grateful of help.
Thanks
Bill
N.B. I'm not sure if this has any relevance, but the rest endpoint I am accessing does have an IP restriction, so if in some way the WebClient were to alter the originating IP, this may have some effect. Though I would have thought it would be more like a 4** of some sort, I'm not seeing any of those!
Or possibly a Media type issue, as it is going through a DMZ, where the guardian may be changing an 'unauthorised' request from application/json to text/* for example.
Another point which may have relevance is that to get a successful start of the application it is necessary to run it with the following property:
spring.main.web-application-type=none
Update
I now have my application running, though not as I want!
The issue appeared to be transitive dependencies imported by the team pom I am required to use (as a parent pom).
I now get a successful start of the project. But still find that the json Object (which is now Jackson) is still Empty (as reported by object.isEmpty()).
my dependencies/versions now are:
org.springframework:5.2.8.RELEASE
org.springframework.boot:2.3.3.RELEASE
com.fasterxml.jackson:2.11.2
I know I am fighting a parent pom which is against what am trying to do but would like to know what the dependencies I really need are

How to authenticate MicroProfile REST Client calls with OAuth?

In my server application, I want to consume some third party API using a MicroProfile REST client. To do so, I need to send an Authorization Header with a bearer token.
I don't want to always get a token before I make any call so I need a mechanism to only retrieve a new token if there is no token yet or if the token expired. The token could then be stored and used in each call until it expires. The next call to the API which would cause a HTTP 401 Unauthorized shall then cause a new token to be obtained.
Unfortunately so far I wasn't able to find any resources on how to consume OAuth secured APIs using the MicroProfile REST client. I hope anybody can give me any tips. I'm using Kotlin and Quarkus but Java related documentation would be fine as well. Anything helps.
Here is my rather simple client:
#RegisterRestClient
#Produces(MediaType.APPLICATION_JSON)
interface SomeThirdPartyApiClient {
#POST
#Path("/some/random/url")
fun someRandomUrl(body: SomeJsonRequestObject, #HeaderParam("Authorization") bearer: String): SomeJsonResponseObject
}
As discussed with iabughosh, there seems to be no automatic way of doing what I want to do. Instead I have written the code myself as suggested by iabughosh. I went with the route of catching errors in the call. If the error has a 401 status, then I retrieve a new token and retry the call.
When the application starts and has no token yet, the first call always causes a 401 and then I get the first token. The next 401 appears only when the token expires (or was removed by a server admin prematurely) so then I simply get the token and do the call again.
As for now this seems to work just fine. I'll have to see how it turns out in production when there are a lot of (parallel) calls. If I find a better solution, I'll try to remember this question and update it accordingly.
There isn't any way to pass it at annotation level, through eclipse microprofile configuration, the only way to pass a dynamic token is by adding
#HeadParameter("Authorization") authString
in your rest call, in case you are using jwt, usually you can inject the JsonWebToken and do all the checks with this object, so you wouldn't need that parameter, however, you can add it and just ignore, than in your rest client method declaration you have to add it too (as I seen your case you did it already, just assure the order of parameters is the same), and the restclient will be able to pass the token though the header (you need to pass "Bearer "+tokenString), but you need to access to the code of your rest service.

SSO using spring-security-oauth2 : Authentication Code never read

Using :
spring-security 3.2.5
spring-security-oauth 2.0.7
I have a working oauth2 provider built with spring-security-oauth (oauth2).
I have my client configured in it to use authorization_code grant type.
The provider works perfectly :
Testing with curl, I can get an authorization code and exchange it for an access token.
So on the service provider part, all is fine.
Now I'm trying to implements the client application, also with spring-security-oauth.
I'm using xml configuration, strongly based on the example here, but using my own provider (mentionned above) instead of google.
When I make a call to a protected resource on my client, the OAuth2ClientAuthenticationProcessingFilter tries to obtain an access token, so it redirect to my service provider. That one force the user to log in, as expected, and then redirect him to the configured redirect_uri (the redirect uri is the one configured for my OAuth2ClientAuthenticationProcessingFilter : something like http://myClient/context/external/login).
The problem is : the client never read the authorization code in the request returned from the service provider. So the OAuth2ClientAuthenticationProcessingFilter restarts the flow, asking for an authorization code.
I've been able to make it work by modifying the OAuth2ClientAuthenticationProcessingFilter to read the request and set the authorization code in the AccessTokenRequest. Here is the snippet :
OAuth2AccessToken accessToken;
try {
String code = request.getParameter("code");
if(code != null) {
restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code);
}
accessToken = restTemplate.getAccessToken();
...
Before trying this, I tried to make a "call hierarchy" on the method org.springframework.security.oauth2.client.token.AccessTokenRequest.setAuthorizationCode(), to find where in the code spring does call the method, but it returned nothing.
Is that a bug ?
I really would like not to have to replace the OAuth2ClientAuthenticationProcessingFilter with my own.
Does someone made it work ( in that version or another) ?
Update
It's the setAuthorizationCode() method that is never called (error in my initial question). But I digged a little more and I realized this is not the problem.
I can assert that the OAuth2ClientContextFilter is called before OAuth2ClientAuthenticationProcessingFilter (I checked that with a debugger).
What I found, but don't know if it is normal :
The default constructor of DefaultAccessTokenRequest is only called once : at the application startup. The other constructor (the one taking the parameter's map), is never called. Since I've seen in RestTemplateBeanDefinitionParser that the access token request is scoped 'request', I would expect the constructor taking the parameter's map to be called on each new http request to my client application.
In the RestTemplateBeanDefinitionParser :
BeanDefinitionBuilder request = BeanDefinitionBuilder.genericBeanDefinition(DefaultAccessTokenRequest.class);
request.setScope("request");
request.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
request.addConstructorArgValue("#{request.parameterMap}");
request.addPropertyValue("currentUri", "#{request.getAttribute('currentUri')}");
That can explain my problem with the authorization code never read from the request. The hack I mentionned in my initial question just pushed back the problem. Now I get csrf protection errors because the AccessTokenRequest always remembers some stateKey when I presume it does not need anymore once I get the access token.
Again, maybe I just misunderstand the hole think, so feel free to tell me :)
I did not post my configuration because it's pretty the same as that one here.
You need an OAuth2ClientContextFilter and it needs to fire before the authentication processing filter (it basically does that stuff you have in your custom filter). I can't tell from the code you posted if you have one and it isn't firing or you don't have one.
Sorry for all of you that spent precious time trying to help me. I was so focused debugging that I missed a configuration problem.
Do never configure Oauth2RestTemplate like this :
<beans:bean id="myRestTemplate" class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<beans:constructor-arg ref="myResourceId"/>
</beans:bean>
That explain why the DefaultAccessTokenRequest was not request scoped, hence it's default controller called instead of the one taking request's parameter map.
Don't do like me and use the xml namespace ! :
<oauth:rest-template id="myRestTemplate" resource="myResourceId"/>
Still wondering why I've done that :P

Categories

Resources