entity must be null for http method GET - java

I'm trying to consume an external api exposed a payment provider.
I user Jersey and javax.ws.rs for request, because I can easily send authent with Digest.
But when it comes to make the request, a GET with payload, Jersey returns
> IllegalStateException. Entity must be null for http method GET
CashTransactionResponse responseData = null;
//We connect to intouch server
String requestUrl = rootUrlTouchPay + agency.getAgencyCode() + "/" + IntouchMethodApis.TRANSACTION + "?loginAgent=" + agency.getLogin() + "&passwordAgent=" + agency.getPassword();
ClientConfig clientConfig = new ClientConfig();
//Open Digest authentication
HttpAuthenticationFeature feature = HttpAuthenticationFeature.digest(BASIC_LOGIN, BASIC_PWD);
clientConfig.register(feature);
clientConfig.register(JacksonFeature.class);
//Create new rest client
Client client = ClientBuilder.newClient(clientConfig);
clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
//Set the url
WebTarget webTarget = client.target(requestUrl);
Invocation.Builder invocationBuilder = webTarget.request(javax.ws.rs.core.MediaType.APPLICATION_JSON);
logger.info("Initialisation of cashout service successful for cash");
// create request
Gson gson = new Gson();
String transactionString = gson.toJson(cashRequest);
Response response = null;
// start the response
if (cashRequest.getServiceCode().contains(TelecomEnum.WAVE.name().toUpperCase())) {
response = invocationBuilder.method("GET", Entity.entity(transactionString, javax.ws.rs.core.MediaType.APPLICATION_JSON));
} else {
response = invocationBuilder.put(Entity.entity(transactionString, javax.ws.rs.core.MediaType.APPLICATION_JSON));
}
Please how could i do to send my GET request with body ?
Thanks

I believe the problem is in
Client client = ClientBuilder.newClient(clientConfig);
clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
The client copied the values from clientConfig and any further settings on clientConfig do not have any impact on the client.
Either switch the lines or set the ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION property on the client.

Instead of doing an invocationBuilder.method("GET", ...), use invocationBuilder.post(entity), as described here. This will allow you to POST your transaction String to the endpoint.

Related

Using rest api of azure devops

I want to fetch data from the rest api of Azure Devops using Java.But not sure how to establish the connection.May be personal acces token will help,but how to use the token in Code for establishing the connection between code and azure devops? An example from anyone will be very helpful.
A code example will be very helpfull
If I am understanding you correctly, you are trying to call azure APIs, and those API need authorization token?
For example this azure API to send data into Azure queue : https://learn.microsoft.com/en-us/rest/api/servicebus/send-message-to-queue
It needs some payload and Authorization in request header !!
If my Understanding is correct, than from java you need to use any rest client or HTTP client to call the REST API and you need to pass the Authorization token in the request header
For calling a Rest API in java with passing header below is an example:
MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
map.add("Authorization", "Bearer <Azure AD JWT token>"); // set your token here
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON); //someother http headers you want to set
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
String azure_url = "https://azure_url"; // your azure devops REST URL
ResponseEntity<String> response = restTemplate.postForEntity(azure_url, request , String.class);
A small example with httpclient:
static String ServiceUrl = "https://dev.azure.com/<your_org>/";
static String TeamProjectName = "your_team_project_name";
static String UrlEndGetWorkItemById = "/_apis/wit/workitems/";
static Integer WorkItemId = 1208;
static String PAT = "your_pat";
String AuthStr = ":" + PAT;
Base64 base64 = new Base64();
String encodedPAT = new String(base64.encode(AuthStr.getBytes()));
URL url = new URL(ServiceUrl + TeamProjectName + UrlEndGetWorkItemById + WorkItemId.toString());
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Authorization", "Basic " + encodedPAT);
con.setRequestMethod("GET");
int status = con.getResponseCode();
Link to the file: ResApiMain.java
You can the use java client library for azure devops rest api. This will take the overload of encoding your personal access token and indeed supports OAuth authentication.
It is been actively developed and used in production.
Source code - https://github.com/hkarthik7/azure-devops-java-sdk
Documentation - https://azure-devops-java-sdk-docs.readthedocs.io/en/latest/?badge=latest
A quick example:
public class Main {
public static void main(String[] args) {
String organisation = "myOrganisationName";
String personalAccessToken = "accessToken";
String projectName = "myProject";
// Connect Azure DevOps API with organisation name and personal access token.
var webApi = new AzDClientApi(organisation, project, personalAccessToken);
// call the respective API with created webApi client connection object;
var core = webApi.getCoreApi();
var wit = webApi.getWorkItemTrackingApi();
try {
// get the list of projects
core.getProjects();
// get a workitem
wit.getWorkItem(15);
// Get a work item and optionally expand the field
wit.getWorkItem(15, WorkItemExpand.ALL);
} catch (AzDException e1) {
e1.printStackTrace();
}
}
}
The library has support to most of the APIs and you can view the documentation and examples folder in the github repo to know how to get the most out of it.

Logging into Content Navigator from external application

I'm trying to access a PluginService on Content Navigator from my Java Application (Event Action Handler in FileNet P8). The application uses the JAXRS logon service to receive the security_token from the Content Navigator server. However, if I try to call the PluginService I get a response that my login has expired.
I'm able to get the security token, as described in this code block:
URL logonUrl = new URL("http://icn-host:9081/jaxrs/logon"
+ "?userid=user"
+ "&password=password"
+ "&desktop=admin"
+ "&contextPath=%2Fnavigator");
HttpURLConnection logonConnection = (HttpURLConnection)logonUrl.openConnection();
logonConnection.setRequestMethod("POST");
logonConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
logonConnection.setDoOutput(true);
InputStream logonResponse = logonConnection.getInputStream();
String responseText = IOUtils.toString(logonResponse, "UTF-8")
.replaceFirst("^\\{}&&", "");
JSONObject responseJson = JSONObject.parse(responseText);
return (String)responseJson.get("security_token");
But when I try to make another request, I get an error response:
URL requestUrl = new URL("http://icn-host:9081/plugin.do"
+ "?plugin=myPlugin&action=myPluginService&myRequestProps=foobar");
HttpURLConnection requestConnection =
(HttpURLConnection)requestUrl.openConnection();
requestConnection.setRequestMethod("GET");
String securityToken = getSecurityToken(); // calls above code
requestConnection.setRequestProperty("security_token", securityToken);
equestConnection.setDoOutput(true);
InputStream responseStream = requestConnection.getInputStream();
String responseText = IOUtils.toString(responseStream, "UTF-8")
.replaceFirst("^\\{}&&", "");
log.info("response was: " + responseText);
I always get the following response:
{
"messagesEncoded":true,
"errors": [
{
"adminResponse":null,
"moreInformation":null,
"explanation":"Your session expired because of inactivity.",
"number":"1003",
"userResponse":"Log in again.",
"text":"Your session expired."
}
]
}
I've also tried to set the cookies, but no success.
java.net.CookieManager cookieManager = new java.net.CookieManager();
Map<String, List<String>> headerFields = logonConnection.getHeaderFields();
List<String> cookiesHeader = headerFields.get("Set-Cookie");
if (cookiesHeader != null) {
for (String cookie : cookiesHeader) {
cookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
}
}
// ...
StringBuilder cookieHeader = new StringBuilder();
List<HttpCookie> cookies = cookieManager.getCookieStore().getCookies();
for (int i = 0; i < cookies.size(); i++) {
if (i > 0) {
cookieHeader.append(";");
}
HttpCookie cookie = cookies.get(i);
log.info("Cookie " + i + ": " + cookie.toString());
cookieHeader.append(cookie.toString());
}
requestConnection.setRequestProperty("Cookie", cookieHeader.toString());
I tried to replicate the request using XMLHttpRequest in a Content Navigator window and it works as expected:
var xhr = new XMLHttpRequest();
xhr.open("GET", "plugin.do" +
"?plugin=myPlugin" +
"&action=myPluginService" +
"&myRequestProps=foobar");
xhr.setRequestHeader("security_token", ecm.model.Request._security_token);
xhr.send();
I had a similar challenge for a client a few months ago where i had to automate the process of installing plugins and applying configuration for CI purposes.
I discovered it is key to obtain the desktop as the first api call after login for the session to become 'valid'.
So first jaxrs/logon, then jaxrs/getDesktop, then your service invoke.
A little sidenote: If you plan on having container managed authentication later on, the process will be different. The jaxrs/logon won't work, and instead the jaxrs/getDesktop will deliver the security_token.
A little remark though: wouldn't it be a better solution to have a shared library that you'd be able to use both from your Event Action as the ICN service?
Using shared libraries (see Ivo's answer) is definitely the best approach, calling jaxrs/getDesktop didn't work for me. Instead I just used the Maven Assembly Plugin to include a newer version of the org.apache.httpcomponents dependency and call the requests with an HttpClient.
My final code looks something like this:
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setDefaultRequestConfig(requestConfig)
.build();
HttpUriRequest logonRequest = RequestBuilder.post()
.setUri("http://icn-host:9081/navigator/jarxrs/logon")
.addParameter("desktop", "admin")
.addParameter("contextPath", "/navigator")
.addParameter("userid", "icnadmin")
.addParameter("password", "password")
.build();
CloseableHttpResponse logonResponse = httpClient.execute(logonRequest);
HttpEntity responseEntity = logonResponse.getEntity();
String responseText = EntityUtils.toString(responseEntity)
.replaceFirst("^\\{}&&", "");
JSONObject responseJson = JSONObject.parse(responseText);
String securityToken = (String) responseJson.get("security_token");
HttpUriRequest request = RequestBuilder.get()
.setUri("http://icn-host:9081/navigator/plugin.do")
.addParameter("plugin", "myPlugin")
.addParameter("action", "myPluginService")
.addParameter("myRequestProps", "foobar")
.addHeader("security_token", securityToken)
.build();
HttpClientContext context = HttpClientContext.create();
CookieStore cookieStore = new BasicCookieStore();
context.setCookieStore(cookieStore);
CloseableHttpResponse response = httpClient.execute(request, context);

How to implement Junit test for rest client program which calls external rest service api

Having Rest Client program:
- Create client object using Jersey libraries
- Invokes the external rest services using URL.
- read the response and process the response.
public String getResourceFromService() {
ClientConfig clientConfig = new DefaultClientConfig();
Client restClient = Client.create(clientConfig);
WebResource webResource = restClient.resource(serviceURL)
ClientResponse response = webResource.accept("application/xml")
.header("Authorization", "Basic " + credentials)
.get(ClientResponse.class);
//response data
if(response.getStatus()== HttpStatus.SC_OK) {
String responseInXMLString = response.getEntity(String.class);
return responseInXMLString;
}
}
Please suggest, What and how to implement Junit for this program.

Consuming soap service with NTLM Authentication

I am trying to consume a SOAP service with NTLM authentication by creating a NTLM engine (following instructions on http://hc.apache.org/httpcomponents-client-4.3.x/ntlm.html ) implemented AuthSchemeFactory and finally registered the AuthSchemeFactory to my HTTP Client. When I hit the service using my HTTP Client I get a reponse that "Status code - 415 , Message - The server cannot service the request because the media type is unsupported."
Can anybody tell how can I fix this issue of unsupported media to consume a NTLM-protected SOAP web service on Java platform. Is using JCIFS a correct option to conmsume NTLM protected service or are there any better approach(s). Thanks in advance.
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getAuthSchemes().register(AuthSchemes.NTLM,
new JCIFSNTLMSchemeFactory());
CredentialsProvider credsProvider = new BasicCredentialsProvider();
NTCredentials ntcred = new NTCredentials("USERNAME", "PASSWORD",
"HOST", "DOMAIN");
credsProvider.setCredentials(new AuthScope("HOST", 443,
AuthScope.ANY_REALM, "NTLM"), ntcred);
httpclient.setCredentialsProvider(credsProvider);
httpclient.getParams().setParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
Writer writer = new StringWriter();
writer.write("MY SOAP REQUEST BODY");
HttpPost httppost = new HttpPost(
"https://<HOST_NAME>/XiPay30WS.asmx");
httppost.setEntity(new StringEntity(writer.toString()));
httppost.setHeader("Content-Type",
"application/x-www-form-urlencoded");
HttpResponse httpresponse = httpclient.execute(
new HttpHost("HOST", 443, "https"),
httppost, new BasicHttpContext());
String statusCode = httpresponse.getStatusCode();
If you use Spring WS support:
Check this Solution
http://dolszewski.com/spring/sharepoint-web-services-spring-and-ntlm-authentication/
#Bean("navisionMessageSender")
public HttpComponentsMessageSender httpComponentsMessageSender() {
HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
String user = env.getProperty("navision.endpoint.user");
String password = env.getProperty("navision.endpoint.password");
String domain = env.getProperty("navision.endpoint.domain");
NTCredentials credentials = new NTCredentials(user, String.valueOf(password), null, domain);
httpComponentsMessageSender.setCredentials(credentials);
return httpComponentsMessageSender;
}
Sample python implementation with NTLM Auth with FLASK.
If you want to use with java , run the standalone flask code below and call the url (e.g POST request /dora/httpWithNTLM ) from java code by http request
from flask import Flask, render_template, flash, request, url_for, redirect, session , Response
import requests,sys,json
from requests_ntlm import HttpNtlmAuth
app = Flask(__name__)
#app.route("/dora/httpWithNTLM",methods=['POST'])
def invokeHTTPReqWithNTLM():
url =""
reqData = json.loads(request.data)
reqxml=request.data
headers = {}
headers["SOAPAction"] = "";
headers["Content-Type"] = "text/xml"
headers["Accept"] = "text/xml"
print("req headers "+str(request.headers))
r = requests.Request("POST",url,auth=HttpNtlmAuth('domain\\username','password'), data=reqxml, headers=headers)
prepared = r.prepare()
s = requests.Session()
resp = s.send(prepared)
print (resp.status_code)
return Response(resp.text.replace("<","<").replace(">",">"),resp.status_code)
if __name__ == '__main__':
app.run(host="0.0.0.0",port=5001)

Authentication and authorization for RESTfull API (java jersery)

implementing service something similar with tinyurl or bit.ly, I'm would like to expose service as API, I'm using java and jersey as RESTfull service implementation.
I'm looking for simplest way for authentification of users who use API, OAuth is first thing coming in mind, but the problem is I don't need this 3 iteration calls with request token query, than access token query with callback url passing. I just need to give user ability to invoke api with no additional security calls to my server.
Thanks to patrickmcgraw comment I used 2-legged oauth authentificaton.
Here is some java code.
For client side (using Jersey api):
OAuthParameters params = new OAuthParameters().signatureMethod("HMAC-SHA1").
consumerKey("consumerKey").version("1.1");
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("secretKey");
OAuthClientFilter filter = new OAuthClientFilter(client().getProviders(), params, secrets);
WebResource webResource = resource();
webResource.addFilter(filter);
String responseMsg = webResource.path("oauth").get(String.class);
On provider side:
#Path("oauth")
public class OAuthService {
#GET
#Produces("text/html")
public String secretService(#Context HttpContext httpContext) {
OAuthServerRequest request = new OAuthServerRequest(httpContext.getRequest());
OAuthParameters params = new OAuthParameters();
params.readRequest(request);
OAuthSecrets secrets = new OAuthSecrets().consumerSecret("secretKey");
try {
if(!OAuthSignature.verify(request, params, secrets))
return "false";
} catch (OAuthSignatureException ose) {
return "false";
}
return "OK";
}
}
Here is code for PHP client:
<?php
require_once 'oauth.php';
$key = 'consumerKey';
$secret = 'secretKey';
$consumer = new OAuthConsumer($key, $secret);
$api_endpoint = 'http://localhost:9998/oauth';
$sig_method = new OAuthSignatureMethod_HMAC_SHA1;
$parameters = null;
$req = OAuthRequest::from_consumer_and_token($consumer, null, "GET", $api_endpoint, $parameters);
$sig_method = new OAuthSignatureMethod_HMAC_SHA1();
$req->sign_request($sig_method, $consumer, null);//note: double entry of token
//get data using signed url
$ch = curl_init($req->to_url());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($ch);
echo $res;
curl_close($ch);
if youre using http at the transport layer you can always use basic http authentication

Categories