As a proof of concept I had to test if TIBCO could perform authentication from its BusinessWorks suite to a Microsoft Dynamics CRM web service using ‘Integrated Windows Authentication’.
TIBCO BusinessWorks has all the necessary tools for connectivity, transformation and orchestration of processes but unfortunately it has no support for Integrated Windows Authentication. But I don’t consider it as a flaw of TIBCO BusinessWorks. Integrated Windows Authentication is specific to Microsoft products and the protocol that is currently in scope for the POC, NTLM, is a proprietary protocol.
What is the goal of the POC?
Authenticate TIBCO when calling the Microsoft Dynamics CRM web service. The authentication needs to be done using the NTLMv2 protocol. The account I use is a designated system account for TIBCO, which has received the correct access.
How did I start?
A lot of developers think: ‘what I do, I do better’. Well, I am more in favor of ‘use instead of build’. So first I started to find solutions on the internet that might do the trick for us. Since that didn’t work out well, I started to use some libraries that implement NTLM and to see if it works with TIBCO BusinessWorks.
I also wanted to find a solution as fast as possible. So instead of trying to investigate further on why something doesn’t work by the book, I just tried a different library/application.
So here is a summary of things I’ve tried:
Proxy solutions:
NTLMAPS: This is a tool that was used at a client side but stopped working for them after they switched to a new Active Directory domain. For my POC, and using the latest NTLMAPS version, I constantly received a 401 error back. So I had to quickly give up on this.
CNTLM: A rewrite of NTLMAPS and I managed to get authenticated when I was trying it from a Non-MS browser like chrome or Firefox. However the tool was prompting me for credentials for user authentication, which were then used for NTLM authentication. I quickly tried to configure my SOAP Request-Reply activity using HTTP Authentication and a correctly set Identity but unfortunately it didn't work. I didn’t investigate further on this.
Since the proxy solutions did not work out well, I tried to use a Java Code activity and tried to use libraries implementing the NTLM protocol.
Client solutions:
According to the documentation on Apache it should support NTLMv2 but I didn’t manage to get it to work. Although following the guidelines, authentication was always failing with a 401 error. Maybe I was doing something wrong but since TIBCO BusinessWorks is also using (an older) HTTPClient in its third party library repository, I decided not to investigate further on this. Just to be sure that an upgrade would cause a nasty side effect.
I didn’t try this one because on the site of JCIFS, they themselves recommend to use the Jespa library if you’re looking for full NTLM support.
Unfortunately the Jespa library is not open-source and has some limitations when you integrate directly with Active Directory. However in my situation I only needed a small portion of this library. I needed to establish a connection and needed a provider that will authenticate against the NTLMv2 protocol. So for my POC there is no impact.
Proxy setup
I’ve made a small TIBCO BW project, which can act as a proxy, between TIBCO BusinessWorks and MS Dynamics CRM web services. This service is working identically as the NTLMAPS application. It will sit as a proxy between the Soap Request-Reply activities and the endpoint.
How does the Forward request activity look like?
1) First I defined some input parameters so I could dynamically configure my process:
2) Configuring the Java Code: Updating the import statements
import java.util.*;
import java.io.*;
import java.net.URL;
import java.security.PrivilegedExceptionAction;
import jespa.http.HttpURLConnection;
import jespa.security.PasswordCredential;
import jespa.security.RunAs;
3) Configuring the Java Code: Add an inner class
This class will perform the POST action and return the soap reply.
public class HttpPost implements PrivilegedExceptionAction
{
private URL url = null;
private HttpURLConnection conn = null;
private OutputStreamWriter wout = null;
private BufferedReader rd = null;
private StringBuilder sb = null;
private String line = null;
private String responseMessage = null;
private int responseCode = 0;
private String responseBody = null;
private String endpoint;
public HttpPost(String endpoint){
this.endpoint = endpoint;
}
public Object run() throws Exception
{
url = new URL(endpoint);
conn = new HttpURLConnection(url);
try {
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.addRequestProperty("SOAPAction", soapAction);
conn.addRequestProperty("Content-Type", contentType);
conn.setReadTimeout(timeout);
// Set the input
wout = new OutputStreamWriter( conn.getOutputStream() );
wout.write(soapRequest);
wout.flush(); // this triggers the POST
wout.close();
// Get the response
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
sb = new StringBuilder();
while ((line = rd.readLine()) != null) {
sb.append(line + "\n");
}
rd.close();
} catch (IOException ioe) {
System.err.println(ioe.getMessage()); // such as '404 Not Found'
rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
sb = new StringBuilder();
while ((line = rd.readLine()) != null) {
sb.append(line + "\n");
}
rd.close();
} finally {
responseCode = conn.getResponseCode();
responseMessage = conn.getResponseMessage();
responseBody = sb.toString();
conn.disconnect();
wout=null;
rd = null;
sb = null;
conn = null;
}
return null;
}
public int getResponseCode()
{
return this.responseCode;
}
public String getResponseMessage()
{
return this.responseMessage;
}
public String getResponseBody()
{
return this.responseBody;
}
}
4) Implement the invoke function
org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger("bw.logger");
HttpPost t = new HttpPost(endpoint);
RunAs.runAs(t, new PasswordCredential(domain + "\\" + userName, password.toCharArray()));
logger.info("Server replied with HTTP status code: " + t.getResponseCode() + " " + t.getResponseMessage());
soapReply = t.getResponseBody();
Using the proxy class
When configuring my Soap Request-Reply message, I only need to configure a Proxy Configuration which points to my HTTP Receiver. My HTTP Receiver will forward the request and returns back the correct response.
Update: As some readers have commented, there seems to be a bug inside the above code.
The updated project can be downloaded here. This project has updated java code that improves the handling of the soap request/response. You'll have to change the global variables so the authentication group is updated with your login credentials.
Also note that since BusinessWorks version 5.10, Tibco has added NTLM authentication support. See the release notes at https://docs.tibco.com/
Author: Günther