Monday, June 4, 2012

Protecting a WebCenter app with OAM 11g

Last year I wrote an article on OAM and ADF Applications with Anonymous access. This week I did some work with another A-Team guy building on that previous work.

The new requirement was that the customer wanted two different portions of the app to be protected by different login pages. In other words a user would start on an unprotected page and then choose to go to "A" or "B". If you click on "A" you see one login page and then (after logging in) you see page A. If you click on B you see a different login page and then page B.

If I was doing this with plain old HTML or JSPs I would probably just put A's resources in directory /a/ and B's resources in /b/ and we'd be done. But as you know from the previous post things can get a little bit more complex when you are using ADF.

Martin is going to blog more deeply about the ADF side of the story (link to follow) and I'm blogging about the OAM setup side.

Martin's post is now available on the A-Team's WebCenter blog.


The first thing you as a security person (as opposed to a WebCenter/ADF/JSF programmer type) need to remember with ADF is that URLs aren't what you might expect. In the project tree you'll see stuff like this:


But the browser don't necessarily see the JSPX file names directly in the URL. Instead the browser window shows things like this:


What's more when you click on a button or a link the URL doesn't necessarily update the way you expect.

Or put more simply, all of that stuff you think about when protecting simple JSPs or other web apps may not apply when you're talking about ADF and WebCenter-based apps.

From my previous post you'll probably recall that the simple way to protect only one part of an ADF app is to protect /adfAuthentication with OAM and then rely on ADF security to secure the app. Which works perfectly if you want one login page for the entire app. In this case the customer wants a single application to have multiple login pages depending on where the user is trying to go. Now normally I'd say "that's silly. The whole point of building an SSO infrastructure is that you have one login process and it works the same for every application", but sometimes there are good reasons to do the unreasonable.

So what we, or more accurately Martin, did was to create URLs for those pages in the Navigation Model (default-navigation-model.xml in this particular project). The XML looks like this:

<?xml version="1.0" encoding="US-ASCII" ?>
<navigationDefinition description="Default Navigation" 
               id="default-navigation-model"
               name="Default Navigation"
               xmlns="http://xmlns.oracle.com/adf/rcs/catalog" visible="#{true}">
  <contents>
    <url id="public_Home"
         factoryClass="oracle.webcenter.portalframework.sitestructure.rc.AdfPageResourceFactory"
         url="page://oracle/webcenter/portalapp/pages/public/public_home.jspx"
         visible="#{true}">
      <attributes>
        <attribute value="Home" attributeId="Title" isKey="false"/>
      </attributes>
    </url>
    <url id="secA_Home"
         factoryClass="oracle.webcenter.portalframework.sitestructure.rc.AdfPageResourceFactory"
         url="page://oracle/webcenter/portalapp/pages/secured/secured_A_home.jspx"
         visible="#{true}">
      <attributes>
        <attribute value="Group A Home Page" attributeId="Title" isKey="false"/>
      </attributes>
    </url>
    <url id="secB_Home"
         factoryClass="oracle.webcenter.portalframework.sitestructure.rc.AdfPageResourceFactory"
         url="page://oracle/webcenter/portalapp/pages/secured/secured_B_home.jspx"
         visible="#{true}">
      <attributes>
        <attribute value="Group B Home Page" attributeId="Title" isKey="false"/>
      </attributes>
    </url>
  </contents>

Then in the unprotected content area when you want to link to the various protected pages you just use the pretty URL. Like so:

<af:forEach var="node" varStatus="vs"
                          items="#{navigationContext.defaultNavigationModel.listModel['startNode=/, includeStartNode=false']}">
                <af:spacer width="10" height="10" id="s1"/>
                <af:panelGroupLayout id="pgl2" layout="vertical"
                                     inlineStyle="border:blue solid 1px">
                  <af:goLink id="pt_gl1" text="#{node.title}"
                             destination="#{node.goLinkPrettyUrl}"
                             targetFrame="#{node.attributes['Target']}"
                             inlineStyle="font-size:large;#{node.selected ? 'font-weight:bold;' : ''}"/>
                  <af:spacer width="10" height="10" id="s2"/>
                  <af:outputText value="#{node.goLinkPrettyUrl}" id="ot2"
                                 inlineStyle="font-size:medium; font-weight:bold;"/>
                </af:panelGroupLayout>
              </af:forEach>

I'm not an ADF guy and I don't play one on the Internet, but apparently if you know ADF this makes complete sense.

Anyway, what all that means is that the "Pretty URL" translates to /secA_home and /secB_home we defined in the navigation model file. Which means we have URLs that OAM can recognize. Once you have that you can use the OAM policies you know and love:

  • the app itself is located at /OAM_WC/ - which we unprotect
  • there are a bunch of ADF related bits inside the root of the app - so we unprotect those
  • most of the actual user visible URLs are inside /OAM_WC/faces/ - which we also want unprotected by default
  • we want to protect /OAM_WC/faces/secA with an Authentication Policy that uses the "A" login page
  • and we want to protect /OAM_WC/faces/secB with an Authentication Policy that uses the "B" login page
The policies look like this:


Then all that's left is the auth schemes - I created two schemes that are virtually identical:


If you look closely you notice that we wrote the login page as a JSPX and gave a fully qualified URL. That's because we wanted to create the login page as part of the application. Again, I generally dissuade you from doing this, but sometimes you have to. When you do this just remember that you need the login page to submit back to the OAM server and NOT to the WLS server where your application is deployed. Here's what that looks like in the JSPX file itself:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document title="Settings" id="d1">
      <af:panelGroupLayout id="pgl1" layout="vertical"/>
      <af:outputText value="LOGIN FORM FOR A" id="ot1"/>
      <form id="loginform" name="loginform" method="POST"
            action="http://XXXXXXX.us.oracle.com:14100/oam/server/auth_cred_submit">
        <table>
          <tr>
            <td align="right">username:</td>
            <td align="left">
              <input name="username" type="text"/>
            </td>
          </tr>
           
          <tr>
            <td align="right">password:</td>
            <td align="left">
              <input name="password" type="password"/>
            </td>
          </tr>
           
          <tr>
            <td colspan="2" align="center">
              <input value=" login " type="submit"/>
            </td>
          </tr>
        </table>
        <input name="request_id" type="hidden" value="${param['request_id']}"
               id="itsss"/>
      </form>
    </af:document>
  </f:view>
</jsp:root>

Hope this helps!

No comments:

Post a Comment

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