Pages

Saturday, July 30, 2011

Custom Authentication Plugin for OAM 10g

Recently I have been involved in developing a custom authentication plugin for Oracle Access Manager 10g.
The plugin was intended to deal with digital certificates issued by many Certification Authorities. This plugin was needed as there are no two CAs that write into the digital certificate the same information in the same place. The issue for me was related to email address of the user holding the digital certificate.
OAM 10g knows how to read the email address from SubjectDN field but does not know how to read it from Subject Alternative Name (X509 extension) field.

The task was simple as OpenSSL library did a perfect job as you can see:

certData = pFnBlock->GetCredFn(pInfo->Creds, "certificate");//read the certificate data from OAM internal structures


//in order to have it processed by OpenSSL you have to add "-----BEGIN X509 CERTIFICATE-----" at the beginning and "-----END X509 CERTIFICATE-----" at the end of the certData variable value.


bio_mem = BIO_new_mem_buf(certData, -1);//read certificate into OpenSSL structures

certificate = X509_new();//create an empty certificate structure
certificate = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);//populate the newly created certificate structure with relevant data
eMailList = X509_get1_email(certificate);//get ALL the emails from the certificate

This was very simple till now :).

Next task was to send some data to backend applications which are protected by OAM. Using action fields in OAM you can send only data persisted in LDAP.
After almost two days of researching I have found out that the solution is again in Oracle Documentation :)

The custom plugin may set action fields by using SetActionFn function just like this:

type = "HeaderVar:";

pFnBlock->SetActionFn(pInfo->ActionInfo,type+"CertSubject.SubjectDN", print_string(pOutputSubject), ObAnSuccessFixedVals);

In OAM there are two possibilities to send data to backend systems:
1. HTTP headers - use HeaderVar
2. cookies - use cookie

ObAnSuccessFixedVals is used to set these action fields in case of successful authentication. You may use ObAnFailFixedVals in order to send these action fields in case of unsuccessful authentication.

Hope to save someone else time by writing this post.

Thursday, March 24, 2011

Clearing JSF view within portlet running in WLP 10.3.2

I have seen in numerous posts over the net that a lot of people are trying to clear the state (JSF view) of an portlet in certain conditions. I have seen this feature in some JSR 329 implementations but not in the one used by WebLogic Portal 10.3.2 and this is why I am trying to explain my solution.

This may be used in scenario when the user needs to reach the default JSF view when a portal page is rendered.

In a step-by-step fashion the solution is the following:

set up an extension class to be used by the portlet
public class ClearStateFacesPortlet extends GenericFacesPortlet {
}
and use it when creating the portlet

<portlet-class>com.oracle.jsf.ClearState.ClearStateFacesPortlet</portlet-class>


set up the portlet to clear its state
add the following init parameter in portlet.xml file:

<init-param>
<name>com.oracle.jsf.CLEAR_STATE_ENABLED</name>
<value>true</value>
</init-param>
This parameter tells the portlet to clear the current state and set up the inital view. This enables the developer to put more portlets on the same portal page some of them having the state cleared out and some keeping their current state.


set up a event to be received by the portlet
Add the following code to the portlet.xml file in order to define the event:

<event-definition>
<qname xmlns:x="http://oracle.com/clearStateEvent">x:clear.state</qname>
<value-type>java.lang.String</value-type>
</event-definition>

Also add the code to the same file for the portlet in order to receive the event:

<portlet>
............
<supported-processing-event>
<qname
         xmlns:x="http://oracle.com/clearStateEvent">x:clear.state</qname>
</supported-processing-event>
<portlet>

set up event processing method for the portlet
@ProcessEvent(qname = "{http://oracle.com/clearStateEvent}clear.state")
public void processClearStateEvent(EventRequest eRequest, EventResponse eResponse)
throws PortletException, IOException {
Event event = eRequest.getEvent();
String eventValue = (String) event.getValue();
String sIsClearStateEnabled = getInitParameter(ClearStatePortletConstants.CLEAR_STATE_PORTLET_PARAM_NAME);
if (!sIsClearStateEnabled.equalsIgnoreCase("true")) {
eResponse.setRenderParameters(eRequest);
return;
}
String sReceivedPortletID = null;
String sReceivedURLValue = null;
try {
StringTokenizer st = new StringTokenizer(eventValue, "@");
sReceivedPortletID = st.nextToken();
                        sReceivedURLValue = st.nextToken();
} catch (Exception e) {
eResponse.setRenderParameters(eRequest);
return;
}
if (!sReceivedPortletID.equals(eRequest.getWindowID())) {
eResponse.setRenderParameters(eRequest);
return;
}
if (!sReceivedURLValue.equalsIgnoreCase("true")) {
eResponse.setRenderParameters(eRequest);
return;
}
eResponse.setRenderParameter(
ClearStatePortletConstants.CLEAR_STATE_RENDER_PARAM_NAME + "."
+ eRequest.getWindowID(), eventValue);
}
The above function will be called whenever the specified event is fired


set up doView() method that will perform the 'magic'

@Override
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, java.io.IOException {
if (request.getParameter(ClearStatePortletConstants.CLEAR_STATE_RENDER_PARAM_NAME + "." + request.getWindowID()) != null) {
request.setAttribute("javax.portlet.faces.viewId", getInitParameter(ClearStatePortletConstants.JSF_DEFAULT_VIEW_PARAMETER));
}
super.doView(request, response);
}

When the portlet is going to be rendered after the event was processed the JSF implementation will know what JSF view will render.

set up page links to activate the 'magic'

In order to trigger the event two solutions are possible:
 - use portlet backing file (weblogic specific)

@Override
public boolean handlePostbackData(HttpServletRequest request,
HttpServletResponse response) {
String urlParameterValue = null;
String queryString = request.getQueryString();
int firstIndex = queryString.indexOf(ClearStatePortletConstants.CLEAR_STATE_URL_PARAM_NAME);
int secondIndex;
if (firstIndex != -1) {
firstIndex = firstIndex + ClearStatePortletConstants.CLEAR_STATE_URL_PARAM_NAME.length() + 1;
secondIndex = queryString.indexOf("&", firstIndex);
if (secondIndex == -1)
secondIndex = queryString.length();
urlParameterValue = queryString.substring(firstIndex, secondIndex);
if (urlParameterValue.equalsIgnoreCase("true")) {
PortletBackingContext.getPortletBackingContext(request).fireCustomEvent(new QName("http://oracle.com/clearStateEvent","clear.state"), PortletBackingContext.getPortletBackingContext(request).getInstanceLabel()+"@"+urlParameterValue);
}
}
   return super.handlePostbackData(request, response);
}

 - use JSF managed beans
//get the URL parameter and check its value and if it is true call the below function with the eventPayload=<portlet_instance_ID>@true

public void sendEvent(String eventPayload)
{
FacesContext fc = FacesContext.getCurrentInstance();
Object obj = fc.getExternalContext().getResponse();
if (obj instanceof ActionResponse){
ActionResponse ar = (ActionResponse) obj;


ar.setEvent(new QName("http://oracle.com/clearStateEvent","clear.state"),eventPayload );
    }
}
both above solutions are possible only if the URL contains the parameter named as in ClearStatePortletConstants.CLEAR_STATE_URL_PARAM_NAME. In order to have all portal pages having this URL parameter you can edit the default URL template in the file beehive-url-template-config.xml as following:

     <url-template>
     <name>default</name>
     <value>
     http://{url:domain}:{url:port}/{url:path}?{url:queryString}{url:currentPage}&amp;com.oracle.jsf.CLEAR_STATE=true
     </value>
    </url-template>


All of this will work if the JSR 329 bridge implementation used by WLP 103.2 is informed to let event processing for the portlet container as per following parameter set up in web.xml file:

<context-param>
<param-name>javax.portlet.faces.autoDispatchEvents</param-name>
<param-value>false</param-value>
</context-param>



The constants used in this example are the followings:

public class ClearStatePortletConstants {
public static final String CLEAR_STATE_URL_PARAM_NAME = "com.oracle.jsf.CLEAR_STATE";
public static final String CLEAR_STATE_RENDER_PARAM_NAME = "com.oracle.jsf.render.CLEAR_STATE";
public static final String CLEAR_STATE_PORTLET_PARAM_NAME = "com.oracle.jsf.CLEAR_STATE_ENABLED";
public static final String CLEAR_STATE_PORTLET_PARAM_DEFAULT_VALUE = "false";
    public static final String JSF_DEFAULT_VIEW_PARAMETER = "javax.portlet.faces.defaultViewId.view";
}