Thursday, February 4, 2010

Extending the Universal Content Managament (UCM) Security Model with Oracle Entitlements Server (OES) - Part 1

I've been working with a customer on setting up OES to provide authorization for WebLogic Portal (WLP) and Universal Content Management (UCM). The OES support for WLP is pretty straight-forward and can be found in the product documentation. The OES-UCM integration was a pretty trivial coding exercise, simply combining two existing product samples.

Overview


I've discussed before on the issues around data security and how to apply OES to that problem., but putting it into action, and seeing it work is very cool. Before I get into the details on the integration, I want to explain why you would need to add OES to your UCM solution. UCM has a very sophisticated security model optimized for content management, but there are at least two scenarios I can think of. The first is that you need a common model that goes beyond just UCM. UCM can externalize groups and accounts via LDAP, but some if you need a model richer than "groups", externalization via OES might make sense. The second is if you need very fine grained authorization based on rich context. The most common case is an Attribute Based Access Control (ABAC) model comparing information about users with attributes on the documents/folders in UCM.

In either one of the two scenarios, the basic philosophy is to try to use as much of the OOTB UCM Security functionality as possible, and then use OES to externalize additional security capabilities. In practice, you're effectively ANDing the permissions from OES with the permissions in UCM, so making UCM should deliver higher performance and less work in OES.

Setting up OES with UCM



  • Create an instance of the Java SM - I wanted to use the Java SM for performance reasons. This introduces some other issues, but in OES 10.1.4.3 cp3, there is a sample that demonstrates how to use a custom classloader with the Java SM. This can be very handy when integrating into Java containers because you can avoid/work around a lot of very nasty class loading conflicts.
  • Configure the UsernameIdentity Asserter in the SM - Chris called this thing the "worlds most dangerous identity asserter" and he's right, but from inside a Java container when all you have is the username, I think this will do.
  • Modify the intradoc.cfg to include the Java system properties and classpath for the instance - When the instance is created a file called BEA_HOME/ales32-ssm/java-ssm/instancename/bin/set-env.sh is created. This file contains a long list of Java system properties that need to be added to UCM environment. This is done in intradoc.cfg as follows:


    #Content Server Directory Variables
    IntradocDir=/VZ/opt/ucm/server/
    WebBrowserPath=/usr/bin/firefox

    #Additional Variables
    HTMLEditorPath=/usr/bin/gedit
    JAVA_CLASSPATH_defaultjdbc=$SHAREDDIR/classes/ojdbc14.jar
    FileEncoding=UTF8

    # Start Standard Java SM Properties
    JAVA_OPTIONS_1=-Dwles.scm.port=7063
    JAVA_OPTIONS_2=-Dwles.arme.port=7772
    JAVA_OPTIONS_3=-Dwles.config.signer=poc.us.oracle.com
    JAVA_OPTIONS_4=-Dlog4j.configuration=file:/opt/fmw/ales32-ssm/java-ssm/instance/ucm/config/log4j.properties
    JAVA_OPTIONS_5=-Dlog4j.ignoreTCL=true
    JAVA_OPTIONS_6=-Dwles.ssl.passwordFile=/opt/fmw/ales32-shared/keys/password.xml
    JAVA_OPTIONS_7=-Dwles.ssl.passwordKeyFile=/opt/fmw/ales32-shared/keys/password.key
    JAVA_OPTIONS_8=-Dwles.ssl.identityKeyStore=/opt/fmw/ales32-shared/keys/identity.jceks
    JAVA_OPTIONS_9=-Dwles.ssl.identityKeyAlias=wles-ssm
    JAVA_OPTIONS_10=-Dwles.ssl.identityKeyPasswordAlias=wles-ssm
    JAVA_OPTIONS_11=-Dwles.ssl.trustedCAKeyStore=/opt/fmw/ales32-shared/keys/trust.jks
    JAVA_OPTIONS_12=-Dwles.ssl.trustedPeerKeyStore=/opt/fmw/ales32-shared/keys/peer.jks
    JAVA_OPTIONS_13=-Djava.io.tmpdir=/opt/fmw/ales32-ssm/java-ssm/instance/ucm/work/jar_temp
    JAVA_OPTIONS_14=-Darme.configuration=/opt/fmw/ales32-ssm/java-ssm/instance/ucm/config/WLESarme.properties
    JAVA_OPTIONS_15=-Dales.blm.home=/opt/fmw/ales32-ssm/java-ssm/instance/ucm
    JAVA_OPTIONS_16=-Dwles.scm.useSSL=true
    # End standard Java properties

    # Add this so the Java SM can identify which config to use
    JAVA_OPTIONS_17=-Dwles.realm=ucm
    # The directory where the custom classloader looks for its classes
    JAVA_OPTIONS_18=-Dwles.providers.dir=/opt/fmw/ales32-ssm/java-ssm/lib/providers
    # Helpful debug flag for apache-commons-1.1.1
    JAVA_OPTIONS_19=-Dorg.apache.commons.logging.diagnostics.dest=STDOUT
    # An additional prefix for the UCM resources
    JAVA_OPTIONS_20=-Does.ucm.prefix=ucm/

    # Just log4j.jar and Apache Commons 1.1.1 go here. The rest are loaded using the custom class loader from OES
    CLASSPATH=/opt/fmw/ales32-ssm/java-ssm/lib/log4j.jar:/opt/fmw/ales32-ssm/java-ssm/lib/commons-logging-1.1.1.jar


    The classpath set-up is in two parts. The first is in the intradoc.cfg above. In order for OES to work inside of UCM, you'll need to move to apache commons logging-1.1.1. You can download the file from here. Add it to the BEA_HOME/ales32-ssm/java-ssm/lib directory. The second step is to configure the classes that the custom class loader is going to use. Copy the file BEA_HOME/ales32-ssm/java-ssm/examples/JavaAPIExample-with-customclassloader/JarFileList.txt to BEA_HOME/ales32-ssm/java-ssm/lib. From there modify the file as follows:

    /java-ssm/instance//config
    /java-ssm/lib/oesucmclassloader.jar
    /java-ssm/lib/api.jar
    /java-ssm/lib/css.jar
    /java-ssm/lib/saaj.jar
    /java-ssm/lib/framework.jar
    /java-ssm/lib/scmapi.jar
    #/java-ssm/lib/log4j.jar
    /java-ssm/lib/jmx.jar
    /java-ssm/lib/connector.jar
    /java-ssm/lib/asi_classes.jar
    /java-ssm/lib/EccpressoCore.jar
    /java-ssm/lib/EccpressoJcae.jar
    /java-ssm/lib/jsafeFIPS.jar
    /java-ssm/lib/jsafeJCEFIPS.jar
    /java-ssm/lib/sslplus.jar
    /java-ssm/lib/ssladapter.jar
    /java-ssm/lib/wlcipher.jar
    /java-ssm/lib/asitools.jar
    /java-ssm/lib/webservice.jar
    /java-ssm/lib/webserviceclient.jar
    /java-ssm/lib/org.mortbay.jetty.jar
    /java-ssm/lib/javax.servlet.jar
    /java-ssm/lib/sslserver.jar
    /java-ssm/lib/sslclient.jar
    /java-ssm/lib/pdsoap.jar
    /java-ssm/lib/antlr.jar
    /java-ssm/lib/axis.jar
    /java-ssm/lib/commons-discovery-0.2.jar
    #/java-ssm/lib/commons-logging-1.0.4.jar
    #/java-ssm/lib/commons-logging-1.1.1.jar
    /java-ssm/lib/wsdl4j-1.5.1.jar
    /java-ssm/lib/jaxrpc.jar
    /java-ssm/lib/providers/ales/xercesImpl.jar
    /java-ssm/lib/providers/ales/xml-apis.jar
    /java-ssm/lib/ld-client.jar
    /java-ssm/lib/ld-server-core.jar
    /java-ssm/lib

    Notice that I've commented out log4j.jar, and both on the commons-logging jars. Also, there is a jar file listed oesucmclassloader.jar that needs to be added to the file. You don't have this file yet, but in the next section you'll learn about how to build and finishing deploying the OES-UCM integration
  • One last thing, copy the exampleNames.xml to UCM_HOME/server/bin from ALES_SSM/java-ssm/examples/JavaAPIExample-with-customclassloader/config. This file is naming authority file used by the sample.

Building and Deploying the OES UCM Integration


The OES UCM integration is basically the combination of the Java custom-classloader example, and some of the basic UCM examples. The OES examples are shipped with the product and can be found under BEA_HOME/ales32-ssm/java-ssm/examples/JavaAPIExample-with-customclassloader. The UCM examples (HowToBundle) can be found here. I've worked the two examples together to perform some basic integration and uploaded to https://oes-ucm.samplecode.oracle.com/. I checked in the entire JDeveloper application. Its simple to build, but there are a few things that have to be done.

  • Update the oes-ucm library with the OES 10gR3cp3 and UCM 10gR3 libraries - The OES libraries are in BEA_HOME/ales32-ssm/java-ssm/lib (take all of them), and the ucm library is simply the UCM_HOME/server/shared/classes/server.zip



  • Update the deployment locations of the oescomponent and CustomClassLoader projects - The deployment target for the oescomponent project creates a deployable UCM component. The CustomClassLoader project generates a jar that gets loaded by the system classloader and works with the OES custom classloader (not the classloader itself - so bad jar name on my part ;). Modify the settings to deploy these jars to a valid locations


  • Deploy the oescomponent project - Deploy the ucm jar target. This will not only generate the oes-ucm.jar (the deployable UCM component), but it will also generate the oesucmclassloader.jar.
  • Copy the oesucmclassloader.jar to BEA_HOME/ales32-ssm/java-ssm/lib
  • Deploy the oes-ucm component to UCM - UCM_HOME/server/bin/ComponentTool -install location of oes-ucm.jar. This will create a component called oes
  • Start UCM - If everything works, you should have a Java SM running inside of UCM.

Summary


By combining the Java SM custom classloader example and some of the UCM custom examples, I've created a basic integration between OES and UCM. That integration is available on https://oes-ucm.samplecode.oracle.com. I'm looking forward to sharing more of the details of what the integration actual does in a Part 2 post, very shortly.

9 comments:

  1. Hi Josh,

    Thanks for your post on your blog on the integration of UCM and OES using CustomClassLoader.

    Similarly, I tried running java SSM in weblogic container. Initially, I tried running java ssm in weblogic without any custom class loader and was having some conflicts with class path. Hence, after seeing your blog thought of using the custom class loader shipped with java ssm examples.
    Typically, we have a web service hosted on weblogic container. This webservice calls the OESRuntimeWrapper for the custom class loader piece. And the code is as follows,

    OESRuntimeWrapper runtime = OESRuntimeWrapper.getInstance("Java API Example Application", CONFIGNAME, "exampleResource", "exampleAction");

    And the relevant methods are called on the runtime object.

    The OESRuntimeWrapper and supporting classes are packaged into a jar file (OESCustomClassLoader.jar) and is reference in the startweblogic.cmd classpath.

    The JAVA_OPTIONS and other classpath changes recommended in your blog are applied.

    Now, when I run my webservice, I get the following error.. I see, all the jar files mentioned in JarFile.txt and other files got loaded.. But, the below error is thrown..

    Caused by: java.lang.ClassCastException: weblogic.logging.WLMessageLogger cannot be cast to weblogic.i18n.logging.MessageLogger
    at weblogic.i18n.logging.MessageLoggerRegistry.getRootLogger(MessageLoggerRegistry.java:121)
    at weblogic.i18n.logging.Loggable.(Loggable.java:123)
    at weblogic.logging.Loggable.(Loggable.java:83)
    at weblogic.security.SecurityLogger.logInitializingUsingRealmLoggable(SecurityLogger.java:1230)
    at com.bea.security.internal.css.SecurityServiceManager.initialize(SecurityServiceManager.java:768)
    at com.bea.security.ssal.css.CSSSecurityServiceManagerWrapper.initialize(CSSSecurityServiceManagerWrapper.java:65)

    ReplyDelete
  2. @Vetrichelvam You shouldn't use the Java SM in the WebLogic Server. You should use the WebLogic SM and call it through the same API you would use with the Java SM.

    There's an example in ales32-ssm/wls-ssm/examples directory.

    ReplyDelete
  3. Thanks Chris for your response. Now, we are running into issues with WLS SSM. Would appreciate, if you could provide some support to resolve this issue.

    We have installed WLSSSM and having the following issues,

    We are able to start the weblogic server with default Authorization Provider (XACMLAuthorizer) and Role Mapping Provider (XACMLRoleMapper).

    However, we are NOT able to start the server with Authorization Provider (ASIAuthorizationProvider) and Role Mapping Provider (ASIRoleMapperProvider)

    The following error is thrown when trying to start the weblogic server with WLS SSM.

    ARME is started now


    <Server
    subsystem failed. Reason: weblogic.security.SecurityInitializationException: Use
    r weblogic is not permitted to boot the server; The server policy may have chang
    ed in such a way that the user is no longer able to boot the server.Reboot the s
    erver with the administrative user account or contact the system administrator t
    o update the server policy definitions.
    weblogic.security.SecurityInitializationException: User weblogic is not permitte
    d to boot the server; The server policy may have changed in such a way that the
    user is no longer able to boot the server.Reboot the server with the administrat
    ive user account or contact the system administrator to update the server policy
    definitions.
    at weblogic.security.service.CommonSecurityServiceManagerDelegateImpl.do
    BootAuthorization(Unknown Source)
    at weblogic.security.service.CommonSecurityServiceManagerDelegateImpl.in
    itialize(Unknown Source)
    at weblogic.security.

    ReplyDelete
  4. @Vetrichelvam did you use the ConfigTool to create the SM instance? If you used the config wizard I'd suggest backing out your changes to the WLS config.xml and running the configtool instead. The configtool creates an appropriate set of policies inside OES that allow WLS to boot.

    ReplyDelete
  5. Thanks very much Chris. This issue is resolved. Actually, we used the Configtool only, but after the ssm installation the policies were not properly distributed. So, we created a new ssm instance and everything looks good now. Thanks again.

    -Vetri

    ReplyDelete
  6. Hi Chris,

    Thanks Chris. The issue is resolved now. I had run into another issue now. I deployed a web service into the weblogic domain protected by WLS SSM. Now, when I try to access the webservice, we get access denied. So, I modified the security realm to be DD only and I am able to access the WSDL now.

    However, we are not able to access the operations of the webservice now. I get an error message saying access denied to the operation. Do I need to, modify some configurations so that I can access the service and operations. Would appreciate, if you provide some information.

    Thanks in advance,

    Thanks,
    Vetri

    ReplyDelete
  7. Vetri,

    Take a look at your logs. Access Denied means you don't have an authorization policy granting access. So you don't have to change the configuration but you do need to create a policy.

    If you want the WSDL to be visible publicly to anonymous users you'll need to create the policy to grant access to "anonymous".

    There's info in the manuals online.

    Chris

    ReplyDelete
  8. Hi Josh
    We have created an oes component based on your blog with custom classloader. We have another UCM Service handler which captures the UCM document checking parameters and sends a SOAP request to Oracle Service Bus. We are using JDeveloper to create this service handler and most of the jar files included in weblogic.zip are included in the lib folder. Both the components works fine independently but once loaded together we encounter classpath exception thrown by OES component. We are using following jar files in Service Handler Components by just adding them into lib folder of the component

    !csFailedToInitServer!syServiceRuntime,java.lang.RuntimeException
    at oes.OESRuntimeWrapper.assertIdentity(OESRuntimeWrapper.java:195)
    at oes.oesInstallFilter.doFilter(oesInstallFilter.java:111)
    at intradoc.shared.PluginFilters.filter(PluginFilters.java:94)
    at intradoc.server.IdcExtendedLoader.executeFilter(IdcExtendedLoader.java:302)
    at intradoc.server.IdcExtendedLoader.extraAfterServicesLoadInit(IdcExtendedLoader.java:287)
    at intradoc.server.IdcSystemLoader.loadServiceData(IdcSystemLoader.java:918)
    at intradoc.server.IdcServerManager.init(IdcServerManager.java:118)
    at IdcServer.init(IdcServer.java:64)
    at IdcServer.main(IdcServer.java:37)
    Caused by: java.lang.NullPointerException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at oes.OESRuntimeWrapper.assertIdentity(OESRuntimeWrapper.java:181)
    ... 8 more
    !syExceptionType,java.lang.RuntimeException!syExceptionType,java.lang.NullPointerException
    intradoc.common.ServiceException: !syServiceRuntime,java.lang.RuntimeException
    at oes.OESRuntimeWrapper.assertIdentity(OESRuntimeWrapper.java:195)
    at oes.oesInstallFilter.doFilter(oesInstallFilter.java:111)
    at intradoc.shared.PluginFilters.filter(PluginFilters.java:94)
    at intradoc.server.IdcExtendedLoader.executeFilter(IdcExtendedLoader.java:302)
    at intradoc.server.IdcExtendedLoader.extraAfterServicesLoadInit(IdcExtendedLoader.java:287)
    at intradoc.server.IdcSystemLoader.loadServiceData(IdcSystemLoader.java:918)
    at intradoc.server.IdcServerManager.init(IdcServerManager.java:118)
    at IdcServer.init(IdcServer.java:64)
    at IdcServer.main(IdcServer.java:37)
    Caused by: java.lang.NullPointerException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at oes.OESRuntimeWrapper.assertIdentity(OESRuntimeWrapper.java:181)
    ... 8 more

    at intradoc.server.IdcServerManager.init(IdcServerManager.java:160)
    at IdcServer.init(IdcServer.java:64)
    at IdcServer.main(IdcServer.java:37)
    Caused by: java.lang.RuntimeException
    at oes.OESRuntimeWrapper.assertIdentity(OESRuntimeWrapper.java:195)
    at oes.oesInstallFilter.doFilter(oesInstallFilter.java:111)
    at intradoc.shared.PluginFilters.filter(PluginFilters.java:94)
    at intradoc.server.IdcExtendedLoader.executeFilter(IdcExtendedLoader.java:302)
    at intradoc.server.IdcExtendedLoader.extraAfterServicesLoadInit(IdcExtendedLoader.java:287)
    at intradoc.server.IdcSystemLoader.loadServiceData(IdcSystemLoader.java:918)
    at intradoc.server.IdcServerManager.init(IdcServerManager.java:118)
    ... 2 more

    Regards
    Ron

    ReplyDelete
  9. Caused by: java.lang.NullPointerException

    Looks like on the other side of the reflected method for assertIdentity, you're getting a NPE.

    I would add a try, catch (Throwable t) into that method and see what exactly is going on

    ReplyDelete

Note: Only a member of this blog may post a comment.