Thursday 18 October 2012

How to invoke secured JAX-WS web service from a standalone client


     
       In this post I will explain the procedure of invoking secured JAX-WS web service from a standalone java client. It explains the problems you may face during the process.
There are several ways by which you can invoke a secured web service. I will explain it in two ways here. In this example I have a sample web service which is protected by the policy : wss_saml_or_username_token_service_policy.
I will not get the desired response if I try to invoke the protected web service without providing a proper username token in the soap header.
In weblogic, you can attach policies to web services with the help of owsm. Following block shows the snippet to be present in the soap header in order to get the client asserted by the web service provider which is protected by wss_saml_or_username_token_service_policy policy :

<S:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" S:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-Kr9QjWbqpQgxYI4CDWNxCg22">
<wsse:Username>administrator</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Passw0rd</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</S:Header>

The username in this case is 'administrator' and password is 'Passw0rd'

Here are the two ways in which you can add the above block in the header of the soap request on the client side :
1. First way is to add credentials in the RequestContext of the client port :

      List<CredentialProvider> credProviders =
          new ArrayList<CredentialProvider>();
      String username = "administrator";
      String password = "Passw0rd";
      CredentialProvider cp =
          new ClientUNTCredentialProvider(username.getBytes(),
                                          password.getBytes());
      credProviders.add(cp);
      Map<String, Object> requestContext =
          ((BindingProvider)sampleWebServicePort).getRequestContext();
      requestContext.put(BindingProvider.USERNAME_PROPERTY,username);
      requestContext.put(BindingProvider.PASSWORD_PROPERTY,password);
      sampleWebServicePort.callService();
     

2.  Second way is add to create the soap security header object which is to be added in the HandlerChain :

        try {
            CustomSOAPHandler sh = new CustomSOAPHandler();
            List<Handler> new_handlerChain = new ArrayList<Handler>();
            new_handlerChain.add(sh);
            ((BindingProvider)sampleWebServicePort).getBinding().setHandlerChain(new_handlerChain);
sampleWebServicePort.callService();
        } catch (Throwable e) {
            e.printStackTrace();
        }

Create a custom SOAPHandler class which will add the header in the soap request.
CustomSOAPHandler:


public class CustomSOAPHandler implements SOAPHandler<SOAPMessageContext> {

    private static final String AUTH_PREFIX = "wsse";
    private static final String AUTH_NS =
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
.
.
.


    public boolean handleMessage(SOAPMessageContext context) {

        try {
            SOAPEnvelope envelope =
                context.getMessage().getSOAPPart().getEnvelope();
            SOAPFactory soapFactory = SOAPFactory.newInstance();
            SOAPElement wsSecHeaderElm =
                soapFactory.createElement("Security", AUTH_PREFIX, AUTH_NS);
            Name wsSecHdrMustUnderstandAttr =
                soapFactory.createName("mustUnderstand", "S",
                                       "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
            wsSecHeaderElm.addAttribute(wsSecHdrMustUnderstandAttr, "1");
            SOAPElement userNameTokenElm =
                soapFactory.createElement("UsernameToken", AUTH_PREFIX,
                                          AUTH_NS);
            Name userNameTokenIdName =
                soapFactory.createName("id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
            userNameTokenElm.addAttribute(userNameTokenIdName,
                                          "UsernameToken-ORbTEPzNsEMDfzrI9sscVA22");
            SOAPElement userNameElm =
                soapFactory.createElement("Username", AUTH_PREFIX, AUTH_NS);
            userNameElm.addTextNode("administrator");
            SOAPElement passwdElm =
                soapFactory.createElement("Password", AUTH_PREFIX, AUTH_NS);
            Name passwdTypeAttr = soapFactory.createName("Type");
            passwdElm.addAttribute(passwdTypeAttr,
                                   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
            passwdElm.addTextNode("Passw0rd");
            userNameTokenElm.addChildElement(userNameElm);
            userNameTokenElm.addChildElement(passwdElm);
            wsSecHeaderElm.addChildElement(userNameTokenElm);
            if (envelope.getHeader() == null) {
                SOAPHeader sh = envelope.addHeader();
                sh.addChildElement(wsSecHeaderElm);
            } else {
                SOAPHeader sh = envelope.getHeader();
                sh.addChildElement(wsSecHeaderElm);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return true;
    }

In this method, we are creating the Security element in the header of the soap request and on the server side it gets asserted successfully.
If the credentials are proper, then your service will get executed else it will throw an exception saying that the username token cannot be validated.


The second way does not require any extra jars to be present in the classpath whereas in first way you will need to add some weblogic jars in classpath in order to get it working.
The problem with the second way is if you try to test it with the help of jdeveloper, you will get the following error :

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Unable to add security token for identity, token uri =http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID
at com.sun.xml.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:197)
at com.sun.xml.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:122)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:125)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:95)

If you analyze the stack trace carefully, you will notice that in this case jdeveloper uses classes from jars (glassfish jars) which are not part of jdk. And that's why you get the strange exception. If you run the same code from eclipse or through command line with just jdk, it will work.


Thursday 4 October 2012

OID Performance Tuning



Oracle Internet Directory is highly scalable and manageable in terms of performance tuning as per the hardware resources and high availability configurations.
In this blog I will explain the parameters which can improve the performance of OID.

1. Database Parameters:
                                Recommended values
sga_target,sga_max_size            upto 60-70% of the available
                                   RAM for database machine
db_cache_size                    upto 60-70% of the available 
                                   RAM for database machine
shared_pool_size                  500M
session_cached_cursors            100
processes                        500
pga_aggregate_target              1-4GB
job_queue_processes               1 or more
max_commit_propagation_delay       99 or lower


2. LDAP Server Attributes:
                                  Recommended values
orclmaxcc                        10 - Number of DB Connections 
                                   per Server Processes
orclserverprocs                  4 - Number of OID LDAP Server 
                                   Processes which should be 
                                   equal to the number of cpu 
                                   cores on the system
orclgeneratechangelog             0 - Disables change log 
                                   generation
orclldapconntimeout               60 - LDAP Connection Timeout
orclmatchdenabled                 0 - Enable MatchDN Processing



3. OID Authenticator Parameters:
    If you have configured Oracle Internet Directory Authenticator in myrealm to retrieve users from OID, following parameters can be changed to optimize the performance:
                                            
                                   Recommended values
Group Membership Searching        limited
Connection Pool Size              120
Connect Timeout                  120
Cache Size                       51200
Cache TTL                        300

4. jps-config Parameters

    If the weblogic server is reassociated to an OID and the application policies are stored in it, following parameters should be added in policystore.ldap serviceInstance in jps-config.xml to make the retrieval of policies faster by caching them.

    <property name="oracle.security.jps.policystore.rolemember.cache.type" value="STATIC"/>
    <property name="oracle.security.jps.policystore.rolemember.cache.strategy" value="NONE"/>
    <property name="oracle.security.jps.policystore.rolemember.cache.size" value="100"/>
    <property name="oracle.security.jps.policystore.policy.lazy.load.enable" value="true"/>
    <property name="oracle.security.jps.policystore.policy.cache.strategy" value="NONE"/>
    <property name="oracle.security.jps.policystore.policy.cache.size" value="1000000"/>
    <property name="oracle.security.jps.policystore.refresh.enable" value="true"/>
    <property name="oracle.security.jps.policystore.refresh.purge.timeout" value="43200000"/>
    <property name="oracle.security.jps.ldap.policystore.refresh.interval" value="6000000"/>
    <property name="oracle.security.jps.policystore.rolemember.cache.warmup.enable" value="true"/>
    <property name="connection.pool.min.size" value="120"/>
    <property name="connection.pool.max.size" value="120"/>
    <property name="connection.pool.provider.type" value="IDM"/>
    <property name="connection.pool.timeout" value="300000"/>
    <property name="connection.pool.provider.type" value="5"/>

   OID and weblogic server restarts are required after modifying the above parameters. They can still be optimized depending on the availability of the hardware resources.
   Ref : http://docs.oracle.com/cd/E23943_01/core.1111/e10108/oid.htm

Tuesday 2 October 2012

OID Policystore Migration



                 Policystore is basically a node in the hierarchical structure of Oracle Internet Directory where all the application policies are stored. There will be cases where you will want to replicate the policystore structure to some other OID instance in development or in production mode. This is useful in cases where you want to maintain the same application policies across multiple environments. Of course you can use the same OID across different environments but it will be very difficult for troubleshooting
                 OID provides few set of commands using which a policystore can be exported to an LDIF file and then that LDIF file can be imported on another OID.
                 This blog explains the use of ldifwrite and bulkload commands which are used to export the policystore or to be specific any node to an LDIF file and import the LDIF file respectively.
                 Following environment variables must be set before proceeding:
  1. WLS_HOME=<path_where_middleware_is_installed>
  2. ORACLE_HOME=$WLS_HOME/Oracle_IDM1
  3. ORACLE_INSTANCE=$ORACLE_HOME/asinst_1
                 Following directories must be added to the PATH variable:
  1. ORACLE_HOME/bin
  2. ORACLE_HOME/ldap/bin
  3. ORACLE_INSTANCE/bin
                OID instance must be shut down before performing bulkload commands to avoid any inconsistencies in the loading.
                    opmnctl stopall

                Follow the below steps to replicate a node in one OID to another :
  • Use ldifwrite to export a node to an ldif file :
        ldifwrite connect="connect_string" basedn="source_dn" file="location.ldif"
     e.g. If you want to export 'cn=mynode,cn=jpsContext,cn=jpsroot' (basedn)  which resides in ODS schema of OIDDB_SOURCE (connect_string as specified in ORACLE_INSTANCE/config/tnsnames.ora) to a file source.ldif which is at location (/u01/export/source.ldif), use the following command :
        ldifwrite connect="OIDDB_SOURCE" basedn="cn=mynode,cn=jpsContext,cn=jpsroot" file="/u01/export/source.ldif"
  • Use bulkload to import the ldif file and generate the intermediate SQL*Loader files :
      bulkload connect="connect_string" check="true" generate="true" recover="true" file="location.ldif"
     e.g. Intermediate SQL*Loader files which are to be executed for ODS schema in OIDDB_TARGET (connect_string) can be generated from /u01/export/source.ldif (file) with the following command :
          bulkload connect="OIDDB_TARGET" check="true" generate"true" recover="true" file="/u01/export/source.ldif"

              check flag parses and verifies the input LDIF file to find any corrupt data
              generate flag generates the intermediate files in SQL*Loader format
              restore flag restores the schema in case any problem arises during bulkload operation
  • Use bulkload to load the intermediate SQL*Loader files in the target OID schema :
       bulkload connect="connect_string" load="true"
      e.g. Intermediate files generated by 2 option can be loaded in the schema OIDDB_TARGET by the following command :
           bulkload connect="OIDDB_TARGET" load="true"

   After the completion of bulkload execution, start the OID server instance:
                 opmnctl startall

  The logs file generated by the bulkload tool are at location :
        ORACLE_INSTANCE/diagnostics/logs/OID/tools
  1. bulkload.log : output log
  2. duplicateDN.log : list of duplicate DNs found during loading
  3. *.ctl and *.dat : intermediate files
        ORACLE_INSTANCE/OID/load
  1. badentry.ldif : list of bad LDIF entries
  2. dynGrp.ldif : list of dynamic group entries that can be added using ldapadd command
  3. bsl_*.log : intermediate log files generated by SQL*Loader
      If there are any errors during indexing phase of loading, following command can re-create the indices :
           bulkload connect="OIDDB_TARGET" index="true"
      Indices can be verified by using the command :
              bulkload connect="OIDDB_TARGET" check="true" index="true"

     In this way you can migrate the application policystore or any node for that matter from one OID instance to another. The detailed explanation of all the attributes used in the above commands can be found at the location http://docs.oracle.com/cd/E25054_01/oid.1111/e10029/bulktools.htm