4 Security Considerations for Developers

This chapter describes how you can write custom modules that enhance security for Oracle Communications Convergence.

Writing a Custom Pluggable SSO Module

This information describes how to write a pluggable custom Single Sign-On (SSO) module for Convergence. Convergence offers two Single Sign-on mechanisms:

  • Access Manager SSO (Legacy Mode and Realm mode).

  • Messaging SSO (also referred to as Trusted Circle SSO).

By itself, the pluggable custom SSO module is not an SSO provider nor is it a replacement for any identity or access management services. Instead, the pluggable custom SSO module allows a site to use SSO between Convergence and another web application, where they all use the same SSO provider for identity or access management.

If you want your deployment to use a different SSO mechanism, you must write and implement an SSO module. Internally, Convergence uses a proxy-auth mechanism to perform SSO with Oracle Communications back-end servers. The back-end servers are: Messaging Server, Calendar Server, and Instant Messaging. Convergence enables you to write custom SSO modules to provide Single Sign-On.

SSO Mechanism in Convergence

As with any SSO-aware application, when a user is authenticated by using Access Manager for example, Convergence loads the authentication module to validate the user. On successful validation, the user is allowed to access the application. If the validation is not successful, the user is redirected to the login page.

Implementing the Custom SSO Module

Before designing a solution for the custom SSO module, Convergence SSO provider framework needs to be implemented:

  • All custom SSO modules must implement SSOProvider interface.

  • Convergence stores and accesses user data in a directory server (Schema 1 or Schema 2). This is called UG LDAP.

  • UG LDAP uses the directory server filter to identity user in UG LDAP.

  • The SSO provider must provide the UG LDAP user identifier and domain identifier.

  • After SSO validation the implementation must provide valid uid and valid domain/organization of the user in UG LDAP. This information will be obtained by SSO framework by invoking getUid() and getDomain() method of custom SSO Provider.

  • The SSO implementation can use any other class that requires custom SSO module to work.

To write a custom SSO module:

  1. Convergence defines a set of interfaces and class that need to be implemented. They are:

    • SSOProvider.java

    • SSOListener.java

      SSOProvider and SSOListener interfaces have to be implemented by the same class.

  2. Configure the SSO module using the iwcadmin command.

The following example shows the reference implementation for SSOProvider.java:

package com.sun.comms.client.security.sso;

import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Custom SSO provider must implement this interface.
*/
public interface SSOProvider {
    /**
     *  SSO framework in Convergence will invoke this method by passing all required SSO configuration, that are configured in configuration.
     *  Implementation can store this info for future use. keys in initConfig are case sensitive.
     */
    public void init(Properties initConfig);

    /**
     * This method will be invoked by SSO framework after calling init() method. Implementation can have SSO validation code here.
     * If SSO validation or Single-Sign-On is successful, this method should return true.
     * If SSO validation succeeds implementation must not create http session here. It is taken care by SSO framework
     */
    public boolean SingleSignOn(HttpServletRequest request,HttpServletResponse response) throws SingleSignOnException;

    /**
     * This method will be invoked by SSO framework when user logs out of application and Single-Sign-Off is enabled in configuration.
     * If SSO validation or Single-Sign-Off is successful, this method should return true.
     * Implementation can perform SSO provider specific Single-Sign-Off here like cleanup SSO cookies in response.
     */
    public boolean SingleSignOff(HttpServletRequest request,HttpServletResponse response) throws SingleSignOffException;

    /**
     * This method will be called by SSO framework if Single-Sign-On succeeds. Implementation must provide a uid (user identifier) of the user
     * in UG LDAP. This will be used by framework to load authenticated user from UG LDAP.
     */
    public String getUid();

    /**
     * This method will be called by SSO framework if Single-Sign-On succeeds. Implementation must provide a domain/organization (domain identifier) of the user
     * in UG LDAP. Framework will use this to locate the user under this domain in UG LDAP.
     */
    public String getDomain();

    /**
     * How much more time SSO token is valid with SSO Provider. Currently not used by framework and hence can be ignored.
     */
    public long getTimeLeft();

}

The following example shows the reference implementation for SSOListener.java:

package com.sun.comms.client.security.sso;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/*
* If SSO provider needs to perform some post Single-Sign-On operation. This interface must be implemented.
*/
public interface SSOListener {
    /**
     * This method will be invoked by framework if Single-Sign-On operation succeeded, user entry is loaded and http session is created.
     * Implementation can do postSignOn related tasks here e.g. registering token listener for sso token notification etc.
     *
     * @param request - Http request for single sign on
     * @param response - Http response for single sign on
     * @param session- Convergence session that is created after successful single sign on
     */
    public void postSignOn(HttpServletRequest request,HttpServletResponse response,HttpSession session);

}

SingleSignOffException - Exception thrown if SingleSignOff fails for any abnormal condition.

SingleSignOnException - Exception thrown if SingleSignOn fails for any abnormal condition.

Note:

While implementing the custom SSO module, iwc.jar should be available in the classpath of development environment. The iwc.jar file requires SSO module classes.

Configuration

Once the required classes for the SSO module are created, you must configure it to work with Convergence server. To configure the SSO module, perform the following operations:

  1. Configure the SSO module using the iwcadmin command:

    iwcadmin -o sso.enable -v "true"
    iwcadmin -o sso.enablesignoff -v "true"
    iwcadmin -o sso.servicename -v CUSTOM_SSO
    iwcadmin -o sso.ssoserviceimpl -v "com.client.sample.CustomSSOProvider"
    iwcadmin -o sso.misc.config_name1 -v "Config_Val1"
    iwcadmin -o sso.misc.cofnig_name2 -v "Config_Val2"
    

    Note:

    All the miscellaneous configuration parameters such as config_name1 and config_name2 and their values are case-sensitive. These parameters should match with the SSOProvider classes' init() method.
  2. Create a JAR file with all custom classes and supporting classes.

  3. If you are using GlassFish Server, copy the JAR file to Convergence - Convergence_Domain/applications/Convergence/WEB-INF/lib directory.

  4. If you are using Oracle WebLogic Server, copy the JAR file to Convergence - Convergence_Domain/servers/Managed_Server/tmp/_WL_user/Convergence/war_folder/war/WEB-INF/lib directory.

    • Shutdown all servers in the domain.

    • Start the Administration Server and all Managed Servers in the domain.

    Note:

    The Administration Server does not automatically copy files in the lib directory to Managed Servers on remote machines. If you have Managed Servers that do not share the same physical domain directory as the Administration Server, you must manually copy JAR file(s) to the Convergence_Domain/servers/Managed_Server/tmp/_WL_user/Convergence/war_folder/war/WEB-INF/lib directory on the Managed Server machines.

About the sso.notifyserviceimpl Parameter

In addition, you might choose to enable the sso.notifyserviceimpl parameter, which can be any user defined class that can listen to events from an SSO provider such as Access Manager. The class name is available through configuration properties passed to the custom SSOProvider implementation class (for example: NotificationServiceImplementation as key). In a custom SSO Provider implementation, you can obtain the class name, create the object, and register it as a listener for SSO events such as token expiration, single sign off notification, and so forth. This implementation is an SSO Provider specific class like AMSDK; it is different from SSOListener.

Custom SSO Implementation Example

The following example (CustomSSOProvider.java) shows a custom SSO reference implementation:

**** BEGIN com/client/sample/CustomSSOProvider.java ****

package com.client.sample;

import com.sun.comms.client.logging.IwcLogger;
import com.sun.comms.client.security.sso.SSOProvider;
import com.sun.comms.client.security.sso.SSOListener;
import com.sun.comms.client.security.sso.RenewSSO;
import com.sun.comms.client.security.sso.SingleSignOffException;
import com.sun.comms.client.security.sso.SingleSignOnException;
import com.sun.comms.client.security.sso.GeneralSSOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Properties;
import org.apache.commons.logging.Log;

public class CustomSSOProvider
implements SSOProvider,SSOListener,RenewSSO
{

  private static final Log logger = IwcLogger.getLogger(IwcLogger.AUTH_LOGGER);

  public CustomSSOProvider() {
    logger.debug("Custom SSO Provider created");
  }

  public void init(Properties props)
  {
    logger.debug("init() called");
  }

  public String getDomain()
  {
    logger.debug("getDomain() called");
    return "domain.com";
  }

  public long getTimeLeft()
  {
    logger.debug("getTimeLeft() called");
    return 3600;
  }

  public String getUid()
  {
    logger.debug("getUid() called");
    return "uid";
  }

  public void refreshSSO(HttpServletRequest request,HttpServletResponse response)
    throws GeneralSSOException {
    logger.debug("refreshSSO() called");
  }

  public boolean SingleSignOn(HttpServletRequest request, HttpServletResponse response)
    throws SingleSignOnException
  {
    logger.debug("SingleSignOn() called");
    return true;
  }

  public void postSignOn(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    String sessionid = session.getId();
    String token = (String) session.getAttribute("USER_TOKEN");
    String cookieValue = "jid=" + sessionid + ":token=" + token;;
    logger.debug("postSignOn() called - create a new cookie SSOIwcAuth=" + cookieValue);

    Cookie cookie = new Cookie("SSOIwcAuth", cookieValue);
    cookie.setPath("/");
    cookie.setDomain(".example.com");
    response.addCookie(cookie);
  }

  public boolean SingleSignOff(HttpServletRequest request, HttpServletResponse response)
    throws SingleSignOffException
  {
    logger.debug("SingleSignOff() called");
    return true;
  }
}

**** END com/client/sample/CustomSSOProvider.java ****

For Oracle WebLogic Server:

[/tmp/test]$ cat compile.sh
#!/usr/bin/bash

echo "Compiling...."
/usr/jdk/instances/jdk1.8.0/bin/javac -classpath \
/opt/sun/comms/iwc/web-src/server/WEB-INF/lib/commons-logging-1.1.jar:/opt/sun/comms/iwc/web-src/server/WEB-INF/lib/iwc.jar:/Weblogic_Home/server/lib/javax.javaee-api.jar \
com/client/sample/CustomSSOProvider.java

echo "Creating JAR file"
/usr/jdk/instances/jdk1.8.0/bin/jar -cvf customsso.jar \
com/client/sample/CustomSSOProvider.class

For GlassFish Server

/tmp/test]$ cat compile.sh
#!/usr/bin/bash

echo "Compiling...."
/usr/jdk/instances/jdk1.6.0/bin/javac -classpath \
/opt/sun/comms/iwc/web-src/server/WEB-INF/lib/commons-logging-api-1.1.1.jar:Glassfish_Home/glassfish/lib/javaee.jar \
com/client/sample/CustomSSOProvider.java

echo "Creating JAR file"
/usr/jdk/instances/jdk1.6.0/bin/jar -cvf customsso.jar \
com/client/sample/CustomSSOProvider.class

Summary

Convergence enables you to write your own custom SSO authentication modules. To write a custom SSO module, the Convergence SSO framework requires that you implement the following interfaces:

  • SSOProvider

  • SSOListener

Additionally, you can also use other classes that help you to implement the SSO module. Finally, you must configure Convergence to use the custom SSO module that you created using the iwcadmin command.

Writing a Custom Authentication Module

Convergence server provides an interface that enables you to create custom user authentication in the form of a customizable Java-based authentication module. The custom authentication module allows an organization to use a non-Oracle directory server mechanism (for example, an RDBMS, flat-text file or third-party directory server) to provide authentication functionality.

By default, Convergence uses Oracle Directory Server Enterprise Edition for authentication store.

Basic Concepts

This section defines the terms and describes the authentication framework architecture and its components.

Convergence uses the following repositories to store user data:

  • User Authentication Store: Contains user credentials, such as user name, password, domain information, and a unique identifier to identify the user in the User or Group directory server store.

  • User/Group LDAP Store (UG LDAP): Contains user preferences such as the user time zone, language preference, and user theme. Convergence uses Schema 1 or Schema 2 to store user information in the User or Group directory server.

Convergence Authentication Framework

This section describes how the authentication framework works in Convergence.

  1. The authentication module first authenticates the user in the authentication store using the configured authentication module. The default authentication module that works by default is Oracle Directory Server Enterprise Edition.

  2. On successful authentication, the authentication module gets the user specific attributes like user ID, the domain of the user, organization, and a unique identifier.

  3. The authentication framework loads the user from the User Group directory server using the user ID (userID) and domain name (userDomain).

Contracts Defined by the Authentication Module

Before designing a solution for the custom authentication module, you must be aware of the contracts that the Convergence authentication framework needs for successful transfer of information between the authentication store, the Convergence authentication framework, and UG LDAP.

  • The authentication module must provide a mechanism to identify a user in the UG LDAP after successful authentication. The custom authentication can have any authentication store that can use any type of identifier to authenticate the user. The authentication mechanism should provide a relationship between the authenticated user and UG LDAP. After successful authentication, the authentication module should provide a unique identifier to locate the user in the UG LDAP. For example, if both authentication store and UG LDAP use the same identifier to locate the user, after successful authentication, the authentication module must set userID and userDomain parameters in the HTTP request by using callback handler objects. These parameters are used by UG LDAP filter to load the user from the UG LDAP. In our example, the user ID (example scott) is the unique identifier used to locate the user in UG LDAP.

  • All the custom authentication modules must implement the following three classes:

    • JAAS LoginModule interface. Convergence uses JAAS LoginModule as an interface for all its login modules. The custom authentication module must implement this interface. Although the authentication module uses the JAAS framework for authentication, it does not use all the advanced capabilities like authentication chaining, and multiple login modules.

    • HttpCallbackHandler. An abstract class that implements the CallbackHandler of JAAS. This class must be implemented to handle custom callbacks. All custom authentication modules must implement this class to handle custom callbacks.

    • Convergence uses the JAAS LoginCallback and CallbackHandler interface to get and set information between the authentication module and Convergence application. Since Convergence is a web application, authentication is performed through HTTP based request and response. Convergence provides an abstract class: HttpCallbackHandler, which implements CallbackHandler interface of JAAS.

  • After successful authentication, the authentication module must set the UserPrincipal object in the Subject. This can be done in commit method of login module. UserPrincipal must be created using loginID of the user.

  • Custom authentication modules must not create HTTP session (HTTPSession) object. Convergence authentication framework takes care of initializing the session.

About the Sample Application

This section describes the various files that are created for the custom authentication module to work. Use this as a reference to create other custom authentication modules to suit your enterprise' needs. The sample authentication module can be used as is by copying the source files and following the steps as mentioned in the following sections.

Caution:

If you must change the core class file names provided in this section, you must appropriately refractor the code. Some files use objects created by other core classes of the custom authentication module.

This example describes an authentication module for a file based user data store. The following is a sample set of data that could be used to store user information in the data store.

userinfo.txt

smith:test:siroe.com
jack:test:siroe.com
scott:test123:siroe.com

In the example, each attribute is separated by a colon. For example, the first record of the file provides information about the user ID smith whose password is test with domain siroe.com.

Implementing the Classes Required for the File-Based Authentication Store

This section describes the classes that are used to implement the authentication module for a file-based user store. The following are the core class:

  • SunTestLoginModule.java

    package com.sun.comms.test;
    
    import com.sun.comms.client.logging.IwcLogger;
    import com.sun.comms.client.security.auth.UserPrincipal;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.Map;
    import javax.security.auth.Subject;
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.login.FailedLoginException;
    import javax.security.auth.login.LoginException;
    import javax.security.auth.spi.LoginModule;
    import org.apache.commons.logging.Log;
    import com.sun.comms.test.SunTestAuthCallBack;
    
    public class SunTestLoginModule implements LoginModule {
    
        private Subject subject;
        private CallbackHandler cbh;
        private Map sharedState;
        private Map options;
        private boolean succeeded;
        private UserPrincipal up;
        private SunTestAuthCallBack mcb = null;
        private String credFile = "";
        private final static Log logger = IwcLogger.getLogger(IwcLogger.AUTH_LOGGER);
    
        public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
            this.subject = subject;
            this.cbh = callbackHandler;
            this.sharedState = sharedState;
            this.options = options;
            credFile = (String) options.get("CredentialFile");
        }
    
        public boolean login() throws LoginException {
            Callback[] callbacks = new Callback[1];
            mcb = new SunTestAuthCallBack();
            callbacks[0] = mcb;
    
            if (cbh == null) {
                throw new LoginException("Error: no CallbackHandler available " +
                        "to gather authentication information from the user");
            }
    
            try {
                // Get userid and pwd from request
                cbh.handle(callbacks);
            } catch (Exception ex) {
                throw new LoginException("SunTestLoginModule: login failed");
            }
    
            String[] userInfo = attemptLogin();
    
            if (userInfo != null && userInfo.length==3) {
                mcb.setUserInfo(userInfo[0], userInfo[2]);
                succeeded = true;
                return true;
            } else {
                System.err.println("Unable to find user entry");
                throw new FailedLoginException("Unable to find user entry");
            }
        }
    
        private String[] attemptLogin() throws LoginException {
    
            if(credFile==null)
                throw new LoginException("User database file is not set configuration.");
    
            File loginFile = null;
            String userID = mcb.getUserName();
            String userPwd = mcb.getUserPwd();
    
            if (userID == null || userPwd == null) {
                throw new LoginException("Required user credential not found");
            }
    
            try {
                loginFile = new File(credFile);
                if (loginFile.exists()) {
    
                    BufferedReader reader = new BufferedReader(new FileReader(loginFile));
                    String userEntry = null;
                    while ((userEntry = reader.readLine()) != null) {
                        String[] usrAcc = userEntry.split(":");
                        if (usrAcc != null && usrAcc.length == 3) {
                            if (userID.equals(usrAcc[0]) && userPwd.equals(usrAcc[1])) {
                                return usrAcc;
                            }
                        }
                    }
    
                } else {
                    System.err.println("Unable to find user database file " + credFile);
                    throw new LoginException("Unable to find user database file " + credFile);
                }
            } catch (IOException ex) {
                System.err.println("Unable to load user database file " + credFile);
                throw new LoginException("Unable to load user database file " + credFile);
            }
            return null;
        }
    
        public boolean commit() throws LoginException {
            if (succeeded == false) {
                return false;
            } else {
                // add a Principal (authenticated identity) to the Subject
                UserPrincipal userPrincipal = new UserPrincipal(mcb.getUserName());
    
                if (!subject.getPrincipals().contains(userPrincipal)) {
                    subject.getPrincipals().add(userPrincipal);
                }
            }
            return true;
        }
    
        public boolean abort() throws LoginException {
            return true;
        }
    
        public boolean logout() throws LoginException {
            return true;
        }
    }
    
  • SunTestAuthCallBack.java

    package com.sun.comms.test;
    
    import com.sun.comms.client.security.auth.LoginCallback;
    import java.io.Serializable;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Locale;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    public class SunTestAuthCallBack implements LoginCallback, Serializable {
    
        HttpServletRequest req;
        HttpServletResponse res;
        String username = null;
        String pwd = null;
    
        protected String name = null;
        protected String host = null;
        protected String user = null;
        protected String userDomain = null;
        protected Locale locale = null;
        protected String serverName = null;
    
        SunTestAuthCallBack(){
    
        }
    
        public void setData(HttpServletRequest request,HttpServletResponse response){
            this.req = request;
            this.res = response;
            username = (String)req.getParameter("username");
            pwd = (String)req.getParameter("password");
    
    
        }
    
        public String getUserName(){
            return username;
        }
    
        public String getUserPwd(){
            return pwd;
        }
    
    
        public void setUserInfo(String uid,String domain){
            req.setAttribute("loginID", uid);
            req.setAttribute("userDomain", domain);
        }
    
        public boolean setData(Object obj) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    
        public Locale getLocale() {
    
          if (locale == null)
              return Locale.getDefault();
    
            return locale;
        }
    
        /**
         * set the client locale
         */
    
        public void setLocale(Locale locale) {
            if (locale != null)
                this.locale = locale;
        }
    
        /**
         * get the host name of the machine running the console.
         * this may be required for auditing purposes
         */
    
        public String getHost() {
    
        if (host == null) {
            try {
                    host = InetAddress.getLocalHost().getHostName();
                } catch (UnknownHostException ukhe) {
            host = null;
                }
        }
    
            return host;
        }
    
        /**
         * set the host name of the machine
         */
    
        public void setHost(String host) {
            if (host != null)
                this.host = host;
        }
    
        @Override
        public void loadData() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }
    
  • AppTestCallbackHandler.java

    package com.sun.comms.test;
    
    import com.sun.comms.client.logging.IwcLogger;
    import com.sun.comms.client.security.auth.modules.HttpCallbackHandler;
    import com.sun.comms.client.web.RequestContextProvider;
    import java.io.IOException;
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.UnsupportedCallbackException;
    import org.apache.commons.logging.Log;
    import com.sun.comms.test.SunTestAuthCallBack;
    
    
    public class AppTestCallbackHandler extends HttpCallbackHandler {
        private final static Log logger = IwcLogger.getLogger(IwcLogger.AUTH_LOGGER);
        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            if (callbacks == null) {
            throw new IOException("Empty or null callback array");
        }
    
        for (int i = 0; i < callbacks.length; i++) {
                 if (callbacks[i] instanceof SunTestAuthCallBack ) {
                    SunTestAuthCallBack nc = (SunTestAuthCallBack)callbacks[i];
                    nc.setData( RequestContextProvider.getHttpRequest(), RequestContextProvider.getHttpResponse());
                    System.err.println("request and response set in AppTestCallbackHandler");
                 }else
                    System.err.println("Callback objects are not instance of SunTestAuthCallBack");
            }
        }
    }
    

How the Implementation Works

For every authentication request, the Convergence authentication framework reads the configured login module class name, call back handler class name and executes it using JAAS framework.

The JAAS framework calls the initialize() method by passing all the required arguments. One important argument is the Map option of the initialize() method. Convergence's authentication framework populates this object with all Misc parameters of CustomJAASService configuration.

In this example, we pass the directory location of user database CredentialFile as part of Misc parameter to SunTestLoginModule. The other arguments are:

  • Subject subject - represents Subject that is being authenticated.

  • CallbackHandler callbackHandler - Object that is responsible for handling custom callbacks.

  • Map sharedState - Not used by Convergence and hence ignore it.

After successful initialization, the login module obtains all the required information about the callback handler and all the required configuration. The JAAS framework then invokes the login() method. This method performs the authentication, which is module specific. In this sample, login() method first creates callback object(s):

Callback[] callbacks = new Callback[1];
mcb = new SunTestAuthCallBack();

The call back object is aware of how to obtain the authentication related information such as the username, password, and so on. This is returned as a HTTP request. Once call back objects are created, it passes callback objects to CallBackHandler's handle method.

cbh.handle(callbacks);

callbackhandler knows how to handle call back objects. For example, the method used for callback object, the data to be passed to it, and so on.

the handle() method of callback handler then calls callback object's setData() by passing request and response objects:

SunTestAuthCallBack nc = (SunTestAuthCallBack)callbacks[i];
nc.setData(
            RequestContextProvider.getHttpRequest(),
            RequestContextProvider.getHttpResponse());

Now, the Callback's setData() extracts the required information from request and response. In this sample, it gets request parameter username and password from request.

this.req = request;
    this.res = response;
    username = (String)req.getParameter("username");
    pwd = (String)req.getParameter("password");

The callback object now has all information that is required to authentication the user from the HTTP request. Now login method() calls an internal method attemptLogin(). This method obtains login information from the callback object:

String userID = mcb.getUserName();
        String userPwd = mcb.getUserPwd();

and loads the user database file and performs authentication. If authentication is successful this method returns String array with userID and userDomain, which is identifier to locate user in UG LDAP.

If attemptLogin() method is successful, login() method sets userID and userDomain info back into HTTP request by calling callback object's setUserInfo() method:

mcb.setUserInfo(userInfo[0], userInfo[2]);

Here, userInfo[0] is unique identified to locate user in UG LDAP. For example, uid and userInfo[2] is domain/organization name in UG LDAP under which user entry is available. This method sets this information as parameters in the HTTP request attribute:

public void setUserInfo(String uid,String domain){
        req.setAttribute("loginID", uid);
        req.setAttribute("userDomain", domain);
    }

The authentication framework uses the loginID and userDomain to get the information from the request. All custom modules must use same names for these parameters. This is mandatory for Convergence' authentication framework. The login() method returns true. Now JAAS framework will call commit() method of LoginModule, where UserPrincipal object is populated into authenticated Subject object. This is mandatory for Convergence' authentication framework.

UserPrincipal userPrincipal = new UserPrincipal(mcb.getUserName());

    if (!subject.getPrincipals().contains(userPrincipal)) {
         subject.getPrincipals().add(userPrincipal);
    }

Here, UserPrincipal object takes userName of the user, which is nothing but unique identifier used to locate user entry in UG LDAP.

On successful completion of the commit() method, the control goes back to Convervgence' authentication framework. This step marks the end of the authentication process. The authentication framework now has all the required information like: loginID, userDomain and authenticated Subject with UserPrincipal objects. The Convergence authentication framework then loads the user from the UG LDAP.

Compiling the Sample Custom Module

If you must change the core class file names provided in this section, you must appropriately refactor the code. Some files use objects created by other core classes of the custom authentication module.

Note:

The paths used in the following example may differ for your installation.
  1. In a temporary directory in /com/sun/comms. For example, create /com/sun/comms/test.

    Note:

    The JAR file must be created by following the Java packaging layout rules. For example, the classes in this sample are packaged as com.sun.comms. So the Java files must be copied under the directory structure: com/sun/comms.
  2. Copy the sample code provided earlier into the files AppTestCallbackHandler.java,SunTestAuthCallBack.java, and SunTestLoginModule.java under /com/sun/comms/ test directory.

  3. Compile the java class files.

    • For GlassFish Server:

      cd /temporary_directory/com/sun/comms/test
      javac -classpath /opt/sun/comms/iwc/web-src/server/WEB-INF/lib/iwc.jar:/opt/sun/comms/iwc/web-src/server/WEB-INF/lib/commons-logging-api-1.1.1.jar:Glassfish_Home/glassfish/lib/javaee.jar
      AppTestCallbackHandler.java SunTestAuthCallBack.java SunTestLoginModule.java
      

      where, GlassFish_Home is the directory in which the GlassFish Server software is installed

    • For Oracle WebLogic Server:

      cd /temporary_directory/com/sun/comms/test
      javac -classpath /opt/sun/comms/iwc/web-src/server/WEB-INF/lib/iwc.jar:/opt/sun/comms/iwc/web-src/server/WEB-INF/lib/commons-logging-api-1.1.1.jar:Weblogic_Home/server/lib/javax.javaee-api.jar
      AppTestCallbackHandler.java SunTestAuthCallBack.java SunTestLoginModule.java
      

      where, Weblogic_Home is the directory in which the Oracle WebLogic Server software is installed

  4. Create a JAR archive.

    cd temporary_directory
    jar -cvf SunTestLogin.jar com
    

    Note:

    If your custom authentication module requires any additional JAR files or classes, these must be bundled along with the JAR file.
  5. Place the JAR file in the following directories.

    • For GlassFish Server: Convergence_Domain/applications/iwc/WEB-INF/lib

    • For Oracle WebLogic Server: Convergence_Domain/servers/Managed Server/tmp/_WL_user/Convergence/war_folder/war/WEB-INF/lib

    Note:

    The custom authentication module must be on the system that can be accessed by Oracle certified application server. It is best to place the JAR file on a location outside of the Convergence installation or deployed directories. See your GlassFish Server or Oracle WebLogic Server documentation for more information.

Configuring the Sample Custom Authentication Module

This section describes the steps to configure the custom authentication module with Convergence. Since this example comes bundled with Convergence server, all that is required is to configure Convergence by setting the appropriate configuration parameters. The following are the instructions to enable the custom authentication module to use a file based authentication store.

To configure the sample custom authentication module:

  1. Set the auth.custom.service name parameter in Convergence to indicate that a custom authentication module is being used.

    iwcadmin -o auth.custom.servicename -v "JAAS_CUSTOM"
    
  2. Set the auth.custom.loginimpl parameter to the login module implementation created for custom authentication module.

    iwcadmin -o auth.custom.loginimpl -v "com.sun.comms.test.SunTestLoginModule"
    
  3. Set the auth.custom.callbackhandler parameter to the custom callback handler used for the custom authentication module.

    iwcadmin -o auth.custom.callbackhandler -v "com.sun.comms.test.AppTestCallbackHandler"
    
  4. Set the auth.misc.CredentialFile parameter to the directory where the authentication store is available. In this case, the authentication store is a file.

    Note:

    Here, the value of auth.misc.CredentialFile is case-sensitive. While reading these parameters inside custom authentication module the name should match the configuration.
iwcadmin -o auth.misc.CredentialFile -v "/var/opt/SUNWiwc/config/userinfo.txt"

If you have created a custom authentication module for a different authentication store, you must follow the steps described below to enable the authentication module to work with Convergence.

  1. Compile the custom authentication module source files and bundle them as a Java archive. See "Compiling the Sample Custom Module" for more information.

  2. Configure Convergence to use the custom authentication module.

    1. Set the auth.custom.service configuration parameter to "JAAS_CUSTOM".

    2. Set the auth.custom.loginimpl configuration parameter to the custom login module implementation of the authentication module

    3. Set the auth.custom.callbackhandler to the call back handler of the custom authentication module.

    4. Set any miscellaneous parameters that you have used for your custom authentication module by setting the auth.misc configuration parameter.

  3. Deploy the custom module. See "Deploying the Authentication Module in the Oracle Certified Application Server" for more information.

Deploying the Authentication Module in the Oracle Certified Application Server

Restart the Oracle certified application server so that the module is updated in the classpath of the application server.

Debugging and Troubleshooting the Custom Authentication Module

This section provides instructions on how to debug and troubleshoot the authentication module.

  1. Set Convergence logging to DEBUG level.

    iwcadmin -o log.AUTH.level -v DEBUG
    
  2. Restart the Oracle certified application server.

  3. Use the tail command to see the log messages generated.

Disabling the Custom Authentication Module

To change the custom authentication module to the default authentication module, run following command.

iwcadmin -o auth.ldap.enable -v true

Restart the Oracle certified application server to ensure that the changes take effect in your deployment.

Summary

This section provides a recap of how to create a custom authentication module.

  • Every custom authentication module should implement the following three classes:

    • A class that implements LoginModule interface

    • A class that extends HttpCallBackHander class

    • A (set of) class(es) that implements CallBack interface

    If your custom authentication module requires other classes that are specific to your implementation of the authentication module, the classes must be implemented.

  • The iwc.jar should be there in classpath, while developing custom authentication module as it uses few Convergence specific classes like HttpCallBackHandler and UserPrincipal.

  • As a best practice, it is good to bundle all dependent classes in a jar file. These should be made available in the web container's class path.

  • Implementation of LoginModule interface and HttpCallBackHander class needs to be configured using the command line interface.

  • Any additional configuration specific to custom authentication module can be configured as Misc parameter using CLI

  • The custom authentication module must set two HTTP request attributes, userid and userDomain after successful authentication.

  • The userDomain must be a valid domain entry in UG LDAP under which, Convergence can uniquely locate user entry by using user id as an identifier.

  • The custom authentication module must create UserPrincipal object using userid and set it in Subject after successful authentication.