Why is HTTPBuilder basic auth not working? - groovy

The following code doesn't authenticate the user (no authentication failure happens, but the call fails due to lack of permissions):
def remote = new HTTPBuilder("http://example.com")
remote.auth.basic('username', 'password')
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
response.success = { resp, json ->
json ?: [:]
}
}
But the following works fine:
def remote = new HTTPBuilder("http://example.com")
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
headers.'Authorization' =
"Basic ${"username:password".bytes.encodeBase64().toString()}"
response.success = { resp, json ->
json ?: [:]
}
}
Why isn't the first one working?

Two things that I can think of off the top of my head.
The .setHeaders method requires a map. Have you tried 'Authorization' : "Basic ${"username:password".bytes.encodeBase64().toString()}" ?
If not, It's a bit more work and code, but you could user the URIBuilder as well. Generally I encapsulate to a different class
protected final runGetRequest(String endpointPassedIn, RESTClient Client){
URIBuilder myEndpoint = new URIBuilder(new URI(Client.uri.toString() + endpointPassedIn))
HttpResponseDecorator unprocessedResponse = Client.get(uri: myEndpoint) as HttpResponseDecorator
def Response = unprocessedResponse.getData() as LazyMap
return Response
}
Hope this helps

Looks like your server isn't fully HTTPBuilder-compilant. It should return 401 code (which is standart behaviour for REST servers, but non-standart for others) for HTTPBuilder to catch this status and resend authentication request. Here is written about it.

Related

HTTPBuilder & Session ID

I have the following code to connect to a REST API service, authenticate, retrieve a session ID then make further requests passing the session ID to authenticate. The initial request works and I get a HTTP 200 OK plus the session ID in the response, however when I try to make a second request passing the session ID in the header, I get
Caught: groovyx.net.http.HttpResponseException: Bad Request
I know the script can be written much better with the use of classes and try / catch etc. I am still learning both java and groovy so I start by just trying to do everything within the same class.
Any help much appreciated.
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.URIBuilder
import static groovyx.net.http.Method.POST
import static groovyx.net.http.ContentType.*
def url = 'https://1.1.1.1/web_api/'
def uri = new URIBuilder(url)
String CHKPsid
uri.path = 'login'
def http = new HTTPBuilder(uri)
http.ignoreSSLIssues()
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
body = [
"user":"username",
"password":"password"
]
response.success = { resp, json ->
println (json)
CHKPsid = (json.sid)
println "POST Success: ${resp.statusLine}"
}
}
uri.path = 'show-changes'
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
headers.'X-chkp-sid' = '${CHKPsid}'
body = [
"from-date" : "2017-02-01T08:20:50",
"to-date" : "2017-10-21"
]
response.success = { resp, json ->
println (json)
println "POST Success: ${resp.statusLine}"
}
}
String interpolation does not work with single (or triple single quotes). When groovy will evaluate '${CHKPsid}' (single quotes), its value will be ${CHKPsid} (this string). In order to use the value of the variable, you should use double quotes: "${CHKPsid}" or simply just the variable: headers.'X-chkp-sid' = CHKPsid.
So the output of this:
String CHKPsid = "abc123"
println '${CHKPsid}'
println "${CHKPsid}"
will be:
${CHKPsid}
abc123
In order to quickly test what the server receives, you can use httpbin.org or requestb.in
So as well as the correct assignment of the value of the session ID, I found that calling the same HTTPbuilder - http.request the second time even with a change of uri, header and body was the problem. The listening server still saw this as part of the same login API call. My workaround / resolution was to define a 2nd HTTPbuilder with a different name and this now works. I'm interested to know if this is normal behaviour and how others approach this. Thanks.

How to get complete response for Groovy RestClient failed response

Currently, I'm getting HttpResponseException, which has only statusCode.
How can I get complete body of response?
Here is code I'm using
restClient = new RESTClient("http://${Server}")
try {
HttpResponseDecorator resp = restClient.post(path,body,requestContentType)
as HttpResponseDecorator
return JSONObject.fromObject(resp.getData()).get("topKey","");
}
catch (HttpResponseException e) {
error(e.toString())
}
And it only output this:
[oaf.error] groovyx.net.http.HttpResponseException: Internal Server Error
Add custom failed response handler:
restClient = new RESTClient("http://${Server}")
restClient.handler.failure = { resp, data ->
resp.setData(data)
String headers = ""
resp.headers.each {
headers = headers+"${it.name} : ${it.value}\n"
}
throw new HttpResponseException(resp.getStatus(),"HTTP call failed. Status code: ${resp.getStatus()}\n${headers}\n"+
"Response: "+(resp as HttpResponseDecorator).getData())
}
Actually, you can extract the full response from the exception thrown. For example if your caught exception is e and response body JSON should contain a field called myCustomErrorCode, you can check its value by looking at e.response.data.myCustomErrorCode in addition to e.statusCode.

Is it safe to reuse Groovy HTTPBuilder objects for multiple requests?

I have some HTTPBuilder code that behaves differently depending on whether or not I reuse the same HTTPBuilder object to perform two different requests to the same REST service:
def http = new HTTPBuilder( 'https://myBaseURI/' )
http.auth.basic username, password.getPlainText()
http.ignoreSSLIssues()
http.request(GET,JSON) { req ->
uri.path = 'some/api/path/'
headers.'User-Agent' = 'Mozilla/5.0'
} // this request always behaves as expected
http.request(POST, JSON) { req ->
uri.path = 'some/other/api/path'
headers.'User-Agent' = 'Mozilla/5.0'
body = {
// Request body elided for brevity
}
}
The 'correct' behavior is for the POST to return a 201 - Created, but the response comes back as 200 OK unless I create a new HTTPBuilder to handle issuing the second request, in which case, the API call behaves as expected.
Certainly, the cause of the different results could be elsewhere, but I first wanted to make sure I wasn't misusing this object. Are there problems to be aware of when reusing the HTTPBuilder to issue multiple HTTP requests?
Try removing that forward slash at the end when you set uri.path in the GET request.
Looking at the documentation for setPath in URIBuilder you get:
//Set the path component of this URI. The value may be absolute or relative to the current path. e.g.
def uri = new URIBuilder( 'http://localhost/p1/p2?a=1' )
uri.path = '/p3/p2'
assert uri.toString() == 'http://localhost/p3/p2?a=1'
uri.path = 'p2a'
assert uri.toString() == 'http://localhost/p3/p2a?a=1'
uri.path = '../p4'
assert uri.toString() == 'http://localhost/p4?a=1&b=2&c=3#frag'
I understand this to mean that if you set the uri.path of an httpbuilder object with a slash at the end, you have essentially updated the working path so any subsequent relative path updates to uri.path will result in a concatenation of the path. Therefore, your POST in that example ends up pointing at https://myBaseURI/some/api/path/some/other/api/path

How to use HTTPBuilder behind a proxy with authentication

I tried 2 hours and could not make it work.
This is what I did:
grails add-proxy myproxy "--host=<host>" "--port=<port>" "--username=<username>" "--password=<psw>"
grails use-proxy myproxy
I got connection refused error which mean the proxy is not working
In my groovy file, I add the proxy
def http = new HTTPBuilder("http://http://headers.jsontest.com/")
http.setProxy(host, port, "http");
http.request(Method.GET, JSON) {
uri.path = '/'
response.success = { resp, json ->
.....
}
}
I then get groovyx.net.http.HttpResponseException: Proxy Authentication Required
I could not figure out how I set the user/psw for the proxy to make it work
I tried the java way, not working
System.setProperty("http.proxyUser", username);
System.setProperty("http.proxyPassword", password);
and
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, .toCharArray());
}});
Does anyone know how to do this?
Don't know if it will work, but there's some code over here that shows you should do:
import groovyx.net.http.*
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
import org.apache.http.auth.*
def http = new HTTPBuilder( 'http://www.ipchicken.com' )
http.client.getCredentialsProvider().setCredentials(
new AuthScope("myproxy.com", 8080),
new UsernamePasswordCredentials("proxy-username", "proxy-password")
)
http.setProxy('myproxy.com', 8080, 'http')
http.request( GET, TEXT ){ req ->
response.success = { resp, reader ->
println "Response: ${reader.text}"
}
}
Proxy authentication uses different HTTP header (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization), so simply adding the header should work for you.
String basicAuthCredentials = Base64.getEncoder().encodeToString(String.format("%s:%s", username,password).getBytes());
http.setHeaders(['Proxy-Authorization' : "Basic " + basicAuthCredentials])

Using Artifactory's REST API to deploy jar file

Given this api documentation, how would I use HTTPBuilder and Groovy to construct my query? I've tried multiple things but I'm not getting it right.
def http = new HTTPBuilder()
http.request('http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar', PUT, JSON ) { req ->
body = [
uri: "http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar",
downloadUri: "http://artifactory:8888/libs-snapshot-local/my/jar/1.0/test-jar-1.0.jar",
repo: "libs-snapshot-local",
path: "c:\\pathtojarfile\\test.jar",
created: "2012-02-03T08:37:12.599-0800",
createdBy: "someuser",
size: "1024",
mimeType: "application/java-archive"
]
response.success = { resp, json ->
}
}
This seems to get me part way there, but it uploads an empty jar file. Seems like the body is completely ignored. Removing it produces the same result. I can't seem to find a good reference on how this is done.
The JSON in the mentioned documentation is actually Artifactory's response to the deployment request.
For deployment, Artifactroy requires only a simple PUT request, for example:
def restClient = new RESTClient('http://localhost:8080/artifactory/libs-release-local/')
restClient.auth.basic 'username', 'password'
restClient.encoder.'application/zip' = this.&encodeZipFile
def encodeZipFile(Object data) throws UnsupportedEncodingException {
def entity = new FileEntity((File) data, 'application/zip');
entity.setContentType('application/zip');
return entity
}
def response = restClient.put(path: 'org/artifact/1.0/artifact-1.0.jar',
body: new File('/path/to/local/artifact.jar'),
requestContentType: 'application/zip'
)

Resources