Request is always invalid when using MessageBird RequestSigner - java

I'm not sure what I've done wrong. But the requestSigner.isMatch always returns invalid request.
I've used this https://github.com/messagebird/java-rest-api/blob/master/examples/src/main/java/ExampleRequestSignatureValidation.java as my reference but still same :(
public boolean isValidRequest(String signingKey, String timestamp, InputStream requestBody) throws IOException {
RequestSigner requestSigner = new RequestSigner(messageBirdSigningKey.getBytes());
byte[] bodyBytes = readAllBytes(requestBody);
Request request = new Request(timestamp, "", bodyBytes);
return requestSigner.isMatch(signingKey, request);
}
I pass an empty string for the queryParams since the incoming message has null queryParams.
The messageBirdSigningKey is the signing key provided by message bird.
Any leads would be a great help!
thank you!

Related

ResponseEntity does not accept return type "byte[]" but "ResponseEntity"

I am trying to send data from one server to another one via REST endpoint:
Server B starts this request:
String resource = "http://...";
ResponseEntity<byte[]> response = restTemplate.getForObject(resource, byte[].class, customerID);
Server A receives the request and returns the file:
ResponseEntity<byte[]> resp = new ResponseEntity<byte[]>(myByteArrayOutputStream.toByteArray(), HttpStatus.OK);
return resp;
But I get an error at the .getForObject line:
Required Type: ResponseEntity[]
Provided: byte[]
So obviously I would have to change the statement to:
byte[] response = restTemplate.getForObject(resource, byte[].class, customerID);
or to
ResponseEntity<byte[]> response = restTemplate.getForObject(resource, ResponseEntity.class, customerID);
Then the error is gone but I lose the HttpStatus.Ok message at the response? What would be the proper solution here?
Yes, the getForObject method does not provide access to response metadata like headers and the status code. If you have to access this data, then use RestTemplate#getForEntity(url, responseType, uriVariables)
The RestTemplate#getForEntity returns a wrapper ResponseEntity<T> so you can read the response metadata and also read the body through the getBody() method.
ResponseEntity<byte[]> response = restTemplate.getForEntity(resource, byte[].class, customerID);
if (response.getStatusCode() == HttpStatus.OK) {
byte[] responseContent = response.getBody();
// ...
} else {
// this is not very useful because for error codes (4xx and 5xx) the RestTemplate throws an Exception
}

Spring Boot: How to Add Params to TestRestTemplate.postForEntity?

I am trying to add parameters to a postForEntity request but it seems to never go through. Here is the minimum reproducible code:
#Test
public void test()
{
String urlTemplate = UriComponentsBuilder.fromHttpUrl("http://localhost:8080/test")
.queryParam("update")
// .queryParam("update", "{update}") //This does not work either
.encode()
.toUriString();
HashMap<String, String> paramValues = new HashMap<>();
paramValues.put("update", "true");
HttpEntity<AnimateRequest> httpEntity = new HttpEntity<>(null, new HttpHeaders());
ResponseEntity<Boolean> response = this.template.postForEntity(
urlTemplate,
httpEntity,
Boolean.class,
paramValues);
boolean bb = response.getBody();
}
In a controller:
#PostMapping(value = "/test")
public ResponseEntity<Boolean> tester(#RequestParam(name="update", required = false) boolean rr)
{
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(rr);
}
Errors with:
org.springframework.web.client.RestClientException: Error while extracting response for type [class java.lang.Boolean] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.Boolean` out of START_OBJECT token
at [Source: (PushbackInputStream); line: 1, column: 1]
I'm not exactly sure why, but the return contentType() in the controller return needed to be removed. For some reason Jackson was not parsing the return type properly. Then either a primitive boolean or the class Boolean works.
Main problem, is that your implementation tries to respond with text/plain without registering any Boolean to text/plain converter.
You have several options to solve that:
Just return (response with) the "default (media) type":
return ResponseEntity
.ok()
.body(rr);
If you need to respond with text/plain, then
a. ResponseEntity<String> would be the straight-forward solution:
#PostMapping(value = "/test2")
public ResponseEntity<String> // String!!! not Boolean ... {
return ResponseEntity
.ok()
.contentType(MediaType.TEXT_PLAIN) // explicit media type here or as #PostMapping.produces attribute
.body(String.valueOf(rr)); // convert the boolean here
}
b. Or to really register a custom(ized) (Boolean<->text/plain) converter ...
Then we could test 1. (with TestRestTemplate) like:
#Test
public void test1() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test/";
URI uri = new URI(baseUrl);
// true:
ResponseEntity<Boolean> result = this.restTemplate.postForEntity(uri + "?update=true", null /*here goes normally the "post body"/entity*/, Boolean.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(result.getBody()).isTrue();
}
and 2. accordingly with string result:
#Test
public void test2() throws URISyntaxException {
final String baseUrl = "http://localhost:" + randomServerPort + "/test2/";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = this.restTemplate.postForEntity(uri + "?update=true", null, String.class);
assertThat(result.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
assertThat(Boolean.valueOf(result.getBody())).isTrue();
}
Please consider, we have several (out-of-the-box) options regarding "content encoding" and "how to pass this update parameter".
For brevity, simplicity and lack of need I omit any post objects and headers (null, which would go as the second method argument), and passed the only parameter as "URI parameter".
Also consider the note on RestTemplate, which could also be applied to TerstRestTemplate:
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

How to set a variable endpoint to my URL for HTTP GET method?

I would like to make the endpoint of the URL in that method a variable one:
public User fetchUser() throws IOException {
URL url = new URL("https://api.github.com/users/octocat");
InputStreamReader reader = new InputStreamReader(url.openStream());
User user = new Gson().fromJson(reader, User.class);
if (user == null) {
logger.error("Could not return desired output.");
return null;
} else {
logger.info("The output returned.");
return user;
}
}
Modifying it to this does not solve the case (changed 'octocat' into '{endPoint}'):
public User fetchUser(#PathVariable String endPoint) throws IOException {
URL url = new URL("https://api.github.com/users/{endPoint}");
This is the GET method from my RestController:
#GetMapping("/user/info/{login}")
public User getUser(#PathVariable String login) throws IOException {
return userService.fetchUser();
}
The browser returns this message:
There was an unexpected error (type=Internal Server Error, status=500).
https://api.github.com/users/{endPoint}
Also, if I modify my URL to this:
URL url = new URL("https://api.github.com/users");
Then the response is this:
There was an unexpected error (type=Internal Server Error, status=500).
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
Help, please.
For your first exception, you could try using string concatenation to append the endpoint to the URL and see if the connection opens that way:
URL url = new URL("https://api.github.com/users/" + endpoint);
For your second exception, it looks like you're telling GSON you have an object of type User when you actually have an array of some sort. You may have to change the Type parameter in fromJson() so that gson can deserialize the json properly. Here is an example of deserializing arrays in gson.
Since I see you're using spring-web and this looks like a RESTful API, I would also suggest configuring a RestTemplate Bean and injecting it into your service to make the request out to github rather than using java.net.url. You can find a nice guide on spring.io for how that works.

Consume Post method Rest + SpringBoot

I have been searching all morning and i think i'm missing something .
i have a Spring boot controller with a method to save a client.
this is the method :
// ajouter un client
#RequestMapping(value="/AjoutClient/{clientData}", method=RequestMethod.POST)
public String AjoutClient(#PathVariable String clientData) {
Client c = new Client();
c.setNomClient(clientData.split(";")[0]);
c.setPrenomClient(clientData.split(";")[1]);
c.setAdresseClient(clientData.split(";")[2]);
c.setTelClient(clientData.split(";")[3]);
c.setEmailClient(clientData.split(";")[4]);
c.setCinClient(clientData.split(";")[5]);
client.save(c);
return "test";
}
i want to consume this method from another application with this method :
#RequestMapping(value="/ajoutClient", method=RequestMethod.POST)
public void ajout(#RequestParam("nom") String nom,#RequestParam("prenom") String prenom,#RequestParam("adr") String adr,#RequestParam("tel") String tel,#RequestParam("mail") String mail,#RequestParam("cin") String cin) {
String ClientData=nom+";"+prenom+";"+adr+";"+tel+";"+mail+";"+cin;
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request = new HttpEntity<>(new String(ClientData));
ResponseEntity<String> response = restTemplate
.exchange("http://localhost:9093/AjoutClient/"+ClientData, HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
}
** explication : i get the values from a form and construct a string with those values, then try to send that string to my clientController.
PS: i can't send client object, i have to send the values one by one then create the client object in the clientController.
i'm feeling pretty lost because i can see that something is wrong but i don't know what is it.
First of all I would suggest you avoid using #PathVariable for passing the data like this.
You're already sending everything in the request body, so first step is to change:
public String AjoutClient(#PathVariable String clientData) {
to
public String AjoutClient(#RequestBody String clientData) {
and
restTemplate.exchange("http://localhost:9093/AjoutClient/" + ClientData, HttpMethod.POST, request, String.class);
to just
restTemplate.exchange("http://localhost:9093/AjoutClient", HttpMethod.POST, request, String.class);
Then if you're expecting 201 status then you have to return it:
public ResponseEntity<String> AjoutClient(#RequestBody String clientData) {
...
return ResponseEntity.created(null).body("test");
}
PS: Please pay attention to what #JB Nizet mentioned, cause he has a point here. Just research that keywords (google them) or read some tutorials e.g https://www.baeldung.com/java-url-encoding-decoding or https://www.baeldung.com/rest-template and you'll easily find out more about standard practices.

Validating signed request: signature_invalid

I'm trying to validate an OpenSocial 0.7 signed request, using the sample Java code on that page. I think it should work this way, but I still get a signature_invalid error.
Main validation code:
// NOTE: req = HttpServletRequest
// check for hyves
if (!"hyves.nl".equals(req.getParameter("oauth_consumer_key"))) {
throw new RuntimeException("Only hyves supported");
}
// update hyves' certificate
getHyvesCert(req.getParameter("xoauth_signature_publickey"));
// construct message object
OAuthMessage oaMessage = new OAuthMessage(req.getMethod(), getRequestUrl(req), getParameters(req));
// validate message
// (will throw exception if invalid)
new SimpleOAuthValidator().validateMessage(oaMessage, new OAuthAccessor(OAUTH_CONSUMER_HYVES));
OAUTH_CONSUMER_HYVES:
private static final OAuthServiceProvider OAUTH_THIS = new OAuthServiceProvider(null, null, null);
private static final OAuthConsumer OAUTH_CONSUMER_HYVES = new OAuthConsumer(null, "hyves.nl", null, OAUTH_THIS);
getHyvesCert:
public void getHyvesCert(String name) {
synchronized(certLoadLock) {
// in reality this is code that downloads the certificate
// with the specified name, but this is the result
hyvesCert = "---BEGIN CERTIFICATE---- etc...";
OAUTH_CONSUMER_HYVES.setProperty(RSA_SHA1.X509_CERTIFICATE, hyvesCert);
}
}
The methods getRequestUrl and getParameters are directly copied from here.
I found the problem. getRequestUrl() returned the wrong URL because Tomcat is behind an nginx proxy. So while the sender would use the URL "http://example.com/bla" to sign the request, the server was using "http://example.com:8080/bla" to validate it.

Categories

Resources