Wednesday, November 2, 2011

Propagate user identity in JAX-WS web service on WebSphere using WS-Security

 

Introduction


This article will demonstrate how the user identity (the subject's user principle in java security) of the client can be used for authentication and authorization in a remote web service.

WS-Security is an extension to SOAP and can be used for integrity, confidentiality and authentication, I will focus on the authentication part here, and how the user identity can also be used for authorization in the web service after authentication.

The WS-Security policies are attached to a JAX-WS client or provider after deployment. The developer of the web service does not need to know or be bothered how the authentication is done at runtime. The developer only needs to specify which roles (he defined) can access which pieces of code.

Building a sample


Let's get started with a sample straight away. The RAD 8 project interchange file ("archive", as it is called now) is available at the end of the article.

In RAD 8 we create a new dynamic web project named "MySecureWebService" with all default settings:


We create a new class MyWebServiceImpl in the package arendatwork.wssecurity:


The only method in MyWebServiceImpl is a classic hello world implementation:

public String helloWorld(String name){
    System.out.println("MyWebService says hello " + name);
    return "hello " + name;
}

If we right-click the dynamic web project, we can choose new > other, we can type web service to launch the web service wizard:



The MyWebServiceImpl is the java bean implementation for our bottom up creation.   Be sure to select JAX-WS as the implementation type (in this case my default runtime is WAS 8 and jax-ws is selected by default. If your runtime is WAS7, JAX-RPC is still the default selection).  RAD will publish the web service for you straight away.

Let's create a client!



We will also generate jsp's for the client, therefore we will first create a new dynamic web project to hold these files. Completely similar to creating the first dynamic web project for the web service, we create one for the client, except for the names of the web project (MySecureWebServiceClient) and the enterprise project (MySecureWebServiceClientEAR).
Right click the web service in the enterprise explorer and navigate to generate > client

Slide the slider all the way to the top, up to "Test client", we want those generated jsp files. Also pick the MySecureWebServiceClient project as client project. That way we keep it separate from the project with the web service. Click finish.

If the appserver was already started, RAD will deploy the project. If not, a prompt is shown to start the server. As soon as your test server is running the wizard can finish the deployment of the test client. A browser with the generated test client will open automatically. If not, point your browser to the TestClient.jsp (in my case http://localhost:9080/MySecureWebServiceClient/sampleMyWebServiceImplPortProxy/TestClient.jsp ) If the endpoint is not specified in the test jsp, update it before testing.


On my machine, the endpoint is http://localhost:9080/MySecureWebService/MyWebServiceImplService This can vary depending on your port (server config), context root (project config) and web service name. The input of Arend will result in "hello Arend", while the sysout reads "MyWebService says hello Arend". Perfect!

Adding security



Now we can place a ws-security layer on top of our service. We will specify a policy set that requires an LTPA token as authentication. LTPA tokens are a WebSphere / IBM single sign on mechanism. ( wikipedia link ) The sample policy set bindings of WebSphere already specify how to generate and consume LTPA tokens, so we can reuse those bindings for this article. Log in to the admin console of your WAS environment, and navigate to Services > Policy sets > Application policy sets and click new :


Give "LTPA Only" as name, and add ws-security as policy:
Click on the ws-security policy we just added. Click main policy. In the main policy screen, unmark "Message level protection" and click apply:


then click "request token policies". Here you can add the tokens that are required for the policy set. Let's add LTPA, and give it a name (for example "LTPAToken):


The policy set bindings define how the policy set should be executed at runtime. If the policy set requires an LTPA token in the header, the client binding should define how an LTPA token is generated, and the provider binding knows how to interpret the token. WebSphere 8 has some policy set bindings packaged for both clients and providers. If you navigate in the admin console to Service > Policy sets > General provider policy set bindings you can see the list:


We only require standard WebSphere LTPA token in our ws-security policy, so we can use the "Provider sample" binding for our provider, and the "Client sample" for the client, because these bindings specify how to handle the LTPA tokens (and more). To attach the policy set we created and the sample binding to the provider, navigate to Services > Service providers, and click on our service "MyWebServiceImplService". As you can see you can attach the policy set + binding on different levels - service, port, or method. I'll stick the LTPA Only policy set to the service here, together with de default binding, Sample provider.

 
Save the configuration. After a new policy set has been created, the server has to be restarted to make it usable, so do that before testing.
Important: if you are deploying your applications directly from your eclipse workspace, make sure the publishing settings for your WAS server is set to "Run server with resources on Server". If the "Run server with resources within the workspace" setting is enabled, the policy set bindings do not work! You can attach them to the providers and clients, you can save it, all looks right, but no policy is active at runtime...

Let's run our test client again. You will most likely get a message like this:
CWWSS5514E: An exception while processing WS-Security message: com.ibm.wsspi.wssecurity.core.SoapSecurityException: CWWSS6521E: The Login failed because of an exception: javax.security.auth.login.LoginException: Login Failure: all modules ignored

What happened? Well, we didn't log in to our test client, so no LTPA token is generated! We can quickly setup basic authentication for our test client, so that a user principle is known when the soap message is created.

A requirement for security in your web application, is that global security is enabled in the WAS appserver AND application security is enabled. By right-clicking on the security roles section of the web deployment descriptor, we can add a role "authenticated".



After opening the web deployment descriptor, we can also add security constraints to /*. Only our role "authenticated" can access /*.


It is convenient to map the role "authenticated" directly to "all authenticated users" in the ear file. Right click on the MySecureWebServiceClientEar project, select JAVA EE > generate websphere bindings deployment descriptor. In the META-INF folder of the ear file, ibm-application-bnd.xml is generated. Add the security role "authenticated" and map it to the special subject "all authenticated users":




Now redeploy the MySecureWebServiceClientEar. If it goes right, your browser will prompt for a username and password when you access the test client page. When we hit the web service, we get our response as expected. Authentication is in place!

By adding
   
@Resource
    WebServiceContext context;

to the web service implementation, we can have access to the context, which will hold the userprinciple.
(I also needed to add the @WebService annotation to the class, otherwise the @Resource did not get injected, and context was null)
@WebService
  public class MyWebServiceImpl

We will add this line to our implementation:
System.out.println("MyWebService was called by " + context.getUserPrincipal());

If we login to the test client page with wasadmin, we expect the line "MyWebService was called by wasadmin". After all, we authenticated with wasadmin, right? However, it prints "MyWebService was called by null".

The Caller



The reason why the principal is null, is because we did not specify the Caller in the provider binding. This caller entry tells the appserver which
token should be used for the java security in the application itself. By setting the ltpa token our caller, we can propagate the user identity.
To set the caller, goto Services > Policy sets > General provider policy set bindings , and click on the "Provider sample" binding.
Remember that we used this default policy set binding for our sample. Click on the policy WS-Security, and click Caller:


Click new to create a new caller
Choose a name for your caller (for example "LTPA", it can be anything you like).
For "Caller identity local part" give the value "LTPAv2" and for "Caller identity namespace URI" the value "http://www.ibm.com/websphere/appserver/tokentype".
These are not chosen randomly. They specify which part of the header contains the identity information.



The possible values for the tokentypes out of the box defined in websphere application server, are listed in the infocenter:
http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/uwbs_wsspsbcald.html

It is possible to define multiple Callers, because it is possible to send different types of tokens. The order of the callers is the order of priority.
As soon as a token in the list of callers is found, that one is used for the identity.


The server should be restarted before we can test the sample, because the policy binding has changed.

"MyWebService was called by wasadmin"

It should work now; the userprinciple can be checked for a certain role.


Sample files


The sample files are available here


Tuesday, June 21, 2011

Top down web service and faults in RAD 8

If you create a topdown web service in RAD 8, using a WSDL you created in RAD with the WSDL wizard and the WSDL editor, java exceptions are not created or generated for the web service faults you added.

The reason for this is that the editor does not add a wsdl:fault to the binding section, when you add a fault to the portType. When you add the wsdl fault manually, the Java Exception will be generated.

I will demonstrate in detail what happens:

In RAD, create a new web project. Create a new WSDL file using the wizard in your project.


With the following options


A WSDL file with skeleton is created. When you open this file using the WSDL editor, it will look like this:


You can right click the operation name to add a fault:


NewOperation now has a fault:


Now we will create a java topdown web service using this wsdl. Right click your WSDL file and choose Generate Java Bean Skeleton:


The Web Service wizard will appear, no special options are selected:


We finish the wizard, and our Java implementation is generated. What do we see? The generated Java implementation has no Exception in its signature!


Now what is missing? The WSDL editor did not add the WSDL fault to the binding. You have to put that in place manually to make the exceptions work.


When we generate the Java Bean Skeleton again, the Exception is present in the signature

Wednesday, May 25, 2011

Conditional mapping in an XSLT in WID

In this tutorial we want to map a value only if it is not empty. If it is empty, we want to use another value.

The input data of our datamap will have 2 fields: cban and iban.  If the iban field is empty, use cban.

Drag a connection from the iban field to the bankaccount field (our target). Change the "move" to "if"



Click in the little blue box of your transformation and make sure you see the Properties view of your transformation. In the Condition tab of the properties, enter the following expression:

$iban!= ''

This states this transformation will only be executed if the iban field does not equal an empty string. Now to define the actual transformation, we need to click the "If" in the blue box of the transformation. This will open a new transformation editor, where you can specify that the iban field should be copied tot the  bankaccount field.





Now how  do we add the "else" clause? Hover your mouse over the blue box of our conditional transformation again (you can reach it by clicking XSLTransformation1_req_1 as seen in the screenshot above).
The first icon will create a new condition (else if), the second one an else.  Click on the "else" icon.
You will see a new blue box appearing beneath your "if" box. This new "else" box needs to be connected as well. Connect the icban value to the "else" box, and the "else" box to the bank account. Also create an actual mapping by clicking on the "else" word.



Regenerate xsl resources in WID

As an update to my post about the xsl resources that could not be found, the generation of xsl mapping files seems even more stubborn than I first thought. The xsl file is indeed generated when the map is tested, but it does not change after you change your actual mapping in the editor. If you test the map again, the previously generated file is used. Even at runtime this is the case.

The one way to regenerate the xsl file for sure is to right-click on the mapping editor, and click "Generate XSLT" (or use the keyboard shortcut Ctrl+Shift+G)


Tuesday, March 22, 2011

Passing parameter to included page using jsp:include in JSF 1.2

If you want to pass a parameter to an included page in JSF 1.2, it is not possible to do it like this:

<jsp:include page="header.jsp">
      <jsp:param value="main" name="pageTitle"/>
</jsp:include>

No way you can retrieve the parameter in header.jsp
This is because the JSF cycle already has built up it's view. Parameters you set while rendering, are not yet in the view that jsf created for this request. You could use the c:set tag and retrieve it using a scriptlet, but we want to do it clean and use no scriptlets in jsf, right.

The trick to do it is by binding a UIInput component in the first page to a certain value, and reading that value in the included file.

Say we have a request scoped bean (called pc_Header) HeaderBean.java with variable of type UIInput and name pageTitle. Then you can set the pagetitle in your main page like this:

<h:inputHidden binding="#{pc_Header.pagetitle}" value="My Title"></h:inputHidden>

This value can be used in the included header like this:

<h:outputText value=#{pc_Header.pagetitle.value}" />

Friday, March 18, 2011

Could not find or load xsl resource in WID testing WebSphere ESB component

When testing a mediation component in WebSphere Integration Developer 7 (WID), my xsl transformation failed with this message in the exception trace:


Caused by: com.ibm.wsspi.sibx.mediation.MediationConfigurationException: CWSXM3110E: Exception while initializing the XSL transformation engine with stylesheet 'xslt/MapInput_req_1.xsl'. This has been reported by the following entity: com.ibm.wbiserver.transform.exceptions.TransformServiceConfigurationException: CWSXM3108E: XSLTransform primitive could not find or load the resource 'xslt/MapInput_req_1.xsl'. This has been reported by the following entity: {1}
at com.ibm.ws.sibx.mediation.primitives.util.ExceptionHelper.newMediationConfigurationException(ExceptionHelper.java:96)
at com.ibm.ws.sibx.mediation.primitives.xslt.XSLTMediation.obtainTransformer(XSLTMediation.java:1380)
at com.ibm.ws.sibx.mediation.primitives.xslt.XSLTMediation.init(XSLTMediation.java:574)
at com.ibm.ws.sibx.scax.mediation.component.ESBFlowBuilder.createJavaMediationPrimitive(ESBFlowBuilder.java:1047)
... 54 more
Refer to the server logs for more information.

And in the systemout.log this trace was available:
 
[17/03/11 13:51:57:880 CET] 00000033 MFCImplementa E   CWSXM0202E: An unexpected exception occurred when processing mediation flow for component AuthenticationMediation in module AuthenticationMediation: CWSXM1025E: An unexpected exception occurred when the flow was called: CWSXM0111E:  Service runtime exception calling an import from mediation component AuthenticationMediation in module AuthenticationMediation: CWSXM0206E: An unexpected exception occurred.: com.ibm.ws.sibx.scax.mediation.component.FlowBuildException: CWSXM1007E: Mediation primitive MapToMember in mediation component AuthenticationMediation in module AuthenticationMediation encountered a problem during initialization.

CWSXM3108E: XSLTransform primitive could not find or load the resource 'xslt/MapToMember_res_1.xsl'.

 When opening the project the Enterprise Explorer, I could see that the xsl file was not there for MapToMember_Res_1.map. Normally the xsl file s are generated when building.

Luckily I found a workaround:
I could force WID to generate the xsl file by testing the transformation in the mapping editor of the transformation.


 Hitting the Test Map button (second from the right), made WID generate the xsl file! Sometimes it was not generated. Then I had to really execute the test before the xsl file was generated...

When deploying the module and testing the component, the xsl file was picked up, and the mediation completed successfully. One the problem persisted. The good old remove, restart, re-add of the project gave the final breakthrough.

Wednesday, March 16, 2011

Publishing failed in WebSphere Integration Developer

When publishing my Module to a brand new created ESB profile in WebSphere Integration Developer (WID), everything runs fine. But hitting the publish button after a change to republish the project was less fun.



"The publish encountered some problems and the application may not have been installed or it may have been successfully installed but was unable to start."
The reason being "Failure uploading archive to server"

I found 2 articles giving a different solution:
This technote 1414552 explains that the hostname verification service should be disabled to make sure the publishing does not mind any unresolved hostnames.


This blogpost on www.soa.si tells you that you can change to hostname in the server configuration, to make the publishing work.

None of the 2 methods worked for me. But thinking about why the hostname could not be resolved, I sudddenly saw the light: I configured a proxy in WID to access the online help system. Adding the hostname of the local machine to the "No Proxy For" list made the publishing successful.
You can find these settings in the preferences screen (window > preferences) in the general section, Network connections.

Friday, February 4, 2011

UnsupportedClassVersionError of the OracleDriver when installing Quickr 8.5 on WebSphere Portal

When you want to install IBM Lotus Quickr 8.5 with oracle as an underlying database, you have to specify the driver jar file in the installation wizard.

Now the funny part is that the installation wizard runs with java 6 jre, and will only accept the ojdbc6.jar file (the drivers compiled with java 6) for the drivers. When the ojdbc5.jar is selected, the wizard pops up an error message.

The installation will fail later on with this message in wpinstall.log:
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] domain       'release'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] DbtDbDriver  'oracle.jdbc.OracleDriver'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] DbtDbLibrary 'C:/drivers/oracle/ojdbc5.jar'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] DbtDbUser    'quickr'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] DbtDbUrl     'jdbc:oracle:thin:@localhost:1521:QUICKR'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [echo] DbtDbName    'QUICKR'
(Feb 2, 2011 2:38:00 PM), Quickr.install, com.ibm.wps.install.ExternalCommandAction$OutputWatcher, msg2, StdOut:      [java] Exception in thread "main" java.lang.UnsupportedClassVersionError: (oracle/jdbc/OracleDriver) bad major version at offset=6

 This clearly indicates that te OracleDriver class is not of the desired version. The underlying WebSphere Portal for the Quickr will use a java 5 runtime.

Now how can we use the ojdbc5.jar driver package during the installation? A good ol' switcheroo of files will do the trick! Rename the ojdbc6.jar driver to ojdbc5.jar, select that file in the wizard, and when you arrive on the summary screen of the wizard (before the installation starts), you replace the renamed ojdbc5.jar by the real ojdbc5.jar.

We fooled the wizard, the installer will use the correct oracle drivers.