Properly calling an authorization Karate feature with arguments - karate

I have an authentication feature working, but would like to make it reusable. I'm not able to get working through the methods I've tried so far.
The standalone method that works is:
# Created at 10/4/18
Feature: #Authentication Management
Background:
* url gatewayUrl
* def myid = 'myid'
* def mysecret = 'mysecret'
Scenario: # Generate authentication token for x user
Given path 'mypath'
And header Content-Type = 'application/x-www-form-urlencoded'
And request 'grant_type=api_key&myid=' + myid + '&mysecret=' + mysecret
When method post
Then status 200
* print response.Token
Here is the working POST request for this one:
1 > POST <authURL>
1 > Accept-Encoding: gzip,deflate
1 > Connection: Keep-Alive
1 > Content-Length: 108
1 > Content-Type: application/x-www-form-urlencoded; charset=UTF-8
1 > Host: <authURL>
1 > User-Agent: Apache-HttpClient/4.5.5 (Java/10.0.2)
grant_type=api_key&myid=myId&mysecret=mySecret
I'd like to replace myid and mysecret with the args from the feature that performs the call. Using '#(myid)' and '(#mysecret)' doesn't seem to work anywhere but when defining param(s). Is there a way to do this replacement, or another equivalent way of building up the request data to send?
Thanks

Of course it is worth reading through the docs carefully: https://github.com/intuit/karate#calling-other-feature-files
Try this:
Feature: #Authentication Management
Scenario: # Generate authentication token for x user
Given url gatewayUrl
And path 'mypath'
And header Content-Type = 'application/x-www-form-urlencoded'
And request 'grant_type=api_key&myid=' + myid + '&mysecret=' + mysecret
When method post
Then status 200
And then call as follows:
Feature: main
Background:
* def result = call read('auth.feature') { myid: 'actualid', mysecret: 'actualsecret' }
* def token = result.response.Token
# remaining test uses token

Related

Why does karate configure headers behave differently to setting headers directly in the background?

I wrote a test that calls an api endpoint once and retrieves an etag in the response. After that I do a second call and I am setting the etag value to the if-none-match header. The test looks like the following:
Feature: Retrieve station properties
Background:
* url baseUrl
* def contentType = 'application/vnd.whatever'
* def accessToken = 'ey.foobar.123'
* configure headers = { Authorization: '#("Bearer " + accessToken)', Accept: '#(contentType)' }
Scenario: Fetch station properties once and expect a 304 on the sub-sequent request
Given path '/api/station-properties'
When method GET
Then status 200
And headers {ETag: '#notnull'}
And def etag = responseHeaders['ETag'][0]
Given path '/api/station-properties'
And header If-None-Match = etag
When method GET
Then status 304
This basically works but I was not happy with the configure headers line as I may add additional headers later on. Thus I thought about using a different method to set the headers:
Feature: Retrieve station properties
Background:
* url baseUrl
* def contentType = 'application/vnd.whatever'
* def accessToken = 'ey.foobar.123'
* header Authorization = 'Bearer ' + accessToken
* header Accept = contentType
Scenario: Fetch station properties once and expect a 304 on the sub-sequent request
Given path '/api/station-properties'
When method GET
Then status 200
And headers {ETag: '#notnull'}
And def etag = responseHeaders['ETag'][0]
Given path '/api/station-properties'
And header If-None-Match = etag
When method GET
Then status 304
In this case though, the headers (Authorization and Accept) are set on the first api call but on the second call they are not.
Why is this the case?
Yes, the rule is configure is to "persist" for multiple HTTP calls. So just make this change in the Background:
* configure headers = ({ Authorization: 'Bearer ' + accessToken, Accept: contentType })
Well, yes - do what you were doing earlier. Now it should work.

Why does this REST request work from every client except Talend API tester

This is an example of a REST POST request that works everywhere except Talend API tester. It give a 500 error.
Here is how I set the request up in my applications.
URLConnection connection = new URL("https://" + authHost + "/connect/token").openConnection();
logger.info("Connection oppened");
// message contains the form data: key=value&key=value&key=value
String message = "password=" + password + "&grant_type=password&username=" + username +
"&client_id=foolid&scope=mouthwash";
logger.info(message);
// Setting header fields.
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept", "application/json");
connection.getOutputStream().write(message.getBytes("UTF-8"));
Here is a working example in Postman
POST /connect/token HTTP/1.1
Host: identityserver.uat.example.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Cookie: ARRAffinity=6a37d2ecf3441e27913cb832c4b767c68cad0e45c8806b3c5344d1b52d57f67a; ARRAffinitySameSite=6a37d2ecf3441e27913cb832c4b767c68cad0e45c8806b3c5344d1b52d57f67a
Content-Length: 137
password=secret&grant_type=password&username=fool&client_id=foolid&scope=mouthwash
And here is what Talend say it sends, which gets the 500 error -- which I understand comes from the server.
POST /connect/token HTTP/1.1
Accept: application/json
Content-Length: 137
Content-Type: application/x-www-form-urlencoded
Host: identityserver.uat.example.com
password=secret&grant_type=password&username=fool&client_id=foolid&scope=mouthwash
What's happening here?

Advice for implementing custom authentication scheme [duplicate]

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

How to handle requests with signatures on karate tests?

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

Hide passwords in logs

The following code :
Background:
* url "https://www.google.fr"
Scenario: hide password
Given path "login"
And form field username = 'john'
And form field password = 'secret'
When method post
Then status 200
Gives the following output :
10:38:34.710 request:
1 > POST https://www.google.fr/login
1 > Accept-Encoding: gzip,deflate
1 > Connection: Keep-Alive
1 > Content-Length: 29
1 > Content-Type: application/x-www-form-urlencoded; charset=UTF-8
1 > Host: www.google.fr
1 > User-Agent: Apache-HttpClient/4.5.5 (Java/1.8.0_191)
username=john&password=secret
Is there a way to hide the password here, while keeping the logs at the DEBUG level? If that means also hiding the username, that's not a problem.
This may need to be a feature request if * configure report = { showLog: false } does not do it already.