LDSTechForumProjects

ClientLib4J

(Redirected from SSO ClientLib4J)

Portion of Application Development with Single Sign-On presented at 2010 Developer Conference

There are two versions of ClientLib. This page covers the ClientLib4J java version. The ClientLib4X covers the XQuery version for use in Mark Logic applications.

Getting ClientLib

To include ClientLib4J in your maven project add the following to your pom.xml file to obtain the latest version. Consult the ClientLib4J Revisions page to determine if you need any features in later versions than that on which you are currently running. The ClientLib4J Revisions page will be updated as newer versions become available. With the move to Oracle Entitlements Server and the REST interface fronting the server the version has been changed to indicate the version of the REST interface supported in that version of clientlib. If using the simulator the clientlib version is indicative of the value that should be used in the rest-version attribute of the config element. If specified, it should be 'CD-OESv1' which is the default if not specified.


    <dependency>
        <groupId>org.lds.sso</groupId>
        <artifactId>clientlib</artifactId>
        <version>CD-OESv1-1.20</version>
    </dependency>

For applications that are still using the openSSO rest interface the last version of ClientLib4j that supported the openSSO rest interface is version 1.3.2 and can be included in your maven project with the following dependency. If using the simulator the rest-version that should be declared on the config element is 'openSSO'.

    <dependency>
        <groupId>org.lds.sso</groupId>
        <artifactId>clientlib</artifactId>
        <version>1.3.2</version>
    </dependency>

SSOContextFilter

The core enabler for ClientLib4J is a servlet filter. All requests must be channeled through the filter for which SSO headers will be accessed or during the handling of which fine-grained authorization requests will be made to the OES REST service or the canonical helper will be used. Clientlib4j by default expects use of fine-grained permissions and fails fast with each request if no policy-service-url is found or it cannot connect to the indicated location. Alternatively, version CD-OESv1-1.13 added the ability to use clientlib solely for accessing SSO headers without support for fine grained permissions. This allows clientlib4j to not require the policy-service-url header and not even attempt to connect to the service. See the useFineGrainedPermissions condfiguration parameter below. It can be injected either as an init-parameter or by using Spring injection.


The following is an example of setting up the SSO Context Filter using init parameters in web.xml.

   <filter>
        <filter-name>SSO-Context-Filter</filter-name>
        <filter-class>org.lds.sso.clientlib.SsoContextFilter</filter-class>
        <init-param>
            <param-name>useFineGrainedPermissions</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SSO-Context-Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

Alternatively, the following is an example of setting up the SSO Context Filter in the same manner but using Spring to inject the value.

   <filter>
        <filter-name>SSO-Context-Filter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetBean</param-name>
            <param-value>SSO-Context-Filter</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SSO-Context-Filter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

An the application context then the following bean declaration would instantiate the filter and inject the default policy-domain.

    <bean id="SSO-Context-Filter" class="org.lds.sso.clientlib.SsoContextFilter">
        <property name="useFineGrainedPermissions" value="false"/>
    </bean>

Filter Configuration Parameters

The configuration parameters available for the filter are as follows in the following table. All can be set either via init-parm elements in web.xml or via setters on the SsoContextFilter bean that is then delegated to using Spring's DelegatingFilterProxy.


Parameters

Parameter Description
useFineGrainedPermissions Defaults to true. Added in vCD-OESv1-1.12. Tells clientlib to require access to the fine grained permissions REST service and fail fast on any incoming request if it is not accessible. Setting this value to false allows for the method calls on IPolicyClient related to header access to function as defined without requiring the rest service if the application does not leverage fine grained permissions. See IPolicyClient interface for which methods require the rest service and which do not.
defaultPolicyDomain Deprecated as of vCD-OESV1-1.12 since policy-domains are no longer required for applications but are known only by the rest service to which they delegate fine grained permissions requests.
decodePolicyCookies Unused in OAM and OES. Determines if the cookie value must be decoded before sending to the rest service.
useSessionPolicyCaching Defaults to forced. Enhanced in version vCD-OESv1-1.20 adding forced and optional.
  • Supported values:
    • forced (or true for backwards compatibility) creates an HttpSession and caches fine grained permissions answers and header parsing results therein
    • optional caches on the HttpSession if available at inbound request time, otherwise caches locally during request processing and at outbound response time caches on the HttpSession if one was created by the application
    • false performs no caching

Disabling Spring Security

Remember that the SSO environment will be restricting access to the various resources of your application based upon policies that you will need to convey to the SSO environment managers. Hence you do not need something like Spring Security (formerly ACEGI). For users of some ICS Stack based applications Spring Security can be disabled in the defaultContext.properties file (./web/src/main/resources/spring/defaultContext.properties)

    seam.acegi_filter_enabled=false

JSESSIONID Warning

If you plan on using Java's Http Session in addition to SSO for persisting current user state within the application then the JSESSIONID cookie must be a path based cookie. Remember, in the SSO environment your application will be sharing the URL space of a site with other applications. Therefore, if your application does not use path based cookies and another Java application does likewise, as you pass back and forth between them which it perfecly expected behavior in an SSO environment the cookies for each application will clobber each other in the browser effectively loosing all previous state. For WebSphere for example, in the WebSphere console, navigate to: Application servers > [server name] > Session management > Cookies and change the Cookie path value to match the context path at which your application will be located in the shared URL space for the site. This path is what is known as the canonical context for the application.

IPolicyClient Interface

Once the SSOContextFilter is enabled then your code accesses SSO functionality via an instance of the IPolicyClient interface. You obtain an instance anywhere in your code with the following and can then execute its methods. You'll note that the CanonicalHelper class is being used to get the context path of the application which may vary from the context path seen by a Java application server. For example, /mls/mbr is the canonical context (the path seen in the internet) of an application whose application context within its application server is /nextgen-membership. Also note that actions need not be those of the HTTP specification. The actions are whatever string value is specified in the policies for the application. VIEW is used here as an example which is not included in RFC 2616.

import org.lds.sso.clientlib.WebApplicationPolicyClient;
import org.lds.sso.clientlib.IPolicyClient;

...

IPolicyClient ssoClient = org.lds.sso.clientlib.WebApplicationPolicyClient.getPolicyClient();
CanonicalHelper c7l = ssoClient.getCanonicalHelper();

if (ssoClient.isPermitted("VIEW", c7l.getContextPath() + "navigation/recommends")) {
   // do restricted action
}

JSF and JSP Tags

ClientLib4J includes an is-permitted tag in tag libraries for JSF Facelets and JSP. This tag supports the following four attributes.

action This attribute is optional and if not specified has a default value of GET.
uri This attribute is required and should contain the URN starting with a forward slash character, '/', that identifies the resource being protected by some policy located in the policy-domain. It is recommended that applications choose URNs that mimic URLs within the application space although they will not be actual URLs that will be requested by a browser. Placing such URNs in the same namespace as that of the URLs of the application prevents collision with URNs from other applications.

Also, as noted above, if the canonical URL of your application is different from the application space URL due to URL rewriting prior to the request arriving at the app server's port, then the canonical helper class exposed by IPolicyClient and also available as a request attribute with name c7l can be used via expression language to create URNs reflective of the canonical space. This allows policies to contain URNs reflective of the canonical location of the applications that use them.

policyDomain This attribute is optional and if not specified has the default value injected into the SSO Context Filter.
ctx This attribute is optional and if not specified is ignored in policy evaluation. The value of this attribute should be a comma separated list of name-value pairs with names separated from values by an equal sign, '=' which is ASCII character 61 in decimal or 3D in hexadecimal. Empty-value pairs, ie: name and no equals sign or name and equal sign and no value, are dropped. If parsing the string results in no name-value pairs then ctx is ignored in policy evaluation.

These name-value pairs are a way to pass contextual information to the policy engine to be used in evaluating some policies. For example, suppose we have a mapping application having resource of /map/app/move/house that if permitted by a user allows them to move houses in the application to more accurately show the location of a house in the congregational unit being viewed. If granted to Bishops then any Bishop of any unit can move houses which clearly is not the intent. Rather, the unit being viewed can be passes as a contextual parameter and the policy can be crafted to require not only that the user is a Bishop but that he is the Bishop of the unit being viewed and grant or deny access accordingly.

JSF and JSP Examples

For or each URN shown below, if URL rewriting occurred on the incoming request as indicated by a header of "cctx" with a value of "/leader", the result would be a urn of "/leader/navigation/recommends".

For JSP Facelets pages you include the namespace for the taglib org.lds.sso.clientlib.facelets and then use the is-permitted tag.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:c="http://java.sun.com/jstl/core"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:auth="org.lds.sso.clientlib.facelets"
                template="/navigation/home.xhtml">
...
    <ui:define name="tabcontent">
        <div class="recommends-list list-panel">
            <auth:is-permitted action="GET" uri="#{c7l.contextPath}/navigation/recommends">
                <div class="toolbar">
                    <h2>#{messages['section.heading']}</h2>
...
            </auth:is-permitted>
        </div>
    </ui:define>

</ui:composition>

For JSP pages you declare the taglib with its uri org.lds.sso.clientlib.taglib-1.0.

<%@ taglib prefix="auth" uri="org.lds.sso.clientlib.taglib-1.0" %>

...
            <auth:is-permitted action="GET" uri="${c7l.contextPath}/navigation/recommends">
                <div class="events">
...
            </auth:is-permitted>

Unit Testing Applications that Consume clientlib4J

Prior to version CD-OESv1-1.16 unit tests leveraging WebApplicationPolicyClient.getPolicyClient() were unable to inject a mock object to be retrieved from this method call. As of CD-OESv1-1.16 there is now a class, org.lds.sso.clientlib.PolicyClientAccessor, that enables you to inject an IPolicyClient mock object as part of a unit test so that the code under test will retrieve that mock object when it calls WebApplicationPolicyClient.getPolicyClient(). This can be accomplished in the following way. Suppose that we have a class to be tested:


import org.lds.sso.clientlib.WebApplicationPolicyClient;
import org.lds.sso.clientlib.IPolicyClient;
import org.lds.sso.clientlib.PolicyHeader;

public class UserFactory {

  public User getUser() {
    User usr = new User();
    IPolicyClient ssoClient = WebApplicationPolicyClient.getPolicyClient();
    usr.setIndividualId(ssoClient.getPolicyAttribute(PolicyHeader.INDIVIDUAL_ID))
    return usr;
  }
}

To test this code you could do the following. Yes, the dual curly braces in the subclass of PolicyClientAccessor are correct. This is an instance initializer similar to a static initializer. This causes the mocked IPolicyClient instance to be set in Clientlib's internal thread local and be returned later in the test when getUser() is called. There is also a removePolicyClient() method can can be called to remove your mocked object if needed. Calling setPolicyClient() again will replace the existing mock. You can also use @BeforeClass to inject a mock suitable for all unit tests in the class and then use @AfterClass to remove the mock so that it won't inadvertently be called by other tests.

import static org.easymock.classextension.EasyMock.*; 
import static org.testng.Assert.*;

import org.lds.sso.clientlib.IPolicyClient;
import org.lds.sso.clientlib.PolicyClientAccessor;
import org.lds.sso.clientlib.PolicyHeader;

import org.testng.annotations.Test;

public class UserFactoryTest {

  @Test
  public test_getUser() {
    // first inject a client
    new PolicyClientAccessor() {{
        IPolicyClient client = createMock(IPolicyClient.class);
        expect(client.getPolicyAttribute(PolicyHeader.INDIVIDUAL_ID)).andReturn("123");
        replay(client);

        setPolicyClient(client);
    }};

    // now go run the test
    UserFactory fac = new UserFactory();
    User usr = fac.getUser();
    assertEquals(usr.getIndividualId(), "123");
  }
}
This page was last modified on 8 March 2011, at 13:35.

Note: Content found in this wiki may not always reflect official Church information. See Terms of Use.