Karate Framework- AWS Auth throwing error "the request signature we calculated does not match the signature you provided" [duplicate] - aws-api-gateway

First of all, thanks for build karate it's a very useful for test API's and UI's. We are using it to test a lot of our endpoints but we would like to know if there is a way or which is the best approach to handle requests with signature as part of the request in the header.
In our case we have two headers:
ApiKey: this value is always the same
Signature: this value depends on the request body content
Is there any way to inject the signature value just before the request is executed based on the request body content?
Here you can see two samples of the requests
Sample 1:
* url 'https://dev.sample.com'
* path '/api/user/getAll'
* header Content-Type = 'application/json'
* header ApiKey = 'XXX'
* header Signature = 'YYY'
And request { }
When method POST
Then status 200
Sample 2:
* url 'https://dev.sample.com'
* path '/api/user/getAll'
* header Content-Type = 'application/json'
* header ApiKey = 'XXX'
* header Signature = 'ZZZ'
And request { name: 'John' }
When method POST
Then status 200
Thanks

Karate has a "hook" for generating headers, but as of now it is not "aware" of the currently built request body + headers: https://github.com/intuit/karate#configure-headers
We got a similar request here, and are thinking of adding this capability: How to retrieve raw request contents before making a REST call in Karate DSL?
Maybe the OAuth examples will give you the way forward for your case for now: https://stackoverflow.com/a/55055111/143475
Feel free to raise an enhancement request, and we can get this in to the next version (with your help to test it). I'm thinking - what if you are able to call karate.get('request') from within the header JS function.
But for now all you need to do is do something like this:
* def body = { some: 'json' }
* karate.set('requestBody', body)
* url someUrl
* request body
* method post
And in the header.js function
function fn() {
var body = karate.get('requestBody');
var sign = Utils.sign(body);
return { Signature: sign };
}
EDIT: this will be implemented in Karate 1.0 onwards: https://github.com/intuit/karate/issues/1385

Related

How to technically send the query parameters to google cloud REST API?

I'm trying to call this REST API
How is one expected to add these params? maxResult
Page token
all
filter
How do I technically send the query parameters?
What part of the payload or options?
I couldn't find an example.
/**
* Checks if dataset already exists in project.
*
* #return {boolean} Returns true if dataset already exists.
*/
function datasetExists() {
// Get a list of all datasets in project.
var url =
'https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}/datasets';
var options = {
method: 'GET',
contentType: 'application/json',
payload: ""
};
var response = authUrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
Logger.log(result);
if (result.entities) {
Logger.log('Dataset with ID = %s created.', dataSet.id);
// return a list of identified entities
return result.entities;
}
All the Google Cloud API use the same definition pattern.
Path parameter, which is in the path of the url, like that
https://bigquery.googleapis.com/xxx/yyy/<pathparameters>/zzz
Query parameters that come in the URL, but at the end, after a ? and separated with &. Most of the time it's tokenPage, pageSize, filters,...
https://bigquery.googleapis.com/xxx/yyy/<pathparameters>/zzz?queryparam1=value1&queryparam2=value2
The body to provide when you perform a POST, PUT, PATCH, DELETE. in JSON (therefore you need to add the content type header when you use it).
Finally, in term of security, the APIs requires a Bearer Access Token in the Authorization header

Calling API from Saleforce is giving error code 500

I have a REST API to callout from Salesforce.
The authorization of the API is through access token.
I am able to get the access token through POST request in Salesforce. Also tested from Postman through that token and able to get a successful response.
I am using the below code to callout the API using the access token:
String endpoint_x = '*****';//Putting my endpoint here
Http httpObject;
HttpResponse response;
String accessToken;
accessToken = MyUtilityClass.getAccessToken();
jsonBody = json.serializePretty('', true);//Yes, My JSON is empty
HttpRequest request = new HttpRequest();
request.setEndpoint(endpoint_x);
request.setHeader('Authorization', 'Bearer '+accessToken);
request.setMethod('POST');
request.setBody(jsonBody);
httpObject = new Http();
response = httpObject.send(request);
System.debug('Response=' + response);
Getting Response value as below:
System.HttpResponse[Status=Internal Server Error, StatusCode=500]
I have tried putting '{}' in the Jsonbody. Added 'Content-Type' in header but nothing worked.
Where should I lookout for this?
In the Postman, I was not putting anything in the body, and getting a successful response.
To get the same behaviour, I was using empty string in Apex, like this:
jsonBody = json.serializePretty('', true);
But the parser was not working correctly.
To solve this, I created a class without any field:
class ClassForEmptyBody{
}
And used object of that class in the serializer:
ClassForEmptyBody classForEmptyBodyObject = new ClassForEmptyBody();
jsonBody = json.serializePretty(classForEmptyBodyObject , true);
Why are you passing json body if nothing is in there. Just skip setbody code and try.

Swagger - Why is Swagger creating a request body field when I have not written an annotation for one?

I have written a swagger ui for a GET request that doesn't need a request body. I haven't used the #RequestBody annotation so why is Swagger bringing up a request body field on the ui? Even if I leave it empty, it is causing my API requests to fail with the following error: TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
I understand why this error exists, the curl that swagger creates has a -d option. But how can I switch this off?
The only annotations I've used are #Get, #Path, #Operation, #ApiResponses, and #Parameters.
Put simply, how can I tell swagger that I don't need a request body?
If you hadn't annotated some parameter of your method it is automatically considered request body. If you don't want it to, you have to explicitly annotate it as something else or annotate it to ignore param with something like #ApiParam(hidden = true):
// example usage of Swagger with Akka HTTP
#ApiOperation(
httpMethod = "GET",
response = classOf[ApiResponseX],
value = "docs"
)
#ApiImplicitParams(
Array(
new ApiImplicitParam(
name = "id",
dataType = "string",
paramType = "query",
value = "id docs"
)
)
)
def fetchX(
// even if this value has custom handling I have to explicitly ignore it,
// to not make it into a request body
#ApiParam(hidden = true) id: UUID
): Route = ...

Verify API Response

Am getting the response of a request like this:
var response = command.PostCommand(testCommand);
I will like to validate that the response is in a json format so am doing it like this:
Assert.AreEqual("application/json", response.ContentType);
Is this way correctly or do i need to specifically validate it from the content-type header response?
You can use the IRestRequest.OnBeforeDeserialization callback to check the response content type before it gets deserialised:
var request = new RestRequest(url)
.AddQueryParameter(x, y); // whatever you need to configure
request.OnBeforeDeserialization =
response => CheckContentType(response.ContentType);
await client.PostAsync<MyResponse>(request);

Yammer API - Posting to External Networks

I've used the Yammer API extensively for accessing current users internal network. All API calls have been working correctly (GET's and POST's) with the original token extracted from;
"https://www.yammer.com/oauth2/access_token.json?client_id={App ID}&client_secret={App Secret}&code={Access Code}"
and using the headers; "Authorization : Bearer {Token}" and "Cookie : {Cookies Received from HTML request}.
I've gotten the tokens for all accessible networks using;
"https://www.yammer.com/api/v1/oauth/tokens.json".
Accessing external networks beyond this point has proved troublesome. I changed the header to "Authorization : Bearer {NetworkToken}". While I am able to GET details from external networks, I cannot POST to external networks. I always receive a '401 Unauthorized' response. The 'Unauthorized' requests include deleting messages and liking messages in external networks.
Is there another step between being able to read data from an external network and enabling POST methods?
If I could get any insight into this i'd be extremely grateful!
Cheers!
When accessing external networks, you need to set the authToken to the authToken for that external network.
Step 1 - Get all auth tokens:
yam.platform.request({
url: "oauth/tokens.json",
type: 'GET',
success: function (msg) {
accessTokens = msg;
/....
},
error: function (msg) {
console.log(msg);
error(msg);
}
Step 2: Set the authToken to the correct external network
var currentToken = "";
$.each(accessTokens, function (i,val) {
if (val.network_permalink == $.cookie('networkPermalink')) {
currentToken = val;
}
});
While I was working on a project last month, I used the following way to post message.
The message has to be Byte encrypted in UTF-8 format.
Specify the content type as "application/x-www-form-urlencoded".
So, an example code would be:
HttpWebRequest a = (HttpWebRequest)WebRequest.Create(postUrl);
a.Headers.Add("Authorization", "Bearer" + authToken);
a.Method = "POST";
byte[] message = Encoding.UTF8.GetBytes("body=" + message + "&replied_to_id=" + threadID);
a.ContentType = "application/x-www-form-urlencoded";
a.ContentLength = message.Length;
using (var postStream = request.GetRequestStream())
{
postStream.Write(message, 0, message.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (var postStreamForResponse = response.GetResponseStream())
{
StreamReader postReader = new StreamReader(postStreamForResponse);
string results = postReader.ReadToEnd();
postReader.Close();
}
I've discovered quite a few inconsistencies quirks with the Yammer API. I've figured out external networks in their totality now. Here are some things that may not be clear;
When doing a POST or DELETE request, do not include the network_permalink in the url! Only include the network_permalink when you're doing a GET request. This was my main issue.
Required request headers;
Content-Type : application/x-www-form-urlencoded
Accept : application/json
Cookie : _workfeed_session_id=(A code that can be extracted from the response from your first request with an auth token)
Authorization : Bearer (Access token for whichever network you wish to access)
Oh and just FYI, to request threads within the 'All Company' group this is the url; https://www.yammer.com/(network_permalink)/api/v1/messages/general.json
Thanks for the answers!