Writing a Logon Exit

Documentation home

 

Overview. 1

New and old style logon exits 1

Configuration. 2

Code Examples 2

Old style. 2

Sample code. 3

New style. 4

Sample code. 4

 

See also: Runtime Authentication         

 

Overview

 

Ebase provides support for customer-specific end-user authentication via the logon exit mechanism. A logon exit is a Java servlet or JSP that authenticates an end-user. The logon exit program can display pages to the end-user to perform the authentication if necessary, or can use any other authentication mechanism.

 

As an alternative to the logon exit mechanism, Ebase supports authentication performed by the application server. Application server authentication is configured using the web.xml deployment descriptor file and any User ID supplied by this mechanism is extracted automatically by Ebase without the need for further configuration. When application server authentication is used, the logon exit should normally be specifically disabled by unchecking server property Enable Authentication of New Users.

 

 

New and old style logon exits

 

The logon exit process was enhanced with Ebase V3.3 which introduced support for runtime security authorizations. 

 

Prior to V3.3, a logon exit was required to return a User ID to Ebase. This User ID was then available using FPL as $USER and the user was treated as authenticated.  For the remainder of this document, such a logon exit is referred to as old. Old style logon exits are still supported in V3.3 and subsequent releases. Starting from V3.3, when a User ID is returned by an old style logon exit, the Ebase system will then call the configured UserManager to complete the user – this entails adding any roles associated with the user; these roles are in turn associated with authorizations and can be used to perform runtime security checks. (See Ebase Security Authentication for more information)

 

Starting from V3.3, a logon exit can alternatively return a completed subject to Ebase. This subject represents both the User ID and any roles associated with the user; these roles are in turn associated with authorizations and can be used to perform runtime security checks. The logon exit can call the configured UserManager to perform this subject completion, or it can use any other mechanism it chooses.  The User ID is still made available to FPL as $USER as for old-style logon exits. This style of logon exit is referred to as new in this document.

 

The following table shows the attributes of new and old logon exits:

 

 

New

Old

Can be used with release

From V3.3

All

Returns

User ID

Subject

Supports runtime authorizations

Yes

Yes

Gets security roles from

Anywhere

UserManager

Supports customizable logon pages

Yes

Yes

 

In both cases, any security roles associated with the user are saved in a subject, and the subject is itself stored in the session. When a subsequent security authorization check is made, the system calls the configured AuthorizationManager to determine whether or not the user is authorized. The AuthorizationManager is another configurable component where an Ebase-supplied implementation is supplied, but this can be replaced if necessary.

 

Configuration

 

The logon exit will receive control from Ebase when a new session is encountered and server property Enable Authentication of New Users is checked. For example:

 

The logon exit class name is specified in the web.xml file for the Ebase application e.g.

 

<servlet>

      <servlet-name>LogonExit</servlet-name>

      <display-name>LogonExit</display-name>

      <servlet-class>MyLogonExit</servlet-class>

</servlet>

<servlet-mapping>

      <servlet-name>LogonExit</servlet-name>

      <url-pattern>/LogonExitServlet</url-pattern>

</servlet-mapping>

 

Code Examples

 

Old style

 

When a valid User ID has been determined, this must be communicated to Ebase by saving the id in the session state attribute UFSUSERas shown in the following code snippet:

 

import com.ebastech.ufs.kernel.Constants;

..............

HttpSession state = req.getSession(true);

state.setAttribute(Constants.SESSION_STATE_USER ID, User ID);

 

If a valid User ID cannot be determined, then value $?NO_USER?$ should be saved to indicate this as shown below:

 

state.setAttribute(Constants.SESSION_STATE_USER ID, Constants.NO_USER);

 

Please note that an old style logon exit servlet is required to save a value in attribute UFSUSER before returning to Ebase. Failure to do this can result in a loop between Ebase and the logon exit.

 

To return to Ebase, the URL should be forwarded to the ufsmain servlet. The URL must contain all parameters passed on the original request (this is not shown in the example below).

 

RequestDispatcher disp = getServletConfig().getServletContext().getRequestDispatcher('/' + Constants.SERVLET_UFSMAIN);

disp.forward(req, resp);

 

Sample code

 

import java.io.IOException;

import javax.servlet.*;

import javax.servlet.http.*;

import com.ebasetech.ufs.kernel.Constants;

 

public class SampleLogonServlet extends HttpServlet implements Servlet

{

 

      public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            process(req, resp);

      }

 

      public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            process(req, resp);

      }

 

      private void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            HttpSession state = req.getSession( true );

 

            // Determine the User ID here...

            String user = "Fred";

 

            // Set the User ID in the session state

            if (user == null) {

                  state.setAttribute(Constants.SESSION_STATE_USER ID, Constants.NO_USER);

            }

            else {

                  state.setAttribute(Constants.SESSION_STATE_USER ID, user);

            }

           

            // and return to Ebase

            RequestDispatcher disp = getServletConfig().getServletContext().getRequestDispatcher(

                                      '/' + Constants.SERVLET_UFSMAIN);

            disp.forward(req, resp);

      }

}

 

New style

 

It is recommended that new-style logon exits extend class com.ebasetech.ufs.security.authentication.LogonExitServletBase as shown in the example below. This base class provides many utility methods required by a logon exit.

 

When a user has been authenticated, the logon exit must create a javax.security.auth.Subject object and save this in the Ebase session, as per the code snippet below:

 

// address the Ebase session

initialiseEbaseSession(req);

 

// Complete the subject using configured UserManager

UserManager auth = SecurityManager.Instance().getUserManager();

Subject subject = new Subject();

try

{

      auth.completeSubject(subject, user);

}

catch (AuthenticationException aue)

{

      System.out.println("Authentication failed - " + aue.getMessage());

}

 

// Save the subject in the Ebase session

saveSubjectInEbaseSession(subject);

 

The example above illustrates using the configured Ebase User Manager.  Alternatively, this could be achieved using an external security system.  For example:

 

//….. call external system

 

//….. add UserPrincipal (representing User ID)

Subject subject = new Subject();

UserPrincipal userPrincipal = new UserPrincipal(“Fred”);

subject.getPrincipals().add(userPrincipal);

 

//….. add RolePrincipal’s (representing roles associated with the User ID)

Subject subject = new Subject();

RolePrincipal rolePrincipal = new RolePrincipal(superuser”);

subject.getPrincipals().add(rolePrincipal);

 

Both UserPrincipal and RolePrincipal are in package com.ebasetech.ufs.security.authentication. (See Ebase Security Authentication for more information)

 

Sample code

 

This example just shows the minimum code required. The source for the Ebase logon exit is supplied in folder Ufs/samples.

 

public class SampleLogonExit extends LogonExitServletBase implements Servlet

{

      public static final String USERNAME = "e_username";

      public static final String PASSWORD = "e_password";

      public static final String PARM_LOGON_PAGE = "LogonPage";

      public static final String PARM_INVALID_LOGON_PAGE = "InvalidLogonPage";

      public static final String DEFAULT_LOGON_PAGE = "samples/logon/logon.jsp";

      public static final String DEFAULT_INVALID_LOGON_PAGE = "samples/logon/logonInvalid.jsp";

      private String logonPage;

      private String logonInvalidPage;

           

      public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            process(req, resp);

      }

 

      public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            process(req, resp);

      }

 

      private void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

      {

            try

            {

                  // ensure no caching

                 setHTTPResponseHeader(resp);

                 

// address the Ebase session

initialiseEbaseSession(req);

 

                  // First pass through....

                  if (isInitialCall(req))

                  {

                        clearInitialCallFlag(req);

                       

                        // Check for illegal call to this servlet

                        if (!isRequestLegal(req, resp))

                        {

                              displayIllegalCallPage(req, resp);

                              return;

                        }

                       

                        // This is a legal call so..

                        else

                        {

                              // Save the calling parameters for later return to Ebase

                              saveCallingParameters(req);

           

                              // Re-save Ebase session in session state - required for clustering

                              saveEbaseSession(req);

                             

                              // Pass control to the logon page to start the authentication process.

                              linkToPage(req, resp, logonPage);

                              return;

                        }

                  }

     

                  // Subsequent pass, we have received control from the logon page, so check User ID/password

                  else

                  {

                        String user = req.getParameter(USERNAME);

                        String password = req.getParameter(PASSWORD);

                       

                        //Do authentication here...

                        boolean authenticationOK = true;

                       

                        // All OK

                        if (authenticationOK)

                        {

                              UserManager userManager = SecurityManager.Instance().getUserManager();

                              Subject subject = new Subject();

                              try

                              {

                                    userManager.completeSubject(subject, user);

                              }

                              catch (AuthenticationException aue)

                              {

                                    System.out.println("Authentication failed - " + aue.getMessage());

                                    return;

                              }

                              saveSubjectInEbaseSession(subject);

                       

                              /* Clean up and return to Ebase */

                              saveEbaseSession(req);

                              returnToEbase(req, resp);

                        }

     

                        // Failed

                        else

                        {

                              clearEbaseSession(req);

                              clearCallingParameters(req);

                              clearInitialCallFlag(req);

                              linkToPage(req, resp, logonInvalidPage);

                              return;

                        }

                  }

            }

            catch (Throwable e)

            {

                  Helper.logDesignerError("Unexpected error in logon exit " + e.getMessage());

                  e.printStackTrace();

            }

 

      }

 

      public void init( ServletConfig conf ) throws ServletException

      {

            super.init( conf );

            logonPage = conf.getInitParameter(PARM_LOGON_PAGE);

            if (logonPage == null ) logonPage = DEFAULT_LOGON_PAGE;

            logonInvalidPage = conf.getInitParameter(PARM_INVALID_LOGON_PAGE);

            if (logonInvalidPage == null ) logonInvalidPage = DEFAULT_INVALID_LOGON_PAGE;

      }

}