17 oct 2016

Part II: Change JSESSIONID of WebCenter Content with a deploy-plan

As mentioned on my previous post, I was trying to modify the cookie with a deploy plan of weblogic to avoid file-system modification.

Finally after a few hours of test I found the good XPath syntax.

This is de plan.xml file


<?xml version='1.0' encoding='UTF-8'?>
<deployment-plan xmlns="http://xmlns.oracle.com/weblogic/deployment-plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd" global-variables="false">
  <application-name>cs.ear</application-name>
  <variable-definition>
    <variable>
      <name>ucmParamValue</name>
      <value>UCMJSESSIONID</value>
    </variable>
 <variable>
      <name>ucmParamName</name>
      <value>IdcSessionKey</value>
    </variable>
  </variable-definition>
  <module-override>
    <module-name>cs.ear</module-name>
    <module-type>ear</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-application</root-element>
      <uri>META-INF/weblogic-application.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>application</root-element>
      <uri>META-INF/application.xml</uri>
    </module-descriptor>
    <module-descriptor external="true">
      <root-element>wldf-resource</root-element>
      <uri>META-INF/weblogic-diagnostics.xml</uri>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>cs.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-web-app</root-element>
      <uri>WEB-INF/weblogic.xml</uri>
   <variable-assignment>
  <name>ucmParamValue</name>
  <xpath>/weblogic-web-app/session-descriptor/cookie-name</xpath>
  <operation>add</operation>
   </variable-assignment>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
   <variable-assignment>
  <name>ucmParamName</name>
  <xpath>/web-app/filter/[filter-name="JpsFilter"]/init-param/param-name</xpath>
  <operation>add</operation>
   </variable-assignment>
   <variable-assignment>
  <name>ucmParamValue</name>
  <xpath>/web-app/filter/[filter-name="JpsFilter"]/init-param/[param-name="IdcSessionKey"]/param-value</xpath>
  <operation>add</operation>
   </variable-assignment>
   <variable-assignment>
  <name>ucmParamName</name>
  <xpath>/web-app/filter/[filter-name="IdcFilter"]/init-param/param-name</xpath>
  <operation>add</operation>
   </variable-assignment>
   <variable-assignment>
  <name>ucmParamValue</name>
  <xpath>/web-app/filter/[filter-name="IdcFilter"]/init-param/[param-name="IdcSessionKey"]/param-value</xpath>
  <operation>add</operation>
   </variable-assignment>
   <variable-assignment>
  <name>ucmParamName</name>
  <xpath>/web-app/servlet/[servlet-name="adfAuthentication"]/init-param/param-name</xpath>
  <operation>add</operation>
   </variable-assignment>
   <variable-assignment>
  <name>ucmParamValue</name>
  <xpath>/web-app/servlet/[servlet-name="adfAuthentication"]/init-param/[param-name="IdcSessionKey"]/param-value</xpath>
  <operation>add</operation>
   </variable-assignment>
    </module-descriptor>
  </module-override>
  <module-override>
    <module-name>dav.war</module-name>
    <module-type>war</module-type>
    <module-descriptor external="false">
      <root-element>weblogic-web-app</root-element>
      <uri>WEB-INF/weblogic.xml</uri>
    </module-descriptor>
    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
    </module-descriptor>
  </module-override>
  <config-root>/home/oracle/</config-root>
</deployment-plan>

14 oct 2016

Change JSESSIONID cookie on WebCenter Content / UCM

Sometimes we hate an issue, last week was one of them. Due an incompatibility with a legacy portal that customer already has, I was forced to modify UCM session cookie.

If you check Oracle Support website with this task, you will find this note DocID 1507003.1

Not really usefull for me, isn't it? :-)

You should be familiar with <session-descriptor> under weblogic.xml file

More info: https://docs.oracle.com/cd/E13222_01/wls/docs81/webapp/weblogic_xml.html#1038173

On my first attempt I tried to modify only cookie-path, but for some reason UCM was not capable to get the session from another path.

So I get deeper on the code and tried to modify JSESSIONID name cookie. This are the steps you should follow to.

Locate your "cs.ear" file


Go to your weblogic console of WCC domain in my VM for example
http://wcp:7001/console/

Go to "deployments" option menu

 

Now locate your "WCC" main web-app



Click over the deployment and you will get into details and will find the location of the EAR on the installation.

 

In my case /u01/oracle/middleware/Oracle_ECM1/ucm/idc/components/ServletPlugin/cs.ear

Download the file to your desktop.

Modify descriptors file


Now is necessary to modify web.xml and weblogic.xml

This is how weblogic.xml should look like


<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app" xsi:schemalocation="http://www.bea.com/ns/weblogic/weblogic-web-app.xsd">
 <session-descriptor>
  <persistent-store-type>replicated_if_clustered</persistent-store-type>
  <cache-size>0</cache-size>
  <timeout-secs>1200</timeout-secs>
  <cookie-name>UCMJSESSIONID</cookie-name>
 </session-descriptor>
 <container-descriptor>
  <resource-reload-check-secs>1</resource-reload-check-secs>
 </container-descriptor>
 <security-role-assignment>
  <role-name>SSOrole</role-name>
  <principal-name>users</principal-name>
 </security-role-assignment>
</weblogic-web-app>


As you see I've added the <cookie-name> to the original file.

And this is how web.xml should look like
(only the beggining of the file, the rest is unmodified)


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>cs_servlet</display-name>

 <welcome-file-list>
  <welcome-file>portal.htm</welcome-file>
 </welcome-file-list>

 <!-- [ JPS Integration ] -->
 <filter>
  <filter-name>JpsFilter</filter-name>
  <filter-class>oracle.security.jps.ee.http.JpsFilter</filter-class>
  <init-param>
   <param-name>enable.anonymous</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>remove.anonymous.role</param-name>
   <param-value>false</param-value>
  </init-param>
  <init-param>
   <param-name>application.name</param-name>
   <param-value>IDCCS</param-value>
  </init-param>
  <init-param>
   <param-name>IdcSessionKey</param-name>
   <param-value>UCMJSESSIONID</param-value>
  </init-param>
 </filter>

 <filter-mapping>
  <filter-name>JpsFilter</filter-name>
  <url-pattern>*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
 </filter-mapping>
 <!-- [ End JPS Integration ] -->

 <filter>
  <filter-name>IdcFilter</filter-name>
  <filter-class>idcservlet.IdcFilter</filter-class>
  <init-param>
   <param-name>UseRedirectedAuthPrompt</param-name>
   <param-value>1</param-value>
  </init-param>
  <init-param>
   <param-name>IdcServerType</param-name>
   <param-value>server</param-value>
  </init-param>
  <init-param>
   <param-name>IdcProductName</param-name>
   <param-value>idccs</param-value>
  </init-param>
  <init-param>
   <param-name>IdcSessionKey</param-name>
   <param-value>UCMJSESSIONID</param-value>
  </init-param>
 </filter>

 <filter-mapping>
  <filter-name>IdcFilter</filter-name>
  <url-pattern>*</url-pattern>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
  <dispatcher>REQUEST</dispatcher>
 </filter-mapping>


 <servlet>
  <servlet-name>
   adfAuthentication
  </servlet-name>
  <servlet-class>
   oracle.adf.share.security.authentication.AuthenticationServlet
  </servlet-class>
  <init-param>
   <param-name>allow_success_url_param_overwrite</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>allow_logout_url_param_overwrite</param-name>
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>IdcSessionKey</param-name>
   <param-value>UCMJSESSIONID</param-value>
  </init-param>
 </servlet>


After modifiying this files, you can package them into the .war and the war into the .ear.

Copy the new "cs.ear" file to your system replacing the old one.

Now stop the app and re-deploy it with weblogic console, and reboot the node to take the changes.



Appendix


Modify your Apache .conf file to setup the new cookie name.

I think that is not necessary 100% but I also added to my instance this configuracion under each "<location>" on my apache configuration file.

<Location /cs>
   SetHandler weblogic-handler
   WebLogicCluster server1:16200,server2:16200
   WLCookieName UCMJSESSIONID
</Location>
<Location /idc>
   SetHandler weblogic-handler
   WebLogicCluster server1:16200,server2:16200
   WLCookieName UCMJSESSIONID
</Location>
<Location /adfAuthentication>
   SetHandler weblogic-handler
   WebLogicCluster server1:16200,server2:16200
   WLCookieName UCMJSESSIONID
</Location>

This is all, my next step will be to add this modifications with a deployment plan file, this will make unnecessary to modify "cs.ear" file on the filesystem of our installation.


11 feb 2015

Enable alternative login on Weblogic with Kerberos integration

Recently I was helping some coworkers on their WCP project. This installation has Kerberos authentication configured under weblogic (No OAM)

Almost 70% of users connect from the office, using their Active Directory account to login automatically under WebCenter Portal.



The rest of the users try to connect via Internet and when the Kerberos negotiation fails, this error page appears:



If user does manually a F5 or reload from browser the correct login page of WebCenter Portal is shown.

This issue is quite annoying for an amount of 30% of users, so we tried several solutions
  • Create a double virtual-host under OHS to have one with Kerberos and another without it.
    • This solution only works with OAM product, Kerberos Auth includes a Weblogic provider authentication that always try to negotiate first, and the 401 error page still appears.
  • Create a Login web-app (the good one)
    • Create an ADF TaskFlow that shows a login form and performs a redirection when login is successful
    • Include our new TF under WebCenter Portal shared-library
    • Create a public space with portal builder administration (called external-login)
    • Add new TF to the “external-login” Space under the current Space Catalog
    • Create a blank page on the new Space with our custom taskflow.

This is how our new login page looks like:



Now, regular users under office network still access with the URL:
http://myintranet.company.com/webcenter

And external users login with:
http://myintranet.company.com/webcenter/portal/external-login

To simplify the URL you can create a 301 rule with easy domain name :-)

Documentation:

Downloads: 

Thanks to Daniel & Diana for their work on this issue.

31 oct 2014

NullPointerException on GET_SEARCH_RESULTS

I've just fixed a issue that was getting me crazy, this time we couln't search on our WebCenter Content with OracleTextSearch as indexer.

This is the stacktrace that appears on every search:

>services/3     10.31 13:15:07.970      IdcServer-7     !csUserEventMessage,admin.intranet,10.0.3.21:16200!$ intradoc.common.ServiceException: !csUnableToRetrieveSearchResults!csUnableToExecMethod,getSearchResults
 services/3     10.31 13:15:07.970      IdcServer-7     *ScriptStack GET_SEARCH_RESULTS
 services/3     10.31 13:15:07.970      IdcServer-7     3:getSearchResults,**no captured values**
 services/3     10.31 13:15:07.970      IdcServer-7             at intradoc.server.ServiceRequestImplementor.buildServiceException(ServiceRequestImplementor.java:2176)
 ...
 ...
 services/3     10.31 13:15:07.970      IdcServer-7             ... 36 more
 services/3     10.31 13:15:07.970      IdcServer-7     Caused by: java.lang.NullPointerException
 services/3     10.31 13:15:07.970      IdcServer-7             at java.util.Hashtable.put(Hashtable.java:396)
 ...
 ...
 services/3     10.31 13:15:07.970      IdcServer-7             at intradoc.common.ClassHelperUtils.executeMethod(ClassHelperUtils.java:295)


We think that the issue was regarding the data base or the indexer packages needed, but as trace shows, no communication is done between WCC and DB.

Before our surrender and reinstall all the system, I've decompiled some sources (Oracle don't read this) and found the problem.

The line that launches our NPE was this:

props.put("IntradocServerHostName", SharedObjects.getEnvironmentValue("HttpServerAddress"));

After checking my "config.cfg" file I've found that the HttpServerAddress was commented...

Solution now is simple, uncomment the variable and restart the system... et voila! my search works again.

3 ene 2014

Synchronizing users with ACL metadata boxes

Using WebCenter Content with Access Control List (ACL) enabled. Allow users to setup their custom security based on users and groups that they know.

This is how ACL's looks like under the new WCC UI.


The box of users have an "auto-complete" behavior that helps the search of the user that you want to share your content.

The main problem is that WCC only shows users that already have sign-in or "logged" under the product. This means, that only "active" users will be shown.

To solve this, we can perform a "preload" of all users from our LDAP to the internal DB of WCC. After this loading the auto-complete box will show all the available users of our company.

We need to perform this steps:

Configuring the jps-config.xml

This file has the information relative to our LDAP. In my case Im using ActiveDirectory from Microsoft, by default jps file is configured for Oracle LDAP (OID), that means that will search for "uid" attribute in the person objetc. As most of you know, ActiveDirectory uses the attribute "sAMAccountName" as user id.

You need to modify your WCC domain jps-config.xml file, in my case was under this path:
/opt/oracle/domains/domwc/config/fmwconfig/jps-config.xml

<serviceInstance name="idstore.ldap" provider="idstore.ldap.provider">
    <description>LDAP Identity Store Service Instance</description>
    <property name="idstore.config.provider" value="oracle.security.jps.wls.internal.idstore.WlsLdapIdStoreConfigProvider"/>
    <property name="CONNECTION_POOL_CLASS" value="oracle.security.idm.providers.stdldap.JNDIPool"/>
    <property name="username.attr" value="sAMAccountName"/>
    <property name="user.login.attr" value="sAMAccountName"/>
</serviceInstance>

I've included two properties username.attr and user.login.attr, this properties should be added only if your LDAP does not use the "uid" attribute.

Sync users from LDAP with WCC

For sync the users, I've created a small Java program that connects with LDAP to read all available users, and later performs a call to the service "CHECK_USER_CREDENTIALS" via RIDC. That service will force a connect with the LDAP within UCM, and all the information will be added to the local database of the content server.

The Java process needs the following parameters:
  • LDAP IP or Host
  • LDAP Admin user DN
  • LDAP Admin user password
  • Base DN for user search
The sample JDev project has a RIDC connection setup that points to the RIDC ip & port of your content server, be sure to setup this connection along with the parameters needed of the LDAP server.

After the execution of the program, you can search all your users under ACL boxes ;-)


Documentation

Downloads

10 oct 2013

View SiteStudio Dynamic List on WebCenter Portal application

Last week my coworker Daniel wanted to view the results of a Dynamic List on a WebCenter Portal Content Presenter Template. After a few test we saw that this feature is not suppported.

Under a Content Presenter Template (CPT) you can show any field of the DataFile, simple text, WYSWYG, static list of elements, but seems that the Dynamic List was missing by WebCenter developers ;-)

To solve this issue we created a custom TaskFlow that is capable of listing the elements of a Dynamic List (Element Definition).

First I've created a new Element Definition (ED) of type "Dynamic List", this ED will perform this query:

dExtension <matches> `docx`

This will retrieve all Word documents of the WebCenter Content (WCC).

Next, I've created a Region Definition (RD) with two fields, one of type "Single Text" and another with the DynamicList (ED) just created.


After that, I've created a DataFile based on that Region Definition, this is the resulting XML content:


<wcm:root version="8.0.0.0" xmlns:wcm="http://www.stellent.com/wcm-data/ns/8.0.0">
  <wcm:element name="text">This is a simple text</wcm:element>
</wcm:root>

As you see, there is not track about the ED with the DynamicList, but if you edit that DataFile on WCC interface this is how it looks


So, where is the information of the "Documents" section stored? In the Element Definition called "ED-DYNLIST".

This is the content of the ED file:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<elementDefinition xmlns="http://www.oracle.com/sitestudio/Element/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.oracle.com/sitestudio/Element/ http://www.oracle.com/sitestudio/ss_element_definition.xsd">
    <property value="3" name="type"/>
    <property value="" name="description"/>
    <complexProperty name="flags">
  ...
  ...
    </complexProperty>
    <property value="" name="height"/>
    <complexProperty name="dynlistaddregioncontent">
  ...
  ...
    </complexProperty>
    <dataProperty name="querytext">dExtension <matches> `docx`</dataProperty>
    <property value="dDocTitle" name="sortfield"/>
    <property value="asc" name="sortorder"/>
    <property value="20" name="resultcount"/>
    <property value="false" name="limitscope"/>
    <property value="" name="targetnodeid"/>
</elementDefinition>


As you see the query is stored on <dataProperty> TAG.

Now that we have all the needed information, we can create the TaskFlow that parses the WCC query stored on the ED file, and show the results of that query under a Content Presenter.

Diagram of our TaskFlow


This is how it looks like at the end



Download Links

Documentation

2 oct 2013

ADF Twitter timeline

Yesterday I was playing with twitter widgets, specifically I was trying to add the timeline to my blog, Is quite simple and these are the steps.

Log-in on your twitter account and go to "setup" menu item.



Now go to "Widgets" option and try to create a new one.


You can create timeline by user or by hashtag, in this example is created with #webcenter hashtag.


After the creation, the page will give you the HTML code to embed the timeline on your website. We have to look the <a> tag and save the "href" and "data-widget-id"

<a class="twitter-timeline" href="https://twitter.com/search?q=%23webcenter" data-widget-id="385306900536885248">Tweets sobre "#webcenter"</a>

Now we have the HTML to add the timeline in any page, but this won't work on an ADF application. Lets move this code to a TaskFlow, the first step is to create a JSF fragment for our taskflow that handles the HTML code provided by twitter. This is my fragment code:


<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:trh="http://myfaces.apache.org/trinidad/html">
  <af:panelGroupLayout id="pgl1" layout="vertical">
    <a class="twitter-timeline" href="${pageFlowScope.href}" data-widget-id="${pageFlowScope.id}">Tweets by @${pageFlowScope.referer}</a>
    <trh:script generatesContent="true" id="jsTwitter">
    !function(d,s,id)
    {
        var js, fjs=d.getElementsByTagName(s)[0], p=/^http:/.test(d.location)?'http':'https';
        if(!d.getElementById(id))
        {
            js=d.createElement(s);
            js.id=id;
            js.src="https://platform.twitter.com/widgets.js";
            fjs.parentNode.insertBefore(js,fjs);
        }
    }
    (document,"script","twitter-wjs");
    </trh:script>
  </af:panelGroupLayout>
</jsp:root>

As you see, href and id are taskflow input parameters, when you add the taskflow on your page you have to add the parameters that were kept from last step.

Create a TaskFlow with the previous view as main view. Also add the both parameters as input for the taskflow.

After all, you only have to drop your TaskFlow as a region on a test page, this is my sample configuration:



Finally this is the result of the ADF TaskFlow after loading the twitter timeline.



Download the sample (link) and export as a ADF TaskFlow for the use on your ADF application.