Armanino Blog
Article

GP2010 Web Services, Java, and Client Authentication

October 15, 2010

As my blog identity indicates, I am the .Net Ninja – self proclaimed, which is definitely a little weak… but how else do we form a web persona?

Outsiders may see the persona name, or company I work for, and assume that I only do .Net or work with Microsoft (GP) products; this is simply not the case. So naturally, when a client requests a project that is outside that box, I am intrigued. One or my recent project requirements can be simply stated:  “We must integrate data to and from Great Plains using Java.”

The requirement is best met using GP web services, as interoperability and logistics are of the utmost importance. This was my recommendation to the client, which was met with some hesitation. I was surprised.  I began to recall my early days as a developer – I spent 4-5 years working with Java. It was the first language I had ever used. My familiarity led to confidence – I proposed a proof of concept project, in which I would help build a sample Java client.

Googling this topic (GP Web Services and Java) does not turn up many positive results, largely because the Java and .Net communities do not seem to communicate effectively. My online research came up with many threads that said things like “I am a .Net developer…I do not know Java. Or, I am a Java developer…I do not know .Net”.

So, what do we really need? Someone who knows both (at least to some degree). Enter the .Net Ninja, who surprisingly knows Java…after all, Java made his transition to C# a breeze.

I built a simple Java Swing application (Image 1) in Eclipse that would provide data entry for standard GL Journal Entry transactions. I constucted business objects for use with the client; my purpose was to plug-in the Java Client Proxy after having all the other pieces in place…plus, it would be a good exercise in recollection.

GP2010 Web Services do not run out of IIS.  Instead, they use the newer Service Host model and run directly out of Administrative Tools->Services. Additionally, Microsoft built two endpoints for connecting: Legacy and Standard. The Legacy endpoint basically boils down to the ASP.NET 2.0 implementation many are familiar with – basicHTTP binding and an .asmx file. The Standard endpoint uses wsHTTP binding and a WCF service -newer technology.

I ran into my first road block when I tried to generate the client proxy source using Eclipse. I tried connecting to both endpoints using the IDE  interface – neither would connect. I always got an error regarding a naming conflict in the WSDL.  Some research indicated that I could alter the web service config files to get by the error – no such luck. I should admit here, that a colleague was able to generate the proxy from Eclipse using the wsdl2java tool from the command line, but I am not aware of his environment, Eclipse version, or additional libraries used… I didn’t even bother to ask, as I already had a solution when he told me.

I decided to change my Java toolset…enter Project Tango, NetBeans, and Glassfish Metro.  Project Tango is around for this specific purpose – interoperability between .Net web services and Java. After changing to the latest NetBeans tools (Metro included), I was able to generate the Client Proxy using just the interface…no command lines, no issues. I did bind to the Legacy endpoint, as opposed to the Standard endpoint. (Image 2)

With this hurdle out of the way, I was ready to start sending transactions to Great Plains 2010. I plugged in the remaing web services client logic, and immediately began sending transactions to Dynamics GP 2010 (Image 3).

Of course, I was running under administrative context –  able to connect to GP as God and do anything I want.  When I tested as a different user…failure.

This is where the important information comes into play: a little bit of knowledge about .Net services and Java Proxy Clients goes a long.  NetBeans uses the JAX-WS toolset, which uses java.net for its protocols, authentication, and handshaking.  The issue boils down to, “How can I progammatically impersonate a windows user account with java.net when connecting to GP2010 Legacy Web Services?” (Image 4)

The solution involves three components:

  1. Dynamics Security Console (Image 5): This component is installed on the same machine that Web Services are installed.  It can be located by going to Administrative Tools->Dyanmics Security Console.  This piece is important because it allows us to assign a windows user account to specific security roles, granting access to only what we need.  We can use the pre-assigned windows accounts as our “java” impersonation accounts.  Configuration of a windows user account to a security role is trivial if you have the correct domain permissions.
  2. java.net.Authenticator and javax.xml.ws.BindingProvider :  These are the java libraries I used to override the default user account when authenticating.  Code has been provided below.  Basically, we must create a custom Authenticator, set it as the default Authenticator used by java.net, and then override the user name and password in the service client using the BindingProvider functionality.  After much research, I found that Authenticator did not provide good support for NTLM authentication…maybe it can be done, but I did not find it to be simple.  I did find that Authenticator worked great for Basic and Digest authentication.  In that case, I would use Digest, which is considered more secure than NTLM anyway.
  3. WSBinding.config: This is a configuration file located in a subdirectory in the GP2010 Web Services root folder.  It is located in the ServiceConfigs folder.  This config file determines what authentication mechanism GP2010 web services use when a client connects.  There is separate configuration for both the Legacy and Standard endpoints.  By default, the Legacy service is set to use NTLM, but is easy to change.  I tested NTLM, Windows, Basic, Digest, and None authentication types.  NTLM and Windows did not work using this solution.  Basic and Digest did.  None is not applicable, as Web Services should require an authenticated user to perform its own security set by the Dynamics Security Console in #1.  My config file has been provided below.  Just look for the node  “transport” with attribute “clientCredentialType”
For #2, here is the code I used for my Authenticator:
import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class CustomAuthenticator extends Authenticator{

private String myUserName;
private String myPassword;

public CustomAuthenticator(String username,String password)
{
super();
myUserName = username;
myPassword = password;
}

@Override
protected PasswordAuthentication getPasswordAuthentication()
{
char[] pwdChar = myPassword.toCharArray();
return new PasswordAuthentication(myUserName,pwdChar);
}
}

For #2, here is the code I used when override the default Authenticator and Binding Provider properties:
import javax.xml.ws.BindingProvider;
import java.util.Map;
import java.net.URL;
import java.net.Authenticator;

protected void btnTestClick()
{
//My SuperUser credentials
String uName = “DEMO\Administrator”;
String uPass = “XYZ123”;
String oEndpoint = “”http://DEMO:48620/DynamicsGPWebServices/DynamicsGPService.asmx””;

//Create a CustomAuthenticator and establish that is the default
//java.net Authenticator
Authenticator.setDefault(new CustomAuthenticator(uName, uPass));
URL dgpWsdlUri;
String dgpuri = “http://DEMO:48620/Metadata/Legacy/Full/DynamicsGP.wsdl“;
try
{
dgpWsdlUri = new URL(dgpuri);

//Get the service interface and legacy endpoint

DynamicsGP_Service service = new DynamicsGP_Service(dgpWsdlUri);
System.out.println(“** Got DynamicsGP Service interface ***”);
DynamicsGP dynamicsGP = service.getLegacyDynamicsGP();

//Override the URL; I only put this in to show how.
BindingProvider bp = (BindingProvider)dynamicsGP;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, oEndpoint);

//Override default BindingProvide information
Map<String, Object> map = bp.getRequestContext();
map.put(BindingProvider.USERNAME_PROPERTY, uName);
map.put(BindingProvider.PASSWORD_PROPERTY, uPass);

//Begin Web Services Work

//……

//End Web Services Work

} catch (Exception e)
{
//Handle Exceptions
}

}

For #3, here is my WSBinding.Config:
<?xml version=”1.0″ encoding=”utf-8″?>
<bindings>
<basicHttpBinding>
<binding name=”BasicHttpBindingTarget”>
<readerQuotas maxDepth=”2147483647″ maxStringContentLength=”2147483647″ maxArrayLength=”2147483647″ maxBytesPerRead=”2147483647″ maxNameTableCharCount=”2147483647″/>
<security mode=”TransportCredentialOnly”>
<transport clientCredentialType=”Digest”/>
</security>
</binding>
</basicHttpBinding>
<wsHttpBinding>
<binding name=”WSHttpBindingTarget” maxBufferPoolSize=”524288″ maxReceivedMessageSize=”128896″ messageEncoding=”Text” textEncoding=”utf-8″ useDefaultWebProxy=”true”>
<security mode=”Message”>
<message clientCredentialType=”Windows”/>
</security>
</binding>
</wsHttpBinding>
<customBinding>
<binding name=”CustomBinding”>
<textMessageEncoding>
<readerQuotas maxDepth=”2147483647″ maxStringContentLength=”2147483647″ maxArrayLength=”2147483647″ maxBytesPerRead=”2147483647″ maxNameTableCharCount=”2147483647″/>
</textMessageEncoding>
<httpTransport maxBufferPoolSize=”2147483647″ maxReceivedMessageSize=”2147483647″ maxBufferSize=”2147483647″/>
</binding>
</customBinding>
</bindings>

Remember to place this config file in the correct location, and then restart the services using Administrative Tools->Services before trying to connect.

Hopefully, this article will help people that come across this “niche” situation, which should help prevent un-necessary hair loss and cursing.  There are many things not discussed here (how to use Proxy objects, lower security layers, firewalls, etc.), but this solution should get many people off the ground and running.

“Those who realize their folly are not true fools.” – Chuang Tzu

Stay In Touch

Sign up to stay up-to-date with the latest accounting regulations, best practices, industry news and technology insights to run your business.

Resources
Related News & Insights
sage-intacct-2024-r2-release-overview
Webinar
Discover the Newest Sage Intacct Updates With Armanino’s Experts

May 15, 2024 | 01:00 PM - 02:00 PM PT
Fireside Chat: Access to Top-Tier Talent Through Outsourcing
Webinar
The Crucial Role of Internal Communications in Driving Engagement

April 30, 2024 | 10:30 AM - 11:30 AM PT
5 Signs Your Business Has Outgrown its Legacy Accounting System
Webinar
Don't Let Your Legacy System Limit Your Potential

April 24, 2024 | 10:00 AM - 10:45 AM PT