Thursday, July 31, 2014

Filtering Connections Applications

Continuing the Connections integration series, I'm going to talk today about getting your application surfaced in the Connections Navbar, like we do for ProjExec:


Ok, there is no magic this time as this is documented as part of the Connections customization guide. There is a file called apps.jsp that needs to be customized with your own entries. Here is an example:
  Projects 

      --%><tr><%-- 
          --%><th scope="row" class="lotusNowrap"><%-- 
            --%><img class="lconnSprite" 
src="/projexec/tgweb20/media/projexec_blue.gif" alt="" 
role="presentation"><%-- 
            --%><a href="/projexec/projexec/pe_main.xsp"><%-- 
               --%><strong>Projects</strong><%-- 
            --%></a><%-- 
         --%></th><%-- 
     
    --%><td class="lotusNowrap"><%-- 
            --%><a 
href="/projexec/projexec/pe_main.xsp?pepage=myprojects"><%-- 
               --%>My Projects<%-- 
               --%></a><%-- 
         --%></td><%-- 
          
         --%><td class="lotusNowrap lotusLastCell"><%-- 
            --%><a 
href="/projexec/projexec/pe_main.xsp?pepage=myportfolios"><%-- 
               --%>My Portfolios<%-- 
            --%></a><%-- 
         --%></td><%-- 
      --%></tr><%-- 

But wait, some customers asked us to hide these entries for the users that are not entitled to ProjExec. Generally, these users are identified using one of their attributes in the enterprise directory, like a group membership, an organization or even a custom attribute.

Then comes the real thing. In ProjExec, we use J2EE roles to control the access to the application, and then we map the roles to LDAP queries (filters). But  these roles are declared in our EAR, and are only valid in our own application context (war file). On the other hand, the NavBar is executing within all Connections applications contexts, where our roles are just unknown. As a result, we can't then use roles and role mapping for it.

Of course, we can use a direct call to LDAP using JNDI. But this has several drawbacks:
  • We need information to connect to the server: address, user/password...
  • We need to know the LDAP schema used by the actual directory (objectclass, attributes...)
  • We should implement a cache to get efficiency, as this code will be executed every time the navigation bar is displayed
Fortunately, WebSphere exposes a generic directory API that hides the points exposed above. It is called Virtual Member Manager, a.k.a VMM. Of course the app server has to be properly configured. I highly recommend the use of the Federated Repositories in this case.
To check if WAS is properly configured, go to the "User and Groups" in the admin console and verify that it accesses both the users (in Manage Users) and the groups (in Manage Groups). Make sure that the org name is not duplicated and the groups properly retrieved (both can happen when the LDAP directory is a domino server)
Example:


Ok, once WAS is setup, then let's add the real code. In our case, we'd like to enable the ProjExec menu entries if the current user belongs to the 'projexec' group.
We did that by creating a simple hasProjExecRole() method, and then wrap the ProjExec menu items within a test (all in blue bellow). See how the WSCredential API from WebSphere is being used, independently of the actual directory server being used.

Voila, this is it.

=== DO NOT CHANGE ===

   The <lc-ui:serviceLink /> tag can be used to generate links to any service defined in
   LotusConnections-config.xml.

--%>
<%!
   public boolean hasProjExecRole() {
      try {
         java.util.Set creds = com.ibm.websphere.security.auth.WSSubject.getCallerSubject().getPublicCredentials(com.ibm.websphere.security.cred.WSCredential.class);
         for(Object _c: creds) {
            com.ibm.websphere.security.cred.WSCredential c = (com.ibm.websphere.security.cred.WSCredential)_c;
            java.util.List groups = c.getGroupIds();
            if(groups.contains("ProjExec")) {
               return true;
            }
         }
      } catch(Exception ex) {
         ex.printStackTrace();
      }
      return false;
   }
%>

<div role="document"><table class="lotusLayout lotusNavMenuLarge" cellpadding="0" cellspacing="0"><%-- 
   
  Projects 

   --%><c:if test="<%=hasProjExecRole()%>"><%--
      --%><tr><%-- 
          --%><th scope="row" class="lotusNowrap"><%-- 
            --%><img class="lconnSprite" 
src="/projexec/tgweb20/media/projexec_blue.gif" alt="" 
role="presentation"><%-- 
            --%><a href="/projexec/projexec/pe_main.xsp"><%-- 
               --%><strong>Projects</strong><%-- 
            --%></a><%-- 
         --%></th><%-- 
     
    --%><td class="lotusNowrap"><%-- 
            --%><a 
href="/projexec/projexec/pe_main.xsp?pepage=myprojects"><%-- 
               --%>My Projects<%-- 
               --%></a><%-- 
         --%></td><%-- 
          
         --%><td class="lotusNowrap lotusLastCell"><%-- 
            --%><a 
href="/projexec/projexec/pe_main.xsp?pepage=myportfolios"><%-- 
               --%>My Portfolios<%-- 
            --%></a><%-- 
         --%></td><%-- 
      --%></tr><%-- 
   --%></c:if><%--