Search this blog ...

Wednesday, June 27, 2012

JDeveloper - RIDC JAX/WS SAML Connections to UCM / Content Server

I posted a few years back some really useful information on configuring the infrastructure of the JDeveloper 11g embedded WebLogic server to allow consumption of a JAX/WS Web Service protected by a WSM policy:

http://todayguesswhat.blogspot.com.au/2010/09/jdeveloper-11gr1-integrated-weblogic.html

Today I will extend this information with some useful tips for connecting an application that is deployed to the JDeveloper embedded WebLogic server to connect to a back-end Oracle Content Server (aka Oracle UCM; aka Oracle WebCenter Content Server) using JAX/WS protocol.

Firstly, some background on the configuration of the UCM Server (11.1.1.6.0) and its associated domain:

The UCM Server is deployed to a WebLogic Domain (which I now will refer to as the UCM Domain) configured with WSM policy manager; Such that, the oracle.wsmpm_template_11.1.1.jar template has been applied and subsequently configured to connect to the MDS schema installed by the RCU.

The UCM Domain has been configured with an OID Authentication Provider.

A keystore has been created for the UCM Domain and populated with key-certificate pair signed by the demo certificate authority (CA) that ships with WebLogic.  Note – don’t use this CA in production!

oracle.wsm.security credential store entries have been created for the UCM Domain with the credentials / aliases required to access the keystore contents from above.

GPA Policy has also been configured on the UCM Domain for “ws-service” using message protection service policy oracle/wss11_saml_or_username_token_with_message_protection_service_policy which supports username/password connections and saml connections.

Below are some sample script calls that you can leverage to achieve the above UCM server configuration (assuming Linux):

cd $DOMAIN_HOME/config/fmwconfig

# Create service key-certificate pair signed by the demo CA cert "CertGenCA" with key password welcome1
java utils.CertGen -certfile MyPublicCert -keyfile MyPrivateKey -keyfilepass welcome1 -cn "`hostname -f`"

# Create and populate new service keystore default-keystore.jks with service key-certificate pair from above under alias orakey with keystore password welcome1, and alias password welcome1
java utils.ImportPrivateKey -keystore default-keystore.jks -storepass welcome1 -certfile MyPublicCert.der -keyfile MyPrivateKey.der -keyfilepass welcome1 -alias orakey -keypass welcome1

# Now add the root CA to the service keystore under the alias name CA
keytool -importcert -noprompt -alias CA -file $WL_HOME/server/lib/CertGenCA.der -keystore default-keystore.jks -storepass welcome1

$MW_HOME/oracle_common/common/bin/wlst.sh

wls_username="weblogic"
wls_password="welcome1"
wls_url="t3://localhost:7001"
connect(wls_username, wls_password, wls_url)

createCred(map="oracle.wsm.security", key="keystore-csf-key", user="n/a", password="welcome1", desc="keystore access password")
createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="signing key alias/password")
createCred(map="oracle.wsm.security", key="enc-csf-key", user="orakey", password="welcome1", desc="encryption key alias/password")

serverConfig()

domain_name=cmo.getName()
policy_set_name=domain_name+"-ws-service"
policy_type="ws-service"
resource='Domain("' + domain_name + '")'
policy="oracle/wss11_saml_or_username_token_with_message_protection_service_policy"

beginRepositorySession()
createPolicySet(policy_set_name, policy_type, resource)
attachPolicySetPolicy(policy)
validatePolicySet()
commitRepositorySession()
disconnect()
exit()

Note above, we have leveraged a Message Protection service policy.  If we were to switch to a non-Message Protection service policy such as oracle/wss_saml_or_username_token_service_policy life would be much simpler.  However you need to be extremely careful with deploying such a policy in your enterprise.  You need to understand the consequences, as you may be creating yourself an easily exploitable security hole!!!

The policy oracle/wss_saml_or_username_token_service_policy does NOT do signing/encryption, certificates are NOT used!.
Thus the client does not need a keystore configured.  Also, setting up trusted client DN for the issuer will have no affect.
This profile is in no way shape or form secure and should only be used on a closed internal network where no rogue client can exist.
A rogue client can simply leverage the client policy oracle/wss10_saml_token_client_policy and set BindingProvider.USERNAME_PROPERTY to any user they like and make a connection to the service. 

I exploit this backdoor all the time here at Oracle with our fusion test / WAR room environments.  Effectively, you get an EVP-level visibility bug filed against you (which 98% of time is not a bug – at least in my code ;) ) – and you are forced to troubleshoot with a read-only account (if you are lucky) or some minimal obscure log messages.  Using this trick, I just check the UCM login service to see what policy is applied, and if no message protection policy is defined, I go get myself an admin connection and find out what going on before 92ing the bug!

Anyway, back to the topic at hand.  We are using a Message Protection policy, so the client must have a valid keystore and key-certificate pair in use, and the client’s public certificate must be present in the server’s keystore.  The simplest mechanism in a non-security hardened test environment to achieve this requirement is to simply copy/clone the server’s keystore file to the client, and create identical credential store entries for the oracle.wsm.security properties.  In security hardened environments though, each client domain would have a unique signing/encryption keypair, and the client's associated public certificate must be manually imported in to the server domain's keystore.

Let’s proceed with the JDeveloper embedded WebLogic client steps based on simple cloning of the server’s keystore! If you have not already created the default domain in JDeveloper, do so now, and fire up the server!  I’m running Oracle JDeveloper 11g Release 2 (11.1.2.1.0) on Windows XP.

1. From the JDeveloper View menu, choose Application Server Navigator
2. Right click on Application Servers / IntegratedWebLogicServer, select "Create Default Domain..."
2. Right click on Application Servers / IntegratedWebLogicServer, select "Start Server Instance"

Step 1) Copy from the UCM server its default-keystore.jks file.

/u01/app/oracle/product/Middleware/user_projects/domains/base_domain/config/fmwconfig/default-keystore.jks

Copy this file to the %DOMAIN_HOME%\config\fmwconfig directory of your jdeveloper

e.g. from Command Prompt:

SET DOMAIN_HOME=C:\JDeveloper\system11.1.2.1.38.60.81\DefaultDomain
%DOMAIN_HOME%\bin\setDomainEnv.cmd
cd %DOMAIN_HOME%\config\fmwconfig
## ftp binary get from UCM server the following file
/u01/app/oracle/product/Middleware/user_projects/domains/base_domain/config/fmwconfig/default-keystore.jks

Step 2) Create credential store entries to access keys/certificates from keystore file above (make sure your domain is running!)

%MW_HOME%\oracle_common\common\bin\wlst
connect('weblogic','welcome1','t3://127.0.0.1:7101')
createCred(map="oracle.wsm.security", key="keystore-csf-key", user="n/a", password="welcome1")
createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1")
createCred(map="oracle.wsm.security", key="enc-csf-key", user="orakey", password="welcome1")
exit()

Step 3) Create GPA ws-client policy

%MW_HOME%\oracle_common\common\bin\wlst
connect('weblogic','welcome1','t3://127.0.0.1:7101')

serverConfig()

domain_name=cmo.getName()
policy_set_name=domain_name+"-ws-client"
policy_type="ws-client"
resource='Domain("' + domain_name + '")'
policy="oracle/wss11_saml_token_with_message_protection_client_policy"

beginRepositorySession()
createPolicySet(policy_set_name, policy_type, resource)
attachPolicySetPolicy(policy)
validatePolicySet()
commitRepositorySession()
disconnect()
exit()

Step 4) Configure JDeveloper Embedded WebLogic Domain to use OID Authentication Provider and OID server leveraged by the UCM server.

%MW_HOME%\oracle_common\common\bin\wlst
connect('weblogic','welcome1','t3://127.0.0.1:7101')

edit()
startEdit()
securityRealm = cmo.getSecurityConfiguration().getDefaultRealm();
print("Realm: " + securityRealm.getName())

oid_provider_name="OIDAuthenticator"
oid_host="xxx.us.oracle.com"
oid_port=3060
oid_user="cn=orcladmin"
oid_user_pwd="welcome1"
oid_userbase="cn=Users,dc=us,dc=oracle,dc=com"
oid_groupbase="cn=Groups,dc=us,dc=oracle,dc=com"

oidAP = securityRealm.createAuthenticationProvider(oid_provider_name,"weblogic.security.providers.authentication.OracleInternetDirectoryAuthenticator")
oidAP.setHost(oid_host)
oidAP.setPort(oid_port)
oidAP.setPrincipal(oid_user)
oidAP.setCredential(oid_user_pwd)
oidAP.setUserBaseDN(oid_userbase)
oidAP.setAllUsersFilter("(&(uid=*)(objectclass=person))")
oidAP.setUserNameAttribute("uid")
oidAP.setUseRetrievedUserNameAsPrincipal(true)
oidAP.setGroupBaseDN(oid_groupbase)
oidAP.setControlFlag("SUFFICIENT")

print("Provider order (existing) : ")
print securityRealm.getAuthenticationProviders()
daAP = securityRealm.lookupAuthenticationProvider("DefaultAuthenticator")
diaAP = securityRealm.lookupAuthenticationProvider("DefaultIdentityAsserter")
securityRealm.setAuthenticationProviders([oidAP,daAP,diaAP])
print("Provider order (updated) : ")
print securityRealm.getAuthenticationProviders()

print("Default Authenticator Control Flag (existing) : " + daAP.getControlFlag())
daAP.setControlFlag("SUFFICIENT")
print("Default Authenticator Control Flag (updated) : " + daAP.getControlFlag())

save()
activate(block="true")
disconnect()
exit()

Step 5) Optional - update WLS admins ...

%MW_HOME%\oracle_common\common\bin\wlst
connect('weblogic','welcome1','t3://127.0.0.1:7101')

securityRealm = cmo.getSecurityConfiguration().getDefaultRealm()
roleMapper = securityRealm.lookupRoleMapper("XACMLRoleMapper")
existing = roleMapper.getRoleExpression(None,"Admin")
print("WLS Admins (existing) : " +  existing)

ecm_domain_admin_oid_group_cn="ECM Domain Administrators"

roleMapper.setRoleExpression(None,"Admin",existing + "|Grp(" + ecm_domain_admin_oid_group_cn + ")")
print("WLS Admins (updated) : " +  roleMapper.getRoleExpression(None,"Admin"))

disconnect()
exit()

This should take care of all the infrastructure steps required.  Now it is just a matter of creating a little web application in JDeveloper that leverages RIDC to connect to the UCM Server using JAX/WS protocol and GPA client policy.

File > New > General > Applications > Custom Application
Application Name: RIDCJAXWSTest
Project Name: web
Product Features: JSP and Servlets

Right click on web project > New > Web Tier > Servlets > HTTP Servlet
Web Application Version: Servlet 2.5\JSP 2.1
Servlet class: RIDCTestServlet
Servlet package: web
Mapping details:
Name: RIDCTestServlet
URL Pattern /app

Right click on web project > Project Properties > Libraries and Classpath
Add and select a new user library with classpath referencing oracle.ucm.ridc-11.1.1.jar

Servlet and XML Code

web/RIDCTestServlet.java :-

package web;

import java.io.IOException;
import java.io.PrintWriter;

import java.util.Map;

import javax.servlet.*;
import javax.servlet.http.*;

import oracle.stellent.ridc.IdcClient;
import oracle.stellent.ridc.IdcClientException;
import oracle.stellent.ridc.IdcClientManager;
import oracle.stellent.ridc.IdcContext;

import oracle.stellent.ridc.model.DataBinder;
import oracle.stellent.ridc.model.DataObject;

import oracle.stellent.ridc.protocol.ServiceResponse;

public class RIDCTestServlet extends HttpServlet
{
  private static final String url = "
http://ucmserver.com:16200/idcnativews";
   
  private IdcClient m_idcClient = null;

  public void init(ServletConfig config) throws ServletException
  {
    super.init(config);
    IdcClientManager clientManager = new IdcClientManager();
    try
    {
      m_idcClient = clientManager.createClient(url);
    }
    catch (IdcClientException e)
    {
      e.printStackTrace();
      throw new RuntimeException(e);
    }

    // enable verbose ridc logging to console
    oracle.stellent.ridc.common.log.LogFactory.setLogProvider(
      new oracle.stellent.ridc.common.log.simple.SimpleLogProvider());
  }

  protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
  {
    doPost(request, response);
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
  {
    PrintWriter out = response.getWriter();
    out.print("<html><head><title>RIDC Test Servlet</title></head><body><pre>");
   
    try
    {
   
      out.print("RIDC Version: " + m_idcClient.getVersion() + "<br/><hr/>");
     
      String username = request.getRemoteUser();
      out.print("request.getRemoteUser() = " + username + "<br/><hr/>");
     
      // we are using SAML
      IdcContext userContext = userContext = new IdcContext(username);

      // get the binder
      DataBinder binder = m_idcClient.createBinder();
 
      // populate the binder with the parameters
      binder.putLocal("IdcService", "PING_SERVER");

      out.print("Sending PING_SERVER request ...<br/>");

      // execute the request
      ServiceResponse resp = m_idcClient.sendRequest(userContext, binder);
 
      // get the binder - get a binder closes the response automatically
      DataBinder responseBinder = resp.getResponseAsBinder();
     
      out.print("<hr/>PING_SERVER response ...<br/>");

      DataObject localData = responseBinder.getLocalData();
      for (Map.Entry<String, String> entry : localData.entrySet())
      {
        out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
      }
     
      m_idcClient.logout(userContext); // only new versions of RIDC support this
    }
    catch (Exception e)
    {
      out.print("Exception: <br/><hr/>");
      e.printStackTrace(out);
    }
    finally
    {
      out.print("</pre></body></html>");
    }
  }
}

WEB-INF/web.xml :-

<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app xmlns="
http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
  <servlet>
    <servlet-name>RIDCTestServlet</servlet-name>
    <servlet-class>web.RIDCTestServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RIDCTestServlet</servlet-name>
    <url-pattern>/app</url-pattern>
  </servlet-mapping>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Constraint-0</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>valid-users</role-name>
    </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>myrealm</realm-name>
  </login-config>

  <security-role>
    <role-name>valid-users</role-name>
  </security-role>
</web-app>

 

WEB-INF/weblogic.xml :-

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="
http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-

app_2_5.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd">

  <wls:security-role-assignment>
    <wls:role-name>valid-users</wls:role-name>
    <wls:principal-name>users</wls:principal-name>
  </wls:security-role-assignment>

  <!--
  the users group is automatically made available to the security realm;
  this group represents the set of all authenticated users.
  Any user that successfully authenticates itself is a member of the users group.
  -->
</wls:weblogic-web-app>

 

Sample Output:  (after authenticating as user ecmadmin)

RIDC Version: 11.1.1.6.3.7848

request.getRemoteUser() = ecmadmin

Sending PING_SERVER request ...

PING_SERVER response ...
UserDateFormat=iso8601
UserTimeZone=UTC
ClientEncoding=UTF-8
IdcService=PING_SERVER
dUser=ecmadmin
refreshSubjects=
blDateFormat=yyyy-MM-dd HH:mm:ssZ!tUTC!mAM,PM
ECID-Context=1.8987b47a9955d4f6:328ee95f:1382badfe3d:-8000-0000000000000039;kXjE
refreshMonikers=
changedSubjects=
refreshSubMonikers=
blFieldTypes=xForceFolderSecurity text,xPartitionId text,dInDate date,xReadOnly text,xPartnerLevel bigtext,dMessage message,xInhibitUpdate text,xPartnerProgram bigtext,dCreateDate date,xWebFlag text,xHidden text,xPartnerType bigtext,dReleaseDate date,StatusMessage message,xCollectionID int,xStorageRule text,dOutDate date,xExternalDataSet bigtext,xComments memo,xIdcProfile text
NoHttpHeaders=0
changedMonikers=
idcToken=
StatusMessage=You are logged in as 'ecmadmin'.
hasUserAccessChanged=1
IsJava=1
localizedForResponse=1

2 comments:

  1. Hi ,
    I am getting following error while service key-certificate for UCM /WCC server/

    [aime1@adc4120391 fmwconfig]$ setenv DOMAIN_HOME /scratch/aime1/work/mw7349/user_projects/domains/domain1987
    [aime1@adc4120391 fmwconfig]$ cd $DOMAIN_HOME/config/fmwconfig
    [aime1@adc4120391 fmwconfig]$ pwd
    /scratch/aime1/work/mw7349/user_projects/domains/domain1987/config/fmwconfig
    [aime1@adc4120391 fmwconfig]$ java utils.CertGen -certfile MyPublicCert -keyfile MyPrivateKey -keyfilepass welcome1 -cn "'hostname -f'"
    Exception in thread "main" java.lang.NoClassDefFoundError: utils/CertGen
    Caused by: java.lang.ClassNotFoundException: utils.CertGen
    at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
    Could not find the main class: utils.CertGen. Program will exit.
    [aime1@adc4120391 fmwconfig]$

    ReplyDelete
    Replies
    1. Make sure you source setDomainEnv.sh etc in $DOMAIN_HOME/bin

      Delete