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:

And external users login with:

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



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,!$ 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:

<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"/>

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 ;-)



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="" xmlns:wcm="http://www.stellent.com/wcm-data/ns/8.0.0">
  <wcm:element name="text">This is a simple text</wcm:element>

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">
    <property value="" name="height"/>
    <complexProperty name="dynlistaddregioncontent">
    <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"/>

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


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">
        var js, fjs=d.getElementsByTagName(s)[0], p=/^http:/.test(d.location)?'http':'https';

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.

6 ago. 2013

Obtaining user LDAP groups from Weblogic / WebCenter security provider

Sometimes you need to obtain the groups where a user is member. You can check if a user is in a group with a common and simple EL:


This EL will return true if the user is a member of that group.

In other cases you need the full list of groups where users belongs to, you can achieve this purpose with 2 different solutions.

Weblogic mode

     * This method returns the default IdentityStore of Weblogic
     * @return
     * @throws JpsException
    private static IdentityStore getIdentityStore() throws JpsException
        JpsContextFactory ctxf = JpsContextFactory.getContextFactory();
        JpsContext ctx = ctxf.getContext();
        IdentityStoreService storeService = ctx.getServiceInstance(IdentityStoreService.class);
        return storeService.getIdmStore();

     * This method returns a list of the groups where the user belongs to
     * @param userName
     * @return
    public List getRolesFromLDAP()
        List rolesGranted = new ArrayList();
        IdentityStore is = null;
            is = getIdentityStore();
            //Getting current user
            ADFContext ctxt = ADFContext.getCurrent();
            SecurityContext sctxt = ctxt.getSecurityContext();

            User userAux = is.searchUser(sctxt.getUserPrincipal());
            RoleManager rm = is.getRoleManager();
            SearchResponse response = rm.getGrantedRoles(userAux.getPrincipal(), false);
            while (response.hasNext())
                String name = response.next().getName();
                LOG.info("Añadiendo el rol:" + name);
        catch (Exception e)
            LOG.severe("Error obteniendo los grupos del usuario", e);
        return rolesGranted;

WebCenter mode

    public List getRolesWebCenter() throws WCSecurityException
        List roles = new ArrayList();
        ADFContext ctxt = ADFContext.getCurrent();
        SecurityContext sctxt = ctxt.getSecurityContext();
        Collection collection = WebCenterSecurityUtils.getEnterpriseRoles(sctxt.getUserName());
        for (Principal user : collection)
            String userName = user.getName();
            LOG.info("Role:" + userName);
        return roles;

Here is for download a sample project with this two samples.

And this is a capture of the results

27 may. 2013

MouseOut close of ADF Popup with dialog inside.

A couple of months ago I was helping development of ADF 11g application. One of the most important parts of that app was the menu bar.

This menu was designed to support 4 levels, and almost 900 different options items. One of the problems that I faced was that some options of the menu opens a af:popup with an af:dialog inside. That options needs to be opened when user clicks, and self-closed when user moves out of dialog.

In summary, I need the mix of "mouseHover" and "action" types on my <af:showPopupBehavior> tag.

Check the following video to see the menu in action.

To modify the expected behavior of <af:showPopupBehavior> type "action" I've created this simple JavaScript function to overwrite ADF JS library.

AdfShowPopupBehavior.prototype.fire = function (a)
    var b = AdfPage.PAGE, c = this._type, d = a.getSource(), e = d.getClientId(), f = d.findComponent(this._popupId);
    if (AdfPage.PAGE.isScreenReaderMode() && (c == "mouseHover" || c == "mouseMove" || c == "mouseOver" || c == "mouseOut"))
    AdfLogger.LOGGER.fine("showPopupBehavior trigger type " + c + " suppressed in screen reader mode for launch source id: " + e);
    else if (f)
        var g = f._delayedActivationState;
        if (g)
        if (g.launchSourceId == e)
        delete f._delayedActivationState;
        if (f.isPopupVisible())
            var h = this._getPopupWindow(f);
            if (h == null)
            g = this._isInlinePopupSelector(h);
            h = this._wasOpendedFromSameSource(h, e);
            if (g)
                if (f.hide(), h)
            h || f.hide()
        var k = this._align, h = this._alignId, g = 
        g[AdfRichPopup.HINT_LAUNCH_ID] = e;
        var l = this._type == AdfComponentEvent.CONTEXT_MENU_EVENT_TYPE;
        if (h || k || l)
            k && (g[AdfRichPopup.HINT_ALIGN] = k, h || (h = d.getClientId(), k = h.lastIndexOf(":"), k !=  - 1 && (h = h.substring(k + 1))));
            h && ((k = d.findComponent(h)) ? g[AdfRichPopup.HINT_ALIGN_ID] = k.getClientId() : AdfLogger.LOGGER.warning("Unable to find align component: ", h));
            if (l)
            g[AdfDhtmlPopupWindow.HINT_TYPE] = AdfDhtmlPopupWindow.HINT_TYPE_MENU, g[AdfDhtmlPopupWindow.HINT_AUTODISMISS] = AdfDhtmlPopupWindow.HINT_AUTODISMISS_MENU, h || (h = AdfAgent.AGENT.getMousePosition(a.getNativeEvent()), g[AdfDhtmlPopupWindow.HINT_MOUSEPOSITION] = h);
            if (c == "mouseHover" || c == "action")
            g[AdfDhtmlPopupWindow.HINT_AUTODISMISS] = AdfDhtmlPopupWindow.HINT_AUTODISMISS_MOUSEOUT, g[AdfDhtmlPopupWindow.HINT_AUTODISMISS_MOUSEOUT_ID] = g[AdfRichPopup.HINT_ALIGN_ID] ? g[AdfRichPopup.HINT_ALIGN_ID] : g[AdfRichPopup.HINT_LAUNCH_ID]
        a.getType() == AdfUIInputEvent.MOUSE_IN_EVENT_TYPE ? (a = b.scheduleTimer(this, this._onMouseOverTimeout, 
            hints : g, popup : f, source : d
500), f._delayedActivationState = 
            timerId : a, launchSourceId : e
        d.addEventListener(AdfUIInputEvent.MOUSE_OUT_EVENT_TYPE, this._fireCancel, this)) : f.show(g)
    AdfLogger.LOGGER.severe("Could not find popup ", this._popupId, " from component ", d)

The line that changes the expected behavior is this one:

if (c == "mouseHover" || c == "action")

Include that JS fragment in a JS file and later import to your pageTemplate or JSPX page:

<af:resource type="javascript" source="../js/popbehavior.js"/>

Also, you can modify the delay time of mouse-out event modifiying the "500ms" value in the JS code.

 Useful links