Novell Home

AppNote: iChain 2.2 - Developing a Custom OLAC Driver

Novell Cool Solutions: AppNote
By Shane Johns

Digg This - Slashdot This

Posted: 25 Nov 2003
 

Shane Johns
Novell Consultant Resolution Team
sjohnsTAKETHISOUT@novell.com

(This article assumes that you are already familiar with how to setup and administer basic iChain OLAC configurations using the LDAP data source).

Overview

O.L.A.C. stands for "Object-level Access Control", and is a Java application framework that runs on the iChain server that allows user-specific data to be injected into the HTTP requests from iChain to the back-end web server.

OLAC utilizes a plug-in architecture where user-specific data can be extracted from any type of database that supports Java API's. OLAC drivers can extract user information from databases and deliver that user content to the iChain appliance, so that iChain can send this user-specific data to the back-end web server. This is useful for when a back-end web server (protected by iChain) needs user-specific data to customize the responses sent back to the browser, or to perform authentication. The OLAC drivers reside on the iChain server, and are written in Java.

How OLAC Works

With iChain 2.2, there are three OLAC drivers ("plug-ins") that ship with iChain: LDAP, CONSTANT, and SECRETSTORE. The LDAP driver extracts user data from LDAP data stores, the CONSTANT driver allows you to inject constant values (non-changing values) into the OLAC user parameters, and the SECRETSTORE driver extracts user data from the high-encryption Novell SecretStore database.

The most commonly used driver is LDAP. This OLAC driver plugs-in to the iChain OLAC layer, and makes LDAP calls to eDirectory to extract user attribute values as needed. iChain then passes those eDirectory/LDAP user attribute values to the back-end web server.

For example:

  • iChain is fronting a PeopleSoft web server
  • The database that PeopleSoft is using requires a numeric "EmployeeID" value to locate unique users in its database
  • Each user object in eDirectory has an attribute called "EmployeeID" that contains the unique value for the user in the PeopleSoft database

–then iChain's OLAC LDAP driver can make an LDAP call from iChain to eDirectory, passing eDirectory the user's fully distinguished name and the attribute to retrieve ("EmployeeID"), and then inject the value of the "EmployeeID" attribute for this specific user into the HTTP request from iChain to the PeopleSoft web server. iChain sends OLAC parameters to the back-end web servers either in the HTTP QueryString, or in the HTTP header.

Legend for drawing:

  1. iChain server uses its OLAC LDAP driver to request the value of the "EmployeeID" attribute for the user: "cn=sjohns,o=novell"
  2. eDirectory/LDAP server responds to iChain with the value: "EmployeeID = 1234567"
  3. The EmployeeID data is forwarded from the iChain proxy to the web server

But what if the database you want to pull user data from is NOT an LDAP database? You'll have to write your own custom Java OLAC driver. We'll show you how.

"Cut To The Chase" Checklists: Develop and Deploy

To DEVELOP–

  1. Create a Java class that either extends the class "AbstractParamListProcessor", or implements the interface "ParamListProcessor"

    EXAMPLE:

    public class ParamListBuilder extends AbstractParamListProcessor {?}
    public class ParamListBuilder implements ParamListProcessor {?}

  2. To compile your custom OLAC driver, import classes from the following jar's that reside on the iChain proxy server:
    • SYS:\iChain\oac\oac.jar (com.novell.ichain.oac.*)
    • SYS:\iChain\oac\iCommon.jar (com.novell.utility.trace.*)

    EXAMPLE:

    import com.novell.ichain.oac.*;       // SYS:\iChain\oac\oac.jar
    import com.novell.utility.trace.*;     // SYS:\iChain\oac\iCommon.jar

  3. Implement the following three methods in your custom driver class: init(), deinit(), and processParamElements()
  4. In processParamElements(), call these ParamElement methods:
    • getDelimStr() -- Name of the OLAC "Data Source"
    • getAttrName() -- Name of the OLAC "Value"
    • setParamValue() -- Value returned by your plug-in to OLAC
    • setElementProcessed(true) -- Set to "true" when complete

To DEPLOY–

  1. Copy your jar or class file to the iChain proxy server, and add your jar/class to the CLASSPATH in SYS:\system\oacjava.ncf


  2. Create a new plug-in section in SYS:\iChain\oac\oac.properties and add your custom plug-in's fully qualified class name to it.


  3. FORMAT:

    [<Plug-in Name>]
    Class Name = <full classname>

    The value of "Plug-in Name" can be any name that helps identify the plug-in to the user. It must be enclosed in brackets. The value of "classname" must be the fully qualified name of the plug-in class.

    EXAMPLE:

    [TEXT Processor]
    Class Name = com.novell.ichain.oac.text.ParamListBuilder

  4. On your Protected Resource entry on your ISO object in ConsoleOne, add a new OLAC parameter entry that uses your new custom plug-in:
    • Name: Age (any name web server wants)
    • Data Source: TEXT (Plug-in Name of your custom driver)
    • Value: userAge

    You can now refresh OLAC, or restart the iChain proxy server, and make sure that OLAC initializes without any errors (see the OLAC screens on the iChain proxy). OACINT.NLM and OACJAVA both have console screens that print out OLAC-related messages at startup and during attribute processing.

Details of OLAC Framework

OLAC is primarily a Java application framework, but it's important to understand the two core pieces that make up OLAC:

  • OACINT.NLM: this is a small C NLM that is an interface/bridge between the iChain PROXY.NLM and the OLAC Java framework.


  • OACJAVA: the OLAC framework, written in Java. This is launched from the SYS:\system\oacjava.ncf file on the iChain server with the following command:
  • java -ns com.novell.ichain.oac.OACFactory

OLAC Control Flow

  1. URL request comes into iChain.
  2. iChain matches up the URL with a Protected Resource, as defined on the ISO object in ConsoleOne.
  3. iChain determines if that Protected Resource has any OLAC parameters configured (OLAC is configured on a "per Protected Resource" basis).
  4. iChain will then send the OLAC plug-ins the current user's LDAP fully distinguished name and all OLAC parameters for that Protected Resource. It is up to your custom OLAC plug-in to then parse through the OLAC parameter list to see if it should fire-off. Do this by checking the "Data Source" type with the getDelimStr() method. For example, the following data representing two OLAC parameters for the current Protected Resource could be sent to your OLAC driver:
  5. "LastName" "LDAP" "surname"     (Data Source = LDAP)
    "email" "TEXT" "mail"     (Data Source = TEXT)

  6. OLAC user values are retrieved and sent from iChain to web server.

Text for image: You can use iChain OLAC to retrieve the book category that is specific to this user. In this case for the user "cn=bob,o=novell", his book category is "Science_Fiction".

The user enters the URL "http://bookstore.com/favebooks" into their browser and this user's favorite book category will automatically be sent to the back-end web server through iChain's OLAC framework, as the following URL:
"http://bookstore.com/favebooks?CATEGORY=Science_Fiction"

OLAC Caching

OLAC supports two levels of caching for performance:

  1. First level occurs in "oacjava" (the OLAC Server), which caches the Protected Resource name and its associated OLAC parameters, data source, and value, initially when the OLAC server is started and/or when an OACREFRESH command is issued.


  2. Second level of caching is the OLAC user data values that are cached at the iChain proxy for that user session. The life of this cache is only for the duration of that authenticated user session.

OAC.Properties Driver Configuration File

The iChain OLAC plug-ins are driven off of a properties file that contains the information for the plug-ins that are currently configured in the system. The location of this file on the iChain box is:

SYS:\iChain\oac\oac.properties

When the java OACFactory class is loaded, it reads this properties file and sets up the plug-ins.

The format of the oac.properties file is loosely divided into two sections: (1) the [OAC] section at the top, and (2) all other driver sections combined:

= = = = BEGIN FORMAT = = = = = = = = = = = = = = = = = = = =

[OAC]: top-level section for the OLAC object factory itself

["Plug-in name>]: section for each plug-in.

NOTE: Each section contains a square bracket section name and a set of NAME=VALUE settings below the section name. Each section can have multiple NAME=VALUE parameters, and each NAME=VALUE setting must be on its own line. This is where you could list configuration values that your custom driver needs during it's init() procedure.

= = = = END FORMAT = = = = = = = = = = = = = = = = = = = = =

= = = = BEGIN EXAMPLE = = = = = = = = = = = = = = = = = = = =

[OAC]
Worker Count = 32
Refresh Time = 180

[LDAP Processor]
Class Name = com.novell.ichain.oac.ldap.ParamListBuilder

[CONSTANT Processor]
Class Name = com.novell.ichain.oac.constant.ParamListBuilder

[TEXT Processor]
Class Name = com.novell.ichain.oac.text.ParamListBuilder
AdminName = sjohns

= = = = END EXAMPLE = = = = = = = = = = = = = = = = = = = =

Interface com.novell.ichain.oac.ParamListProcessor

For your custom OLAC driver you will create a Java class that implements the com.novell.ichain.oac.ParamListProcessor interface.

The ParamListProcessor interface has three methods you must implement:
  • init()
  • deinit()
  • processParamElements()

None of the above methods need to be thread-safe. Although OACJAVA uses multiple threads to respond to requests, the object factory creates a new instance of each plug-in for each thread.

init()

  • init() is called whenever the OLAC object factory loads your plug-in. The init() method takes one parameter: properties, which contains all properties you defined in the SYS:/ichain/oac/oac.properties file on the iChain proxy server.
  • If your plug-in needs configuration data to initialize itself, you can define any parameters you want within the oac.properties file.
// ********************************************************
//         init()
// ********************************************************
// * @param properties: params defined for plug-in in the OLAC prop file.
// * return 0 if the operation is successful, non-zero otherwise.
public int init(Properties properties)
{
    tracer.out(Tracer.BRIEF, "(->) init");
	// ---- YOUR CODE HERE ----
    tracer.out(Tracer.BRIEF, "(<-) init");
    return 0;
}  // END of init()

deinit()

  • deinit() is called when the plug-in is unloaded.
  • Used for cleanup tasks and is called once upon shutdown of OACJAVA.
  • Shutdown tasks may include closing files or releasing connections or other system resources used by the plug-in.
// ********************************************************
//         deinit()
// ********************************************************
// * return 0 if the operation is successful, non-zero otherwise.
public int deinit()
{
    tracer.out(Tracer.DEBUG10, "(->) deinit");
	// ---- YOUR CODE HERE ----
    tracer.out(Tracer.DEBUG10, "(<-) deinit");
    return 0;
}  // END of deinit()

processParamElements()

  • processParamElements() is where most of the work of your OLAC driver takes place.


  • processParamElements(): performs the actual work of processing the OLAC attribute list. It receives the user's distinguished name in LDAP format (e.g. "CN=bob,O=novell") and the list of OLAC attributes corresponding to the protected resource requested by the user. The list of OLAC attributes is represented by a Vector of ParamElement objects, each of which represents a single OLAC attribute. For example:


  • "Name" "LDAP" "cn" "TelNumber" "ODBC" "telephoneNo"

  • The above string represents two OLAC parameters configured for the current Protected Resource. This string is passed to all OLAC plug-ins. The LDAP plug-in retrieves the CN attribute value of the user (from the LDAP database). Then the ODBC plug-in reads the telephone number (from the ODBC database).


  • The plug-in then processes the data and fills in the parameter values. Once the control comes back to the OLAC framework, it builds a single string and returns it to the proxy server. For example: "Name=SJohns&TelNumber=801-555-1212"

For each ParamElement object (OLAC parameter) in the list, processParamElements() must do the following:

  1. getDelimStr(): Check if the OLAC attribute is to be processed by this plug-in. Generally, this is done by calling the ParamElement.getDelimStr() method on the ParamElement to get its Data Source (middle of the 3 values for each OLAC parameter), and comparing it with the name of the plug-in as defined by the developer. From the example above, the ODBC driver would look for the string "ODBC" from the getDelimStr() call. If the value returned from getDelimStr() was not "ODBC", then this driver would not trigger.


  2. getAttrName(): Get the Value (third of the 3 values for each OLAC parameter) from the OLAC attribute by using the ParamElement.getAttrName() method. Use this Value, along with the user's distinguished name, to retrieve the user's data from your specific database. In the example above, getAttrName() would return "cn" for the LDAP driver, and it would return "telephoneNo" for the ODBC driver.


  3. setParamValue(): Add the user data value your driver retrieved to the ParamElement by calling ParamElement.setParamValue(). This method may be called multiple times (for multi-valued attributes) on the same ParamElement if there are multiple results, which results in a comma-delimited list as the URL query string parameter value.


  4. setElementProcessed(): Set the flag that indicates that the ParamElement has been processed by calling ParamElement.setElementProcessed() with a value of "true", to signal that your plug-in has completed its processing.
// ********************************************************
//         processParamElements()
// ********************************************************
// * @param userDN: DN of user, passed-in by OLAC framework.
// * @param elements: Specifies the OLAC parameter list.
// * return 0 if the operation is successful, non-zero otherwise.
  public int processParamElements(String userDN, Vector elements)
  {
      tracer.out(Tracer.BRIEF, "(->) processParamElements");

      if (elements == null)
          return 0;

      int size = elements.size();
      String delimStr = "";

      try {  // for each OLAC parameter for this Protected Resource

          for (int i = 0; i < size; i++) {
              ParamElement element = (ParamElement)(elements.elementAt(i));

/* 1 */    delimStr = element.getDelimStr();
              tracer.out(Tracer.BRIEF, "     delimStr is: '" + delimStr + "'");

              if (delimStr.equalsIgnoreCase("TEXT")) {  // name of my plug-in

/* 2 */        String name = element.getAttrName();
                  tracer.out(Tracer.BRIEF, "     attrName: '" + name + "'");

                  // ===== THIS DOES THE WORK =============
                  String result = getTextRecord(userDN);  // get value from DB
                  tracer.out(Tracer.BRIEF, "     RESULT is: " + result);

                  if (result != null)
/* 3 */            element.setParamValue(result);
                  else
                      element.setParamValue("");

/* 4 */        element.setElementProcessed(true);  // DONE processing
              }  // end of "DelimStr" check

          } // done checking each OLAC parameter
      }
      catch(Exception e)
      {
          tracer.out(Tracer.BRIEF, "processParamElements() failed" + e, e);
          return -1;
      }

      tracer.out(Tracer.BRIEF, "(<-) processParamElements");
      return 0;
  }  // END of processParamElements()

Screen Shots

This is the "database" that my custom driver is accessing (a simple flat-file text database). It contains records of users and their ages. Each user record is on its own line, and the format is: <record_no> | <username> | <user_age>

----------
These are the two OLAC parameters that are configured for my Protected Resource: one OLAC parameter is the standard LDAP driver, and the other OLAC parameter is my custom TEXT driver.

----------
This is an image of the debug messages on the OLAC screen on the iChain server when the user logs in and the custom TEXT OLAC driver runs.

----------
This is how the user-specific data finally appears on the back-end web server. The "MyEmployeeID" value came from eDirectory/LDAP, and the "UserAge" value came from my TEXT (flat-file) database.

Misc. Notes

  • In general, plug-ins do not need to access the "Name" of the OLAC attribute (first of the 3 values for each OLAC parameter), obtained by calling ParamElement.getParamName(). The "Name" is used only by the OLAC framework for assembling the URL query string in name=value format to send the web server. The plug-ins only supply a result for the "value".


  • Multiple plug-ins can be active at any given time.


  • OLAC will replace any preexisting query string values that have the same parameter names that it will write values to.


  • OLAC is configured on a "per Protected Resource" basis. Meaning that if you have 10 Protected Resource entries on the ISO object in ConsoleOne and you want the same OLAC parameters for every Protected Resource (not likely), you'll have to configure the same OLAC parameters for each Protected Resource individually.


  • You can either have your custom Java class extend the "AbstractParamListProcessor" class, or implement the "ParamListProcessor" interface. But the "AbstractParamListProcessor" class itself simply implements the "ParamListProcessor" interface:


  • public abstract class AbstractParamListProcessor implements ParamListProcessor

  • If a plug-in needs the user's password to be passed to it by the OLAC framework, it will need to implement the ParamListProcessorII interface (instead of ParamListProcessor) which has a processParamElements() method with an additional parameter of user password.

Troubleshooting and Debugging Message Screens

  • The debug flag in OACINT.NLM can be dynamically set to two debug levels e.g. OACINT /d2
  • There are 10 debug levels in Oacjava which, if used, need to be specified in SYS:\iChain\oac\TracerFilter.properties
  • You can enable or disable logging to a file or the screen by editing the properties in SYS:\iChain\oac\TracerMedia.properties

Source Code and PowerPoint Presentation

The full source code for the example driver, and the PowerPoint slides describing Custom OLAC Development with iChain 2.2, is available here. The sample custom OLAC driver is very simple and uses a plain-text file as its "database", so it should be easy to follow.


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell