Developing a custom Java module

Tivoli Federated Identity Manager 6.2

In this tutorial, we will walk through the complete development process for creating a custom trust service (aka Security Token Service or STS) plug-in for Tivoli® Federated Identity Manager (TFIM) 6.2. Customers might develop their own plug-ins for a variety of reasons including advanced user mapping and attribute gathering capabilities, or to support validation and issuing proprietary security token types. This tutorial will use as a working example a simple mapping module which adds a configurable name/value parameter pair as an attribute to the TFIM Trust Service's STSUniversalUser. For those readers familiar with developing STS modules for previous versions of TFIM (see Developing Custom STS Modules), the development interfaces are largely unchanged; however, the packaging and deployment is different as TFIM 6.2 has now moved to an Open Services Gateway Initiative (OSGi) plug-in framework for extensions. This OSGi plug-in framework is used for developing a variety of supported extension points in TFIM, including the STSModule extension point which is the focus of this tutorial.

Share:

Shane B. Weeden, Senior Software Engineer, IBM Tivoli

Shane WeedenShane Weeden is a senior software engineer with the IBM Tivoli Federated Identity Manager development team. He has worked in IT security since 1992, and since 2000 has been working with Tivoli Security products including Tivoli Access Manager for eBusiness and Tivoli Federated Identity Manager. Shane now divides his time between customer focused engagements and core product development activities. He holds a Bachelor of Information Technology from the University of Queensland in Australia.


developerWorks Professional author
        level

Ann-Louise Blair (alblair@au1.ibm.com), Software Engineer, IBM Tivoli

Ann-Louise Blair is a Software Engineer in the IBM Australia Development Laboratory. She has four years experience working in the IT industry and holds a Bachelor of Software Engineering degree from the University of Queensland. Having worked in both testing and development roles in the Gold Coast Integration Factory team, Ann-Louise has gained expertise working with many Tivoli software products.



Simon Chen (simon.chen@us.ibm.com), Staff Software Engineer, IBM Tivoli

Simon ChenJiayue (Simon) Chen was going to devote his career to building robots until he realized that sniffing fumes from a soldering iron could cause more harm than good. So instead, he's now working as a staff software engineer with the Tivoli Federated Identity Manager development team, where the radiation from the computers in his office keeps him warm. Simon graduated from Georgia Tech with an B.S. in Electrical Engineering and has been with IBM full-time since 2006. His technical interests include Eclipse and OSGi.



12 September 2008

Before you start

This tutorial describes the process of developing custom Java™ plug-ins for TFIM 6.2. In particular this tutorial guides you through development of a custom security token service (STS) module. It is designed for advanced TFIM users with strong Java development skills who want to learn how to harness the power of the OSGi extension points introduced in TFIM 6.2.

About this tutorial

TFIM 6.2 introduces a new way to allow customers to extend the capabilities of the product. Utilizing the same OSGi runtime model as the Eclipse platform, TFIM exposes several "extension points" which allows users to develop their own custom code to run inside TFIM. One of these extension points (STSModule) allows for the development of custom plug-ins for trust service (aka Security Token Service or STS) modules. This tutorial will take you through the complete design / development / deployment cycle for a basic custom trust service mapping module. By following the steps in this tutorial you will learn how to create a custom trust service plug-in for Tivoli Federated Identity Manager (TFIM) 6.2. The example module described here is also available for download with this tutorial.

Objectives

In this tutorial you will learn about how to develop and deploy a custom plug-in for TFIM 6.2 which implements the com.tivoli.am.fim.trustserver.sts.STSModule extension point that is available in TFIM 6.2. You will learn how to design a custom plug-in for your needs and create the module using an appropriate development environment. Rational Application Developer 7.0 is used throughout this tutorial, however Eclipse 3.2 or later is also a viable development platform for creating these OSGi-based plug-ins. The tutorial also explains how to deploy and test the plug-in jar file that you have developed into the TFIM 6.2 runtime environment.

Prerequisites

This tutorial is written for people who have an advanced level of understanding of Identity Management concepts. You should have prior experience with Tivoli Federated Identity Manager and a good understanding of associated concepts including the TFIM Security Token Service (STS) and trust service modules. A strong knowledge of the Java programming language is also expected.

System requirements

To work through the examples in this tutorial, you will need an appropriate development environment for creating the module: Rational® Application Developer 7 or Eclipse 3.2 (or later). You will also need a TFIM 6.2 runtime environment for deployment and testing of your module (and to gather jars to setup the development environment).


Develop, deploy and test a custom module

This tutorial describes the step-by-step process of developing a new custom Java™ plug-in for a TFIM 6.2 environment. It includes the process of deploying your code, creating an instance of the new module and including it in trust chains through the TFIM console. The testing and debugging process is also detailed towards the end of this tutorial.

Introduction

The TFIM 6.2 Runtime Environment uses Open Services Gateway Initiative (OSGi) and Eclipse extensions. FIM plug-ins are now packaged as OSGi bundles (in the form of jar files) that implement an extension. These plug-ins can be installed, started, stopped or updated without requiring a reboot of the JVM. In the TFIM environment, these plug-ins are installed into the <TFIM_Home>/plugins directory and deployed to the <WebSphere_profile_config_root>/itfim/plugins directory by using the TFIM console.

com.tivoli.am.fim.trustserver.sts.STSModule is the extension point interface class that STS modules must implement. In TFIM, the terms 'module' and 'plug-in' are synonymous and will be used interchangeably throughout this tutorial.

The main goal of this tutorial is to teach you about developing plug-ins for TFIM 6.2, and the process for achieving this will be by example - developing a basic custom mapping module for TFIM 6.2. The module that we create will allow the STSUniversalUser (STSUU) to be manipulated. Operating in 'map' mode, the plug-in that is created will allow the user to configure an extra attribute and value to be included in the output STSUU. The code for doing this is quite basic - the important point is to understand the process required to develop and deploy the module.

A downloadable jar file has been provided with this tutorial as an example of the expected output. That jar file can be imported directly into a development environment as an Eclipse project and used as a starting point for developing other STSModules of your own.

The tutorial also explains how to deploy and test the custom module in a TFIM 6.2 environment. For simplicity, the mapping module is included in a basic trust chain consisting of a Default STSUU Instance to 'validate' a STSUU, followed by the custom mapping module to add an extra attribute and finally another Default STSUU Instance to 'issue' the token. The creation of this trust chain is described in more detail in the 'Deployment' section. It is important to note that the custom module created in this tutorial could be used in any chain. The chain described in this tutorial was chosen for its simplicity to demonstrate the behavior of the custom module. We will drive the testing of our custom chain using a simple command-line utility to send requests to the SOAP interface of the Trust Service.

Key Concepts

TFIM Security Token Service

TFIM implements the Security Token Service (STS) defined by WS-Trust. The STS receives a RequestSecurityToken (RST) message and returns a RequestSecurityTokenResponse (RSTR) message. This service is accessible by WS-Trust clients (typically via SOAP) and provides a means by which identity tokens can be validated and/or exchanged for different token types or values. The actual task that is performed by the STS is dependent upon the parameters in the request to the STS (RequestSecurityToken or RST) and which trust chain mapping configured in the TFIM STS matches that request. A trust chain mapping maps an incoming request to a trust chain. A trust chain is a configured list of modules that perform specific tasks. These tasks include:

  • Validation of incoming tokens
  • Mapping of one identity token to a different token type
  • Mapping of various values in an identity token
  • Authorization
  • Auditing

An example of an STS task is to take an incoming SAML 1.0 token and exchange it for a LTPA token. The configured trust chain to achieve this might be 3 modules:

  1. Validate the incoming SAML 1.0 token (done with the SAML 1.0 token module in validate mode)
  2. Map the parameters from the SAML 1.0 token to a format which can be issued as an LTPA token (done with a mapping module in map mode - e.g. XSLT mapping module)
  3. Issue the new LTPA token (done with the LTPA token module in issue mode)

The TFIM STS has modules that support a number of token types out of the box but it is also extensible. If a token type is required that is not currently available, a module can be written to support that token type and added to the current modules.

As described above, the STS configuration consists of two main building blocks:

  • A set of trust module chains. A chain is a list of trust module instances which form a workflow to process a request.
  • A set of trust chain mappings. Each mapping maps a set of chain selection criteria from the incoming request to a particular trust module chain. Multiple mappings may point to the same chain; however, each mapping may have unique configuration parameters for the chain modules.




Each WS-Trust request contains one or more well-known parameter values (i.e. selection criteria) which must match to the configuration of one of the chain mappings in the TFIM STS (otherwise the request cannot be processed). Details of these parameters are described in Table 1.

Table 1. WS-Trust Service Request Parameters
ParameterDescription
RequestTypeAn identifier which describes a family of requests, using one of several specification-defined values - e.g. http://schemas.xmlsoap.org/ws/2005/02/trust/Validate
AppliesToA representation of which service the WS-Trust request relates to, or for what scope the requested security token is required. Typically in URL format and may be specified as a regular expression. e.g. http://finance.itso.ibm.com/CreditService
IssuerThe entity or type of service component that is issuing the WS-Trust request. Typically in urn format. e.g. urn:itfim:wesb
TokenTypeOptional URI describing the type of token requested in the response to the WS-Trust request. e.g. http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0

A trust module chain consists of a sequence of module instances. The first module in every chain is responsible for parsing and validating the token in its native format, and converting it into an internal XML representation shared by all other modules in the chain. This XML representation is called the STSUniversalUser (STSUU). Data from the WS-Trust request is also stored in the STS Universal User (STSUU). The STSUU document is then passed between modules in the trust module chain. Intermediate modules transform the data, and the final module in the chain is responsible for transforming data from the STSUU into the resulting desired token type (must be XML). This token is returned as part of the WS-Trust response.

Figure 1. WS-Trust Token Request
Basic Flow

Module instances can be configured in several different modes to suit the business requirements. These modes include validate, map, issue, authenticate, authorize and other. Modules in validate mode are responsible for validating the authenticity of identity tokens. Those in map mode transform the data that passes through the trust module chain e.g. using XSL transforms. Meanwhile, modules that are in issue mode will generate identity tokens from the data that passes through the trust module chain. 'Other' is a general processing mode e.g. to perform authorization. A minimally configured trust module chain will likely have modules configured in validate - map - issue modes. An authorization module may optionally be inserted before or after the map module. Figure 1 shows a trust module chain with validate - authorize - map - issue modules. Multiple map modules may also be required in cases where data for the identity-mapping process needs to be retrieved from multiple data sources.

The TFIM STS supports a variety of identity token types including:

  • Username
  • SAML assertion (versions 1.0, 1.1 and 2.0)
  • RACF PassTickets
  • LTPA
  • Kerberos
  • X.509 certificate
  • STSUniversalUser

When required, additional token modules can be coded using Java. Modules that represent an identity token type are typically configured in validate or issue mode.

GUIXML

GUIXML describes the configuration data for a STS module. The TFIM console uses GUIXML to present the configuration panels including labels, text entry fields, check-boxes, drop-downs etc. This allows the developer of an STSModule to self-describe the configuration parameters required for that module, and the TFIM console will prompt for those configuration parameters when the module is used in a trust chain! Not only will TFIM prompt for your configuration parameters, it will also store them in the WebSphere configuration repository and automatically propagate the configuration to all nodes in a clustered environment such that your module does not need to worry about storage of any standard configuration parameters.

There is a reference section at the end of this tutorial which describes the set of available GUIXML widgets.

In previous versions of TFIM, GUIXML always required a separate java.util.ListResourceBundle for the <PageTitle> strings "titleKey" and "titleDescriptionKey". In TFIM 6.2, this may still be used, however if the module is to be only used in one language the Title and Description can be provided as simple text strings using the "titleText" and "titleDescription" <PageTitle> strings.

The "titleText" and "titleDescription" will be used either instead of or as a default when the ResourceBundle strings cannot be retrieved. The Sample GUIXML structure is shown below in Listing 1.

If internationalization is required, the packaging is easier in TFIM 6.2. Rather than having a separate jar file for the java.util.ListResourceBundle class (as was required in previous versions which had to be copied to the console's installation directory), TFIM 6.2 allows the resource bundles class to be packaged in the same jar file as the STS plug-in.

Listing 1. GUIXML Sample
<PageInfo helpFile="com.tivoli.am....html">
<PageTitle
resourceBundleClass="...some Resource Bundle Class"
titleDescriptionKey="KEY to Resource Bundle Description String"
titleKey="Key to Resource Bundle Title String"
titleText="This is the text string for the title of this page"
titleDescription="This is the text string for the description of this page"/> </PageInfo>

Of even greater convenience with the OSGi plug-in framework is that the GUIXML does not have to be written by hand - the Eclipse development environment will let you build the GUIXML using the plug-in development editor.


Designing the Module

Custom Module Design

The custom module we will build in this tutorial will be designed to manipulate the STSUU object. The module will have a self-described configuration (specified using GUIXML) to gather the name and value of a new attribute that is to be added to the STSUU. As described previously in the GUIXML key concepts section, GUIXML allows a token module to describe its own configuration widgets and parameter names. When using an instance of this mapping module in a TFIM trust chain, the user will be expected to provide the desired attribute name and value pair.

When an instance of this custom module is used in a trust chain the desired name and value of the attribute to be added to the STSUU object needs to be configured. You will be prompted to perform this configuration during the creation of the chain (as shown in Figure 2 below).

Figure 2. Configure the module (as specified by the GUIXML)
stsuuser

As in the above example configuration, an instance of the module in a deployed trust chain could be configured to add a new attribute with name 'testName' and value 'testValue' to the STSUU. NOTE: This is an example attribute only, the attribute name and value can be set to any values whenever an instance of the custom module is configured in a chain.

Figure 3 illustrates the manipulation of the STSUU object when the mapping module (with the example configuration) is applied.

Figure 3. Example attribute added to the STSUU by an instance of the custom module
stsuuser

Developing the module

Custom Module Development

This section of the tutorial describes the development process used to create the custom module. It includes steps to prepare the development environment, writing the code and packaging the module in to a jar file that is ready to be deployed into TFIM.

Setting up the development environment

Before you can begin developing a custom module you need to configure your development environment appropriately. As described previously, either Eclipse 3.2 (or later) or Rational Application Developer 7 (RAD 7) can be used when developing new plug-ins for TFIM 6.2. In this tutorial a RAD 7 environment is used.

As a preliminary step you will need to copy the <TFIM_install_root>/plugins dir from the TFIM runtime environment to a local directory in your development environment. That is, the jar files must be made available locally to the IDE. For our example, we have copied the plug-in directory to c:\plugins.

Now you can start up RAD. You will be prompted to select a workspace as shown in Figure 4. Enter your chosen path and select OK.

Figure 4.Selecting a Workspace
launcher

Close the Welcome screen that appears. Next we need to configure the IDE Target Platform (for plug-in development) to compile against the plug-in jar files that we made available in a local directory in the previous step. Select Windows ->Preferences. A Preferences window will appear. Select Plug-in Development -> Target Platform from the left hand menu as illustrated in Figure 5. Click on Browse... to set the Location value to the folder containing the plug-ins. Select OK.

Figure 5. Setting the Target Platform
target

The environment is now ready for you to begin developing custom modules.

Developing the custom module

To create a new plug-in project that extends the available STS module extension point select File ->New -> Project. The 'New Project' Wizard will start (refer to Figure 6 below). Select 'Plug-in Project' and click Next to continue working through the Wizard.

Figure 6. Project Wizard
pluginWiz1

Enter a 'Project name' of com.tivoli.am.fim.demo.map and ensure the 'Project Settings' and the selected 'Target Platform' are set correctly as depicted in Figure 7. You can then click Next to proceed.

Figure 7. Setting Properties for a new Plug-in Project
pluginWiz2

The Plug-in Content page is displayed with the configured 'Plug-in Properties' as shown in Figure 8. You should uncheck both of the 'Plug-in Options' before clicking Next.

Figure 8. Plug-in Content Wizard
pluginWiz3

The next screen prompts you to decide whether to use one of the available templates. We will be developing the custom module without the use of a template. Uncheck the option of using a template as illustrated in Figure 9 and click Finish to complete the Wizard.

Figure 9. Templates Wizard
pluginWiz4

RAD will prompt you to open the Plug-in Development perspective (as shown in Figure 10). Click 'Yes'.

Figure 10. Switching Perspective
pluginWiz5

The new package for the project is opened in the IDE. The Overview tab will be displayed initially in the centre panel (refer to Figure 11)

Figure 11. The Plug-in Manifest Editor
pluginWiz7

Select the Dependencies tab and then click "Add" (Required Plug-ins). The Plug-in Selection box displayed in Figure 12 opens and allows you to browse through the available plug-ins. You need to specify which packages your new module will depend upon. Browse to find and select: com.tivoli.am.fim.common (6.2.0.0) and com.tivoli.am.fim.sts (6.2.0.0). Click OK.
NOTE: These two plug-ins contain the key classes that are required as a minimum when developing a new plug-in that extends the STS module extension point.

Figure 12. Selecting Dependencies
pluginDepSelect8

Figure 13 shows the updated Dependencies panel that will now have the two selected plug-ins displayed in the list of Required Plug-ins

Figure 13. The Plug-in Manifest Editor
pluginDepReq9

Switch to the Extensions tab and click on "Add" to create a new extension point. The New Extension window appears (refer to Figure 14). Select the com.tivoli.am.fim.sts.module (indicating that we are creating an extension that will extend the STS module extension point i.e. implement the STSModule interface.) Click Finish.

Figure 14. Selecting an Extension Point
extendSTSMod10

The added Extension Point is displayed in the 'Extensions' panel. You can now set the properties of the new extension. Enter a value for ID and Name for the extension as shown in Figure 15 below.

Figure 15. Setting Extension Details
extDetails11

We can now use the plug-in editor's environment to specify our module configuration, including the GUIXML which will self-describe the configuration for our module. Create a New Module as illustrated in Figure 16. That is, right-click on the com.tivoli.am.fim.sts.module extension and select New-> Module.

Figure 16. Creating a Module
newModule12

As shown in Figure 17, set these properties:

  • Set exposedClass to com.tivoli.am.fim.demo.map.DemoMap
  • Set version to 1.0.0
  • Set supportedModes to map
  • Set legacyModule to false



The com.tivoli.am.fim.demo.map.DemoMap class will be the main class containing the functionality of the new plug-in and as such will implement the STSModule interface. This code for this class is shown in Listing 2 later in this tutorial.

Figure 17. Setting Module Properties
modProps13

Before we can begin coding the implementation of the custom module, we need to create a new Java™ package for the project. Right-click on the src folder displayed in the Package Explorer on the left hand side of the Plug-in Perspective. Select New-> Package (refer to Figure 18).

Figure 18. Creating a new Java Package
newPack14

The New Java Package wizard will start. Enter the package details as shown in Figure 19 and click Finish.

Figure 19. New Java Package Properties
pack15

Right-click on the newly created Java package in the Package Explorer and select New -> Class. The New Java Class wizard which is shown in Figure 20 appears. Enter the class details as shown in Figure 20.

Figure 20. New Java Class Details
newClass16

Click on the 'Add' button to add an interface. The Implemented Interfaces Selection box appears (refer to Figure 21) for you to select the interface(s) that will be implemented by this new class. Since DemoMap will be the main class for the plug-in you should select the STSModule interface and click OK. Click Finish to complete the New Java Class wizard.

Figure 21. Selecting Implemented Interfaces
interfaces17

As described earlier in the GUI XML section, in TFIM 6.2 you can include the java.util.ListResourceBundle class describing the GUIXML messages inside the package of the custom java module. For the plug-in that we are developing in this tutorial, we will call this ListResourceBundle class DemoMapMessages.

Repeat the process described to create another new Java class called DemoMapMessages in the com.tivoli.am.fim.demo package. Be sure to set the superclass on the New Java Class screen to java.util.ListResourceBundle and elect to 'Inherit the abstract methods'.

Listing 2. DemoMap class
package com.tivoli.am.fim.demo.map;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.tivoli.am.fim.trustserver.sts.STSGroupMembership;
import com.tivoli.am.fim.trustserver.sts.STSMode;
import com.tivoli.am.fim.trustserver.sts.STSModule;
import com.tivoli.am.fim.trustserver.sts.STSModuleException;
import com.tivoli.am.fim.trustserver.sts.STSRequest;
import com.tivoli.am.fim.trustserver.sts.STSResponse;
import com.tivoli.am.fim.trustserver.sts.STSUniversalUser;
import com.tivoli.am.fim.trustserver.sts.uuser.Attribute;

public class DemoMap implements STSModule {

  final static String CLASS = DemoMap.class.getName();

  final static String CONFIG_ATTRIBUTE_NAME = "mymap.attribute.name";

  final static String CONFIG_ATTRIBUTE_VALUE = "mymap.attribute.value";

  final static String ATTR_TYPE = "";

  Logger _log = Logger.getLogger(CLASS);

  public void destroy() throws STSModuleException {
    // nothing to do here
  }

  public void init(Map arg0) throws STSModuleException {
    // nothing to do here
  }

  public boolean invoke(STSMode mode, STSRequest request, STSResponse response)
    throws STSModuleException {
    String methodName = "invoke";
    _log.entering(CLASS, methodName);

    try {
      if (STSMode.MAP == mode) {
        doMap(request, response);
      }
    } finally {
      _log.exiting(CLASS, methodName);
    }
    return true;
  }

  void doMap(STSRequest request, STSResponse response)
    throws STSModuleException {
    String methodName = "doMap";
    _log.entering(CLASS, methodName);

    try {
      //get configuration
      STSGroupMembership config = request.getRequestGroupMembership();
      String attributeName = config.getSelfProperty(CONFIG_ATTRIBUTE_NAME);
      String attributeValue = config.getSelfProperty(CONFIG_ATTRIBUTE_VALUE);

      // access the STSUU that we intend to update
      STSUniversalUser uuser = response.getSTSUniversalUser();

      // trace
      _log.logp(Level.FINEST, CLASS, methodName, "Starting user: "
        + uuser.toString());
      _log.logp(Level.FINEST, CLASS, methodName, "Adding attribute ("
        + attributeName + "," + ATTR_TYPE + "," + "{"
        + attributeValue + "}");
        
      // create a new attribute and add it
      Attribute a = new Attribute(attributeName, ATTR_TYPE,
        new String[] { attributeValue });
      uuser.addAttribute(a);
      
      // more trace
      _log.logp(Level.FINEST, CLASS, methodName, "Final user: "
        + uuser.toString());
    } finally {
      _log.exiting(CLASS, methodName);
    }
  }
}

It is worthwhile at this time to take a look at some of the details of the implementation code. In particular there are three methods which are part of the com.tivoli.am.fim.trustserver.sts.STSModule interface:

  • init(Map params) - This method is called once for the module. If you have a configuration page for "init" configuration parameters, the values of these parameters are passed in the Map. The Map is String->String[], as each parameter can support an array of values. In our example we will not have any init parameters, so there is nothing to do in the init method.
  • invoke(STSMode mode, STSRequest request, STSResponse response) - This method is the main body of work for the module, and is called whenever a trust chain is executed that uses our module. The mode represents the calling mode for the module and will be one of the modes supported by the module as we defined earlier (see the Supported Modes parameter in Figure 17). The request object will give you access to a variety of attributes about the incoming request, including access to configuration parameters as demonstrated in the doMap() method. The response object is used for setting response attributes, in particular for this demonstration it allows access to the STSUU.
  • destroy() - This method is called once for the module when its lifecycle is ending.

There is Javadoc located in the <TFIM_install_root>/docs directory which covers all the major classes and interfaces used for developing a custom trust service plug-in.

The DemoMapMessages class stub will include the getContents() method inherited from the ListResourceBundle superclass.

Listing 3. DemoMapMessages Class
package com.tivoli.am.fim.demo.map;

import java.util.ListResourceBundle;

public class DemoMapMessages extends ListResourceBundle {

  protected Object[][] getContents() {
    return contents;
  }
  
  static final Object[][] contents = {
    {"DEMO_TITLE", "My Map Configuration"},
    {"DEMO_DESCRIPTION", "Please enter the configuration parameters for My Map"},
    {"TITLE_ATTRIBUTE_NAME", "The name of the attribute to add to the STSUniversalUser"},
    {"TITLE_ATTRIBUTE_VALUE", "The value of the attribute to add to the STSUniversalUser"}
  };
}

As can be seen in Listing 3, we add code to return a multidimensional array that essentially contains a list of 'name/value' pairs that can be accessed by the GUIXML for the module's self-described configuration. The rest of this section details how to create the module's GUIXML configuration in RAD.

You can describe the desired GUI configuration pages for the custom module in the Extensions tab. For the plug-in that we are creating, we only require a single Page element with an appropriate title and a PageLayout container that contains two text fields to enter the details of the attribute that is to be added to the STSUU.

Figure 22 illustrates a sketch of the proposed design for our custom module's GUI for its configuration:

Figure 22. Sample GUI Design for Demo Map Module
PageDesign

The first step is to create the Page element. Click on the extension that you added earlier to highlight it. Right-click and select New -> Page. You can then right-click on the new Page that appears in the All Extensions panel and elect to create a new PageInfo element as shown in Figure 23.

Figure 23. Creating PageInfo
New Page

You should now add a PageTitle to the PageInfo element that you just created. The ResourceBundle class that we want the module to use is the DemoMapMessages class that we created to describe the messages for our new plug-in. That is, enter the value com.tivoli.am.fim.demo.map.DemoMapMessages for the resourceBundleClass. Meanwhile, the titleKey and the titleDescriptionKey values should be set to DEMO_TITLE and DEMO_DESCRIPTION respectively. NOTE: These two keys were made available in the multidimensional array returned by the getContents() method in the DemoMapMessages class.

Figure 24. Page Title Properties
Page Title

The next step is to add a PageLayouts container to the Page (right click on Page and select New -> PageLayouts). You can then add a PageLayout element to the PageLayouts container you just created as shown in Figure 25.

Figure 25. Creating a PageLayout
Page Layout

You should change the configType to "self" in the available drop down box (refer to Figure 26).

NOTE: There are three available page layout modes: 'init', 'self' and 'partner'. 'init' is used once for a module instance regardless of which chain(s) it is used in. The modes 'self' and 'partner' are used by the module in module-specific circumstances i.e. the module code decides when and where to look them up from. There is a logical separation between 'self' and 'partner' because of the different ways in which some modules are used in STS chains for federated single sign-on. The TFIM console will prompt for 'init' properties when a module instance is created. It will prompt for 'self' and 'partner' properties when a module is built into a chain.

Figure 26. Creating a "self" PageLayout
Page Layout Properties

The next step is to define the widgets we want to add to the PageLayout. For our custom module design, we want to add two text fields with appropriate component labels to the PageLayout. These text boxes will allow the user to configure the attribute details (name and value) that our custom plug-in should add into the STSUU when in map mode.

Each widget that you add to a PageLayout allows you to specify a 'modes' value. The 'modes' value acts as a filter and should indicate (in a comma-separated list) one or more calling modes that your module may be configured with (eg. validate, map, issue, exchange, authorize, other) when this parameter is needed. If the module is configured in a mode specified in this list, the parameter will be prompted for during configuration. If the module is not configured in a mode specified in the "modes" list, then it is assumed that this parameter is not needed in that mode, and it will not be prompted for by the console.

For the purposes of our project, we will select 'map' as the mode when configuring the module, and this should be the mode used in the 'modes' field for each of the text fields that we are adding since our custom plug-in is designed to operate in mapping mode only.

The two text fields that we create will both be specified required fields with string values. To create the first text field, right-click on the self (PageLayout) element that you just created and select New -> TextField. Set the values for 'name','required','modes' and 'valueType' as shown in Figure 27.

Figure 27. Adding a TextField for the attribute name
Text Field

A component label can now be added to this 'mymap.attribute.name' text field. Right click on the textField and select New -> ComponentLabel. Enter the following values:

  • textLabel: Attribute Name
  • ResourceBundleClass: com.tivoli.am.fim.demo.map.DemoMapMessages
  • ResourceBundleKey: TITLE_ATTRIBUTE_NAME

Figure 28 shows the configuration of the ComponentLabel for the mymap.attribute.name text field

Figure 28. Adding a Component Label
Text Field Component Label

We now need to create the second textField that will allow the attribute value to be configured. Create another textField on the PageLayout with the following values:

  • name: mymap.attribute.value
  • required: true
  • modes: map
  • valueType: string

Add a component label to this mymap.attribute.value TextField with the following configuration:

  • textLabel: Attribute Value
  • ResourceBundleClass: com.tivoli.am.fim.demo.map.DemoMapMessages
  • ResourceBundleKey: TITLE_ATTRIBUTE_VALUE

This completes the development of the plug-in, including all the required source code. Essentially we have developed a plugin.xml file (which contains all the configuration of the module including GUIXML) and two java classes - DemoMap which contains the actual java code for the module, and DemoMapMessages which contains internationalization strings.


Packaging the Module

Custom Module Packaging

Now that we have finished developing our custom module, we need to package it into a jar file that can be deployed into our TFIM 6.2 environment. We will export our plug-in project into a jar file.

Right click on the project in the Package Explorer panel and select "Export". The Export wizard will start as shown in Figure 29. Select Java™ -> JAR file and then click Next.

Figure 29. Exporting a Jar
Exporting Project as a Jar

Define which resources should be exported into the JAR and enter an appropriate export destination as illustrated in Figure 30. Click Next to proceed to the next screen.
NOTE: The following naming convention is recommended: <pluginName>_<version>.jar.

Figure 30. Jar Export Properties
Setting Export Properties

The JAR Packaging Options screen displayed in Figure 31 appears. Leave the settings as defaults. Click Next.

Figure 31. More Jar Export Properties
More Export Properties

The next screen allows you to configure the JAR Manifest Specification. It is important that you elect to use existing manifest from workspace and navigate to the manifest file from your plug-in project (see Figure 32). Click OK.

Figure 32. Selecting the Project's MANIFEST.MF
Manifest File

Click Finish on the JAR Manifest Specification screen (shown in Figure 33) to complete the JAR Export Wizard.

Figure 33. Jar Manifest Specification
export28

The custom module JAR will now be created in the export destination that was specified during the export wizard i.e. C:\temp\com.tivoli.am.fim.demo.map_1.0.0.jar


Deploying the module

Custom module deployment

This section describes how to deploy our custom module into TFIM 6.2.
First copy the custom module jar file com.tivoli.am.fim.demo.map_1.0.0.jar to the <TFIM_install_root>/plugins directory. The remaining high level steps involved in the deployment are as follows:

  1. Publish the plug-ins through the TFIM Console
  2. Re-Load the TFIM runtime*
  3. Create an instance of our custom module
  4. Configure a trust chain that uses the new plug-in

The remainder of this section describes these steps in more detail.

*NOTE: In previous versions of TFIM, the TFIM Runtime had to be re-deployed in order to detect and load changes to the <TFIM_Home>/plugins directory.
TFIM 6.2 provides the 'Publish Plug-ins' capability which copies jar files from the <TFIM_Home>/plugins directory on the server hosting the TFIM Management Application to the <WebSphereProfileRoot>/config/itfim/plugins directory of all TFIM Runtime nodes in that TFIM domain.

The 'Publish Plug-ins' operation does not reload the TFIM Runtime, but it does reload the TFIM Management application. The console will indicate when updated plug-in data is detected in the plug-ins directory with a message prompting a re-load.
The TFIM runtime must be explicitly reloaded before the new plug-in(s) can be used. The prompt can generally be ignored until all the necessary configuration required for the new plug-in is complete.

Publish the plug-in

Log in to TFIM console: https://<ip_address>:9043/ibm/console/. The TFIM Console shown in Figure 34 will be displayed.

Figure 34. The WebSphere / TFIM Console
TFIM Console

Expand the Tivoli® Federated Identity Manager options and select Domain Management -> Runtime Node Management. Figure 35 shows the Runtime Management panel.

Figure 35. Publishing Plug-ins with the Runtime Node Management Panel
Publish Plug-ins

Click on the Publish plug-ins button.

Load the configuration changes

Once the Publish plug-ins operation completes, a warning message will be displayed in the TFIM Console prompting you to load the recent configuration changes, as shown in Figure 36.

Figure 36. Load Configuration Changes
Load Configuration Changes

Click on the Load configuration changes to Tivoli Federated Identity Manager runtime button and wait for the process to complete.

Create an instance of the custom module

Create an instance of module in the TFIM Console. Navigate to the Configure Trust Service -> Module Instances section of the Management Console. Click on the Create button. A Module Type screen as shown in Figure 37 will appear. The DemoMap class that defines our custom module should be included in the list of available modules. Note that it may appear on the second page.

Figure 37. Module Types
Module Types

Select the DemoMap module, then click Next as shown in Figure 38.

Figure 38. Selecting the DemoMap Module Type
Selecting a module type

Enter a name and description for the new instance being created as illustrated in Figure 39.

Figure 39. Naming the new Module Instance
Name the instance

Click Finish and then re-load the configuration changes to TFIM runtime as prompted.

Create a Trust Service Chain

We can now create new Trust Service Chains that include the 'demoMapInstance' of our custom module.
Navigate to the Configure Trust Service -> Trust Service Chains section of the Management Console. A Trust Service Chains panel as shown in Figure 40 will be displayed.

Figure 40. Trust Chain Management
Trust Chain Management

Click on the Create button and the Trust Service Chain Mapping Wizard will begin. Figure 41 shows the Introduction screen for this wizard.

Figure 41. Trust Chain Wizard
Trust Chain Wizard

Click Next to proceed to the Chain Mapping Identification screen, as shown in Figure 42. Enter the following values for our basic trust chain and then click Next:

  • Chain Mapping Name:DemoChain
  • Description: Test DemoMap module instance
Figure 42. Chain Mapping Identification
d9

The next screen allows you to configure the Chain Mapping Lookup properties. The RequestType for our chain should be set to Validate and addresses for AppliesTo and Issuer need to be entered as shown in Figure 43.

Figure 43. Chain Mapping Lookup Parameters
d10

Click Next and the Chain Identification details can be entered:

  • Chain Name: DemoChain
  • Description: Demo chain including custom mapping module
Figure 44. Chain Identification
Chain Identification

Click Next. We can now specify the Chain Assembly. For simplicity in this tutorial we have decided to include the custom mapping module in a basic trust chain consisting of a Default STSUU Instance in 'validate' mode, followed by the custom module in 'map' mode to add an extra attribute and finally another Default STSUU Instance to 'issue' the token.

Add these selected module instances to the chain so that the created chain assembly appears as illustrated in Figure 45.

Figure 45. Chain Assembly
Chain Assembly

Click Next to continue. The next screen in the Wizard, as shown in Figure 46, is the configuration screen for the first module in the chain. This module is the Default STSUU instance in validation mode. There is no configuration required for this module.

Figure 46. STSUU Validate Properties
STSUU Validate

Click Next and the configuration screen of our custom module will be displayed as shown in Figure 47. Enter the name and value for the attribute that should be added to the STSUU object. For this tutorial we will add a test attribute with name 'testName' and value 'testValue'.

Figure 47. DemoMap Configuration Properties
Configure DemoMap

Click Next. The next screen in the Wizard is the configuration screen for the last module in the chain, as shown in Figure 48. This module configuration is also for the Default STSUU instance (this time in issue mode). Once again, there is no configuration required.

Figure 48. STSUU Issue Properties
STSUU Issue

Click Next and a summary of the new trust chain is displayed as shown in Figure 49.

Figure 49. Chain Summary
Chain Summary

Click Finish to complete the wizard. Click on the button to load the latest configuration changes into the TFIM runtime.

Figure 50. Created Chain
Created Chain

Figure 50 above shows the new chain which appears in the TFIM console.


Testing the module

Custom module testing

In order to test our module, we need a way to send a WS-Trust request (RST) to the STS. A simple command line utility called cURL can be used (as a command-line browser) to send a canned SOAP message to the TFIM STS. Create a file called rst.xml with the following content:

Listing 4. rst.xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust"
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:stsuuser="urn:ibm:names:ITFIM:1.0:stsuuser">
  <soapenv:Header/>
  <soapenv:Body>
    <wst:RequestSecurityToken>
      <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Validate
      </wst:RequestType>
      <wst:Issuer>
        <wsa:Address>http://issuer/demo</wsa:Address>
      </wst:Issuer>
      <wsp:AppliesTo>
        <wsa:EndpointReference>
          <wsa:Address>http://appliesto/demo</wsa:Address>
        </wsa:EndpointReference>
      </wsp:AppliesTo>
      <wst:Base>
        <stsuuser:STSUniversalUser>
          <stsuuser:Principal>
            <stsuuser:Attribute name="name">
            <stsuuser:Value>demoName</stsuuser:Value>
            </stsuuser:Attribute>
          </stsuuser:Principal>
          <stsuuser:AttributeList />
        </stsuuser:STSUniversalUser>
      </wst:Base>
    </wst:RequestSecurityToken>
  </soapenv:Body>
</soapenv:Envelope>

Notes on the rst.xml file:

  • The wst:RequestType should be set to Validate (since the TrustChain was configured for Validate requests.
  • The Issuer Address must have an address value that matches the Issuer value filled in when configuring the chain
  • The AppliesTo Address must have an address value that matches the AppliesTo value filled in when configuring the chain
  • This RST and our example chain do not make use of the optional TokenType for chain matching

In order to invoke the STS with the RST, use a curl command similar to that shown below:

curl --header "soapaction: anything" --data-binary @rst.xml "http://localhost:9080/TrustServer/SecurityTokenService"

The result from the TFIM STS should be a RequestSecurityTokenResponse (RST), similar to that shown here (note - some portions of the response have been removed from this listing for brevity):

Listing 5. rstr.xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Header/>
  <soapenv:Body>
    <wst:RequestSecurityTokenResponse Context=""
      wsu:Id="uuid21de0649-0116-1739-a977-db18971c0b6d"
      xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust"
      xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-
      wss-wssecurity-utility-1.0.xsd">
      ...
      <wst:RequestedSecurityToken>
        <stsuuser:STSUniversalUser xmlns:stsuuser="urn:ibm:names:ITFIM:1.0:stsuuser">
          <stsuuser:Principal>
            <stsuuser:Attribute name="name" type="">
              <stsuuser:Value>demoName</stsuuser:Value>
            </stsuuser:Attribute>
          </stsuuser:Principal>
          <stsuuser:AttributeList>
            <stsuuser:Attribute name="testName" type="">
                <stsuuser:Value>testValue</stsuuser:Value>
            </stsuuser:Attribute>
            </stsuuser:AttributeList>
        <stsuuser:RequestSecurityToken>
        ...
        </stsuuser:RequestSecurityToken>
        <stsuuser:ContextAttributes/>
        </stsuuser:STSUniversalUser>
      </wst:RequestedSecurityToken>
      <wst:Status>
      <wst:Code>http://schemas.xmlsoap.org/ws/2005/02/trust/status/valid</wst:Code>
      </wst:Status>
    </wst:RequestSecurityTokenResponse>
  </soapenv:Body>
</soapenv:Envelope>

Troubleshooting

Debugging and trace

As you may have noticed in the source code in Listing 2, the demonstration code for the DemoMap class makes use of the java.util.logging.Logger class and infrastructure for trace output. This allows for runtime tracing using standard WebSphere® configuration for trace settings and output. For example, on the WebSphere node where the TFIM runtime is executing, use the WebSphere administration console to navigate to Troubleshooting->Logs and Trace->server1->Change Log Detail Levels and update the Runtime or Configuration tab trace string to include "com.tivoli.am.fim.demo.*=all" and you will be able to observe all the trace output available from our demonstration code in the WebSphere trace.log for the server. Refer to Figure 51 below.

Figure 51. Configuring trace setting
Trace Settings

More information on configuration of WebSphere tracing is available in the WebSphere Information Center

Debugging loading problems via the OSGi console

In addition to updating the WebSphere log levels to obtain more detailed trace, TFIM also provides an OSGi console to help you debug any plug-in loading problems. The OSGi console should only be used for debugging purposes and should be disabled after the problem is fixed. Enabling the OSGi console will open a new port on your application server and posts a security risk if left accessible. To enable the OSGi console, you'll need to modify the ITFIMRuntime application's launch.ini file:

  • Open the launch.ini file which is located at <was_profile_root>/installedApps/<node>/ ITFIMRuntime.ear/com.tivoli.am.fim.war.runtime.war/WEB-INF/eclipse/launch.ini
  • Uncomment the property osgi.console.port and set its value to an unused port on your server (e.g. osgi.console.port=8888)
  • Save the file and restart the WebSphere Application Server


When the application server is restarted, TFIM will launch the OSGi console at the port you specified. OSGi console is a command line interface that allows to you view the bundle status in the OSGi runtime and manage the bundles' life cycle. From a terminal or command prompt, use the telnet command to access the OSGi console:

telnet localhost 8888

This will open the OSGi prompt. To get a list of commands you can execute, enter 'help' command at the OSGi prompt. To exit from the OSGi console, use the 'disconnect' command. DO NOT use the 'exit' or 'stop' command because they may crash the WebSphere Application Server. For debugging potential bundle loading problems, first use the 'ss' command to display a list of installed bundles in the OSGi runtime and their states.

Figure 52. A list of installed OSGi bundles in the TFIM Runtime
OSGi Console

An OSGi bundle's state can be either INSTALLED, RESOLVED, STARTING, ACTIVE, STOPPING, or UNINSTALLED. Typically, if your bundle is loaded correctly, it will be in the RESLOVED state at WebSphere start up. However, if the OSGi runtime encountered any exceptions or errors while loading your bundle, the bundle will be left in the INSTALLED state. To view the exception of starting a bundle in the INSTALLED state, run the command 'start <bundle-id>', where the bundle-id is the first column displayed in Figure 52. After you run the 'start' command, the OSGi console will display a Java exception stack trace to help you debug the problem. The start-up errors will also be logged in the Eclipse configuration directory under:
<was_profile_root>/temp/<node>/<server>/ ITFIMRuntime/com.tivoli.am.fim.war.runtime.war/itfim/configuration/

One common problem with loading your custom bundle is that your bundle uses a new package from the WebSphere container, but you forgot to export that package via the com.tivoli.am.fim.osgi.connector_6.2.0.0 MANIFEST.MF (see Advanced Development Considerations). For example, suppose when you try to start your custom bundle, and the following exception occurs:

"org.osgi.framework.BundleException: The bundle could not be resolved.
Reason: Missing Constraint: Require-Bundle: com.tivoli.am.fim.common; bundle-version="0.0.0"... blah blah blah...!MESSAGE Bundle initial@reference:file:plugins/com.tivoli.am.fim.sps_6.2.0.0.jar/ was not resolved"

The error basically says, "I cannot resolve your bundle because the com.tivoli.am.fim.common bundle is missing", which is a misleading message because the com.tivoli.am.fim.common bundle does exist in the runtime and the plugins directory. The real cause of the problem is likely to be "I cannot resolve your bundle and the com.tivoli.am.fim.common bundle". The com.tivoli.am.fim.common is the first bundle that needs to be resolved because all other bundles depend on it. Since common only depends on packages from external libraries in the J2EE container, problems with find these packages will cause a failure.

To resolve this problem, run the 'start' command on the com.tivoli.am.fim.common bundle, and a new exception should be displayed that indicates which packages you are really missing. Follow the steps shown in Advanced Development Considerations to update the MANIFEST.MF file of the com.tivoli.am.fim.osgi.connector_6.2.0.0 bundle with the missing packages.

Restarting the OSGi engine of the TFIM Runtime and Management Service

Recall that when you update your custom plug-in and republish the plug-ins, the TFIM console prompts you to reload the configuration change (see Figure 36). The same ITFIM Runtime reload functionality can be executed directly by using the "Reload Configurations" button on the Runtime Node Management panel. Think of this operation as clearing any memory cache and restarting the ITFIM Runtime without restarting the WebSphere Application Server.

In addition to reloading the ITFIM Runtime configurations, you may also reload the ITFIM Management Service configurations via the ITFIM console. This is useful if the plug-in you added is not showing up in the ITFIM console.

To reload the Management Service configurations, go to Domains panel, select your domain and click Properties. Select the Domain Information tab, and click "Refresh Management Service", as shown in Figure 53:

Figure 53. Reloading the OSGi runtime for the Management Service
Reload Management Service

Advanced development considerations

Class loaders and calling WebSphere or J2EE-container-provided libraries

Plug-ins written for Tivoli® Federated Identity Manager 6.2 operate in an OSGi runtime environment. In this environment, each plug-in uses its own class loader, and this class loader doesn't automatically provide access to java packages and classes from the J2EE container. This means that if you try to access many standard WebSphere® or J2EE™ capabilities, you will likely encounter class loading issues. In this section we will demonstrate how to access WebSphere and other J2EE or external classes from within a plug-in.

The TFIM OSGi runtime environment uses a special bundle called com.tivoli.am.fim.osgi.connector_6.2.0.0 to declare exports from WebSphere and other standard libraries that may be used by other plug-ins (including your own custom plug-in). Many of the standard packages your module may wish to import will already have been declared in the com.tivoli.am.fim.osgi.connector_6.2.0.0 bundle. To see the existing set of imports, look at the file <FIM_INSTALL_ROOT>/plugins/com.tivoli.am.fim.osgi.connector_6.2.0.0/META-INF/MANIFEST.MF.

In your development environment, the Plug-in Target Platform includes the com.tivoli.am.fim.osgi.connector_6.2.0.0 bundle expanded. Whilst this lists all the classes exported for use in other plug-ins as they will be available in the WebSphere runtime, your development environment does not have immediate access to all the libraries that contain the actual classes. This means that your development environment will show compile time errors trying to reference libraries that are only available once the plug-in is deployed. To fix this problem in your development environment, we have a procedure which will allow you to resolve the classes you need, and build in a "clean" development environment. The procedure also shows you how to generate an updated MANIFEST.MF (if necessary) to replace the MANIFEST.MF in the existing com.tivoli.am.fim.osgi.connector_6.2.0.0 expanded bundle. The MANIFEST.MF update is only necessary when you wish to use classes that are provided by the WebSphere J2EE container (or are in the WebSphere JRE's classpath) but which are not already listed in the MANIFEST.MF.

Resolving container-provided classes in your development environment with the TFIM SDK project

This section outlines the process for setting up and working in your development environment with a plug-in that wishes to use container-provided classes that are not a part of the standard J2SE API's. To illustrate the process we will make a small modification to our demonstration mapping module which will have it utilize TAM API's to set an extra attribute in the STSUU for the TAM user's first name. The TAM API's are found in PD.jar, which is available to the container at <WebSphere_AppServer>/java/jre/lib/ext/PD.jar. Here's a code snippet that show's what we are going to try and do:

Listing 6. TAM code to be added to the DemoMap.java mapping module
...
import com.tivoli.pd.jutil.PDContext;
import com.tivoli.pd.jutil.PDMessages;
import com.tivoli.pd.jadmin.PDUser;
...

try {
  // get the PDUser
  PDContext pdc = new PDContext(
  new java.net.URL("file:///path_to_tam_config_file"));
	PDMessages msgs = new PDMessages();
	PDUser pdu = new PDUser(pdc, uuser.getPrincipalName(), msgs);
	msgs.clear();

  // add the firstname as an attribute
  Attribute fn = new Attribute("FirstName", ATTR_TYPE,
    new String[] { pdu.getFirstName() });
  uuser.addAttribute(fn);
} catch (Exception e) {
	// oh well, just don't add it
}

Note that in this particular case the packages to be imported (com.tivoli.pd.jutil and com.tivoli.pd.jadmin) are already exported in the com.tivoli.am.fim.osgi.connector_6.2.0.0 MANIFEST.MF, so we will not need to update that file. Even though we do not have to update it, the process below will demonstrate how to update the MANIFEST.MF so that you have the complete procedure.

First we need to tell our plug-in that we will be using these classes from another package. To do that, open the MANIFEST.MF of the project, and switch to the Dependencies tab, and add the TAM packages to the Imported Packages list, as shown here:

Figure 54. Importing container packages to MANFIEST.MF
Importing Container Packages to MANFIEST.MF

Now let's see what happens if we just try to import the classes and update the code:

Figure 55. Errors importing container packages
Errors Importing Container Packages

This is clearly not good - as the classes should be available in the runtime environment. We could try to add PD.jar directly to the project, and update the project's classpath to use the local copy, however this is not the right thing to do for packages which will be provided from the runtime execution environment. Instead, you should follow these instructions:

  • Copy com.ibm.ws.runtime_6.1.0.jar and com.ibm.wsspi.extension_6.1.0.jar from <WebSphere6.1_AppServer>/plugins to the TFIM plug-ins directory where your Target Platform is pointing. If you don't have a WAS 6.1 installation, you can install an eWAS 6.1 instance via the TFIM installer and get the plug-ins from eWAS.
  • Reload your Target Platform under Window->Preferences->Plug-in Development->Target Platform
  • Obtain the com.tivoli.am.fim.sdk.zip file found in the Downloads section of the tutorial, then expand it and import into your workspace as an existing project.
  • Copy the third-party jars needed by your custom plug-in into the com.tivoli.am.fim.sdk project, and use the Plug-in Manifest Editor to add the jars to the sdk bundle's Bundle-ClassPath. This is done on the Runtime tab under Classpath:
    Figure 56. Updating the SDK classpath
    Updating the SDK Classpath

    This brings us to an interesting point - what if you know the packages for the classes you wish to use and can see they are exported in the MANIFEST.MF from the com.tivoli.am.fim.osgi.connector_6.2.0.0 bundle, but are not sure of the JAR files that contain the classes so that you can add it to the SDK project?

    To aid with that process we have included in the Downloads section a special text file called osgiconnector_exports_to_jar_map.txt which contains a mapping of which JAR's a particular package's classes can be found in. Use this file for reference if you need to locate the JAR file that contains the classes you wish to use.
  • Export the packages needed by your custom plug-in in the sdk bundle's Manifest. This is done with the Plug-in Manifest Editor on the Runtime tab under Exported Packages:
    Figure 57. Updating the SDK exported packages
    Updating the SDK Exported Packages
  • Expand your custom plug-in project in the Java or Plug-in Development perspective, and verify if the sdk project is automatically listed under your plug-in project's Plug-in Dependencies list. If com.tivoli.am.fim.sdk is not, but org.eclipse.osgi bundle is listed, you will still have compile errors in your custom plug-in, as shown here:
    Figure 58. SDK not in dependencies
    SDK not in dependencies

    To fix this in the Eclipse 3.3 IDE, go to Windows->Preferences->Plug-in Development->Target Platform. Uncheck org.eclipse.osgi from the list of plug-ins and click "OK".
    To fix this in the Eclipse 3.2 IDE (including Rational Software Architect 7) go to Windows->Preferences->Plug-in Development->Target Platform. Uncheck org.eclipse.osgi from the list of plug-ins and click "Apply", then recheck the org.eclipse.osgi plug-in, and click "OK". You will have to do this again whenever you restart Eclipse 3.2.
    Your custom plug-in project's Plug-in Dependencies should now show com.tivoli.am.fim.sdk instead of org.eclipse.osgi, and your compile errors should have resolved, as shown here:
    Figure 59. SDK in dependencies
    SDK in dependencies

Now the plug-in is resolved, and can be exported (after filling in the correct path for the TAM configuration file of course) and deployed and it will run successfully in the TFIM runtime environment.

Essentially what has been done with the above process is to use the Export-Package list in the MANIFEST.MF of com.tivoli.am.fim.sdk project as a subset of the runtime-available list in the com.tivoli.am.fim.osgi.connector_6.2.0.0 project. We only add to the sdk project the subset of available container-provided jars, and export the particular packages we need. Typically com.tivoli.am.fim.osgi.connector_6.2.0.0 will be a superset of the jars/packages you use in your SDK project. The SDK project exists only to resolve build-time packages.

Whilst in this case there was no need to add new packages to the Export-Package list of the com.tivoli.am.fim.osgi.connector_6.2.0.0 MANIFEST.MF (because the dependent packages were already exported), there may be rare occasions when you know of a container-available package that is not already listed. If that is the case, you may add the package you need to the MANIFEST.MF yourself. This should be done in the <FIM_INSTALL_ROOT>/plugins/com.tivoli.am.fim.osgi.connector_6.2.0.0/META-INF/MANIFEST.MF file, and then perform a "Publish Plug-ins", followed by "Reload Configurations" from the management console. This is the very reason why the com.tivoli.am.fim.osgi.connector_6.2.0.0 plug-in is already expanded in the TFIM plug-ins directory. To help you automatically build the correct replacement MANIFEST.MF, there is a utility application built into the com.tivoli.am.fim.sdk project which will take as parameters the existing com.tivoli.am.fim.osgi.connector_6.2.0.0's MANIFEST.MF and your SDK's MANIFEST.MF and compute the union of the exported packages and generate a new MANIFEST.MF for you with all the packages. You do not need to use this utility, but it is provided for convenience including fully commented, self-documenting source. You can run this utility (a simple Java command-line program) straight from the IDE if you wish, and that is what will be demonstrated here.

To invoke the MANIFEST.MF generation application, open a Java Perspective in the IDE, and in the Package Explorer navigate to OSGiExtensionBundleGenerator.java, right-click, and select Run As->Run, as shown in Figure 60:

Figure 60. Invoking OSGiExtensionBundleGenerator
Invoking OSGiExtensionBundleGenerator

You then need to enter the program arguments. The first parameter is the path to the existing com.tivoli.am.fim.osgi.connector_6.2.0.0 MANIFEST.MF. The second parameter is the path to the SDK's MANIFEST.MF, and the third is the output directory to write the results. Figure 61 shows these for my development environment:

Figure 61. OSGiExtensionBundleGenerator arguments
OSGiExtensionBundleGenerator Arguments

Press Run, to execute the application, and observe the output in the Console window. If it all runs well, you should see output similar to that shown in Figure 62:

Figure 62. OSGiExtensionBundleGenerator output
OSGiExtensionBundleGenerator Output

In our demonstration there is now a new MANIFEST.MF in C:\temp\output\com.tivoli.am.fim.osgi.connector_6.2.0.0\META-INF\MANIFEST.MF which can be replace the <FIM_INSTALL_ROOT>/plugins/com.tivoli.am.fim.osgi.connector_6.2.0.0/META-INF/MANIFEST.MF. The newly developed mapping plug-in should also be exported and copied to <FIM_INSTALL_ROOT>/plugins. The console is then used to Publish Plug-ins, then Reload Configurations and the module is ready for use.

Switching Class Loaders at runtime

In addition to resolving compile-time issues by updating the MANIFEST.MF, you may also run into run-time problems in your plug-in when calling certain WebSphere API's that use context class loading. Modules written for Tivoli Federated Identity Manager 6.2 use the class org.eclipse.core.runtime.internal.adaptor.ContextFinder as the thread context class loader instead of the one provided by the WebSphere container. For calling operations such as SOAP Message Factory instantiation, JNDI lookup and RMI lookup you need to temporarily switch the context class loader from TFIM's OSGi runtime to the WebSphere container. This can be achieved by leveraging a TFIM-provided wrapper mechanism called J2EEContainerAction. To use this mechanism, first create a class that implements com.tivoli.am.fim.j2eeactions.J2EEContainerAction. This interface is exported by the com.tivoli.am.fim.common bundle:

Listing 7. J2EEContainerAction interface
package com.tivoli.am.fim.j2eeactions;

public interface J2EEContainerAction {

    public Object run() throws Exception;
}

As an example, consider this simple action class which performs an RMI lookup:

Listing 8. Example J2EEContainerAction implementation
package com.tivoli.am.fim.j2eeactions;

import java.rmi.Naming;

public class RMILookupAction implements J2EEContainerAction {

    String _endpoint;

    public RMILookupAction(String endpoint) {
        _endpoint = endpoint;
    }
    
    public Object run() throws Exception {
        // call container-classloader-required code here
        return Naming.lookup(_endpoint);
    }
};

Next, you will invoke your action class via the com.tivoli.am.fim.j2eeactions.J2EEContainerFactory class, as shown here:

Listing 9. Utilizing your J2EEContainerAction
import com.tivoli.am.fim.j2eeactions.J2EEContainerFactory;
import com.tivoli.am.fim.j2eeactions.RMILookupAction;

...
String naming_endpoint = "rmi://....";
RmiLookupAction myAction = new RmiLookupAction(naming_endpoint);

MyRemoteObject o = (MyRemoteObject) 
  J2EEContainerFactory.runInJ2EEContainer(myRmiLookupAction);
...

The J2EEContainerFactory will handle the thread context class loader switching from TFIM's OSGi runtime to the J2EE container. Then, it will call the run() method in your action class, and finally switch the context class loader back to the original. To aid in regular development tasks, the following three commonly used J2EEContainerActions have bee defined for you:

Table 2. Supplied Common J2EEContainAction implementations
Class NameDescriptionUsage Example
com.tivoli.am.fim.j2eeactions.CreateMessageFactoryActionCreate a javax.xml.soap.MessageFactory instance.
import com.tivoli.am.fim.j2eeactions.J2EEContainerFactory;
import com.tivoli.am.fim.j2eeactions.CreateMessageFactoryAction;
import javax.xml.soap.MessageFactory;
...
CreateMessageFactoryAction mfAction = new CreateMessageFactoryAction();
MessageFactory mf = (MessageFactory)
  J2EEContainerFactory.runInJ2EEContainer(mfAction);
...
com.tivoli.am.fim.j2eeactions.JndiLookupActionPerform lookup of a String name, equivalent to: (new javax.naming.InitialContext()).lookup(name);
import com.tivoli.am.fim.j2eeactions.J2EEContainerFactory;
import com.tivoli.am.fim.j2eeactions.JndiLookupAction;
import com.ibm.websphere.security.UserRegistry;

...
JndiLookupAction myJndiLookupAction = new JndiLookupAction("UserRegistry");
UserRegistry webSphereUserRegistry = (UserRegistry) 
  J2EEContainerFactory.runInJ2EEContainer(myJndiLookupAction);
...
com.tivoli.am.fim.j2eeactions.RmiLookupActionCreate a javax.xml.soap.MessageFactory instance.
import com.tivoli.am.fim.j2eeactions.J2EEContainerFactory;
import com.tivoli.am.fim.j2eeactions.RMILookupAction;

...
String naming_endpoint = "rmi://....";
RmiLookupAction myAction = new RmiLookupAction(naming_endpoint);

MyRemoteObject o = (MyRemoteObject) 
  J2EEContainerFactory.runInJ2EEContainer(myRmiLookupAction);
...

Using third party libraries in your custom module

In addition to TFIM, WebSphere and J2EE API's, your custom module may also need to call third-party libraries. In this case, you will need to embed the additional jar files into your bundle.

Using your IDE (Eclipse or Rational® Application Developer):

  • Copy the third-party jar files you need directly into the workspace under the plug-in project of your custom module.
  • Open the project's MANIFEST.MF file with the Plug-in manifest editor. Select the Runtime tab, and add the libraries from your plug-in project to the Classpath. This will update your project's MANIFEST.MF with a new stanza called Bundle-ClassPath. Make sure the Bundle-ClassPath includes your embedded jar files as well as ".", which represents the classpath to the local classes you've created in your bundle.
  • When you export the plug-in project, make sure the third-party jar files are included inside the bundle jar.

The libraries declared in the Bundle-ClassPath stanza will only be visible to the classes inside that bundle. However, you may make the packages of the embedded jars visible to other bundles by declaring them in the Export-Package stanza. In case you haven't already noticed, this is exactly what the com.tivoli.am.fim.osgi.connector_6.2.0.0 package does for WebSphere and standard J2EE packages!

In very rare cases some 3rd-party libraries may not be able to be run from within the OSGi environment at all. In that case you can copy them to the <WebSphere_AppServer_root>/lib directory and then treat them like new container-provided packages as demonstrated in the section Resolving Container-Provided Classes in your Development Environment with the TFIM SDK Project.


GUIXML widgets reference

Available GUI XML Widgets and their Configuration

When designing a new page layout using GUI XML, (as was done earlier in this tutorial as part of "Developing the Custom Module"), there are a number of available widgets. For the purposes of the GUI design for our demo module, only TextFields with their associated labels were required (refer back to Figure 22).
This section describes all the available widgets and their configuration when developing custom Java™ modules.

There are 8 widget types available for use in a Page Layout, as shown in Figure 63 below.

Figure 63. Available GUI Elements
GUIXML Widgets

Following is a table showing the various widgets and their configuration properties. There are some commonalities among the widgets. For example:

  • name - The name is always required, and represents the name of the configuration parameter. This is the same name that you use to retrieve the parameter value in your module code. See example in Listing 2.
  • required - I bet you can figure this one out.
  • modes - This acts as a filter, and is a comma-separated listed of STS module modes that the module may operate in and use this parameter. The set of modes we are talking about is the set of operational modes that the module can be configured in when added to a module chain (i.e. validate,map,issue,exchange,other,authenticate,authorize). The console will only prompt for the parameter when the module is configured in a listed mode. For example it makes no sense to prompt for a "signature validation key" for a module that is in issue mode. Similarly it doesn't make sense to prompt for a "signing key" for a module in validate mode. The console only prompts for the parameters required for the mode the module is operating in.



Non-obvious properties for the widgets are described in detail in the table.

Widget TypeDescriptionConfigurable PropertiesConfiguration Notes
TextFieldText input box that allows users to enter text on a single line.name*
required* {true|false}
modes*
valueType*
multivalued {true|false}
password {true|false}
displayWidth
defaultValue
issuerField {true|false}
The valueType for a text field is not currently used, but the intent is to eventually use it for validating entry. For now we recommend you set it to string (this is what the internal modules use), and perform your own validation of the input at runtime.
The multivalued attribute allows a TextField to behave like a TextArea. This is historical, and we now recommend using a TextArea where multiple lines of input are desired.
The password attribute determines if the characters on the display are replaced with *. There are two main differences between using this and a PasswordField widget. First, the value is stored in plaintext when using a TextField, but obscured when using a PasswordField. Second, the PasswordField provides a secondary "re-enter password" field for validation, whereas TextField does not.
The displayWidth attribute is an integer that controls the default width of the input string. The default is 60 and is suitable for most purposes.
The displayValue attribute allows you to pre-populate the text field with a default value.
The issuerField attribute is a special attribute that is used internally by TFIM to occasionally hide a text field attribute from the display when collecting parameter information in wizards. For example the issuer attribute for generating SAML assertions is generally defaulted to the Identity Provider ID, and for simplifying the amount of data collected it is not prompted for during wizard federation creation. It is not recommended that you use this field for your own token modules.
TextAreaText input area that allows the user to enter large amounts of text across multiple lines.name*
required* {true|false}
modes*
This is a multi-line text entry field. You can distinguish the lines in your code by looking at the String[] returned by STSGroupMembership.getSelfProperties or STSGroupMembership.getPartnerProperties
CheckBoxA check box (tick box) that represents an option to the user. The user is permitted to make multiple selections from a number of options.name*
required* {true|false}
modes*
checked* {true|false}
The checked attribute determines if the checkbox is pre-selected as checked.
KeyIdentifierInputAllows the user to enter a key identifier. A key identifier represents a public or private key, and consists of a keystore, plus the label of the key within that keystore.name*
required* {true|false}
modes*
checked* {true|false}
enabledName
keyTypes
validateUseKeyInfoName
This complex widget consists of a set of components which allow you to pick a key. It looks something like this:
KeyIdentifier Widget

The enabledName attribute describes an optional configuration parameter name. If provided, the widget will display a checkbox to describe if the function associated with this key is enabled. If no enabledName value is provided, a key is to always be prompted for. If a value is provided, a checkbox will appear (like the example above which has the label "Really need a key?"). When the checkbox is selected, the entire rest of the key widget is enabled, when it is not selected, the rest of the key widget is disabled. The tricky part here is providing a label for the checkbox. This is done by adding a ComponentLabel under the KeyIdentifierInput and setting the for: attribute of the ComponentLabel to checkbox, as shown here:
KeyIdentifier Checkbox Label

Note that this ComponentLabel is in addition to the one which describes the label for the key identifier widget itself (defined as "My KeyID" above). To define the ComponentLabel for the actual key identifier widget, set the for: attribute of the ComponentLabel to keyidentifier, or just leave it blank.

The first element in the main portion of the key widget (labeled Keystore) is a drop-down list box of keystore names. This will be filtered based on the type of keys selected in the keyTypes parameter.
The keyTypes parameter is a comma-separated list of one or more of the following values:
  • public - a public key, such as is typically used for XML encryption or signature validation
  • keypair - a private/public key pair such as is typically used for XML decryption or signature generation
  • symmetric - a symmetric key (e.g. DES)

The second element in the main portion of the key widget is for the Keystore Password, which is the password used to open the keystore you have selected.

The third element in the main portion of the key widget is the List Keys button, and the table which shows all the listed keys. The List Keys button should only be pressed after the password is entered, and the keys will only be listed if the password is correct. Each of the keys in the keystore which match the keyTypes filter will be shown.

The last part of the main portion of the key widget is actually a variable set of drop-down list boxes which permit you to select which portions of the key are included in the KeyInfo element of digital signatures. Of course in your own module, this will not have the same semantics as what you use the key for is completely up to your own code. Therefore unless you have specific requirements for allowing a user to select these elements, you probably will not need to use them in your own modules. For completeness of documentation though, you can optionally decide whether or not to prompt for any of the following:
  • The public key
  • The X509 certificate data
  • The X509 subject name
  • The X509 subject key identifier
  • The X509 issuer details

For each attribute you will be prompted for one of the following values:
  • Use the default (which equates to unset)
  • Yes (which equates to a value of true)
  • No (which equates to a value of false)
In our example above you will see we only added prompts for two of the four variable portions (for demonstration purposes).

The checked attribute controls whether or not the main portion widget is enabled, and basically pre-checks the optional parameter defined by the checkedName attribute for enabling or disabling key selection. It doesn't make sense to set this attribute to false unless you are definitely including the enabledName attribute and want it to be disabled by default. A good example where TFIM uses this combination internally is enabling signatures for the TAM Credential module.

The only other attribute not yet mentioned is validateUseKeyInfoName. This is an advanced configuration parameter we do not recommend using in your own modules (leave it blank). This parameter again represents an optional configuration parameter name, which if provided, causes the key widget to prompt for an additional key selection options (not shown in our example) via a radio button group at the very top of the main part of the widget. The radio buttons allow you to select whether or not a key to be used for signature validation should come from the KeyInfo inside an XMLSignature element itself, or from a user-selected key from the widget (as shown in our example). If signature validation is done from KeyInfo information, then no key is selected from a keystore, and instead we prompt for an optional subject DN expression for allowable X509 certificates. The semantics of this property are very particular to TFIM internal API options for signature validation, and it is more likely that for your own module you would use a combination of other widgets to prompt for these style of options rather than this particular attribute of KeyIdentifierInput.
ComboBoxA drop down list box containing a list of existing options that can be selected from.name*
required* {true|false}
modes*
editable {true|false}
The editable attribute describes whether or not the user can put in their own text rather than selecting one of the built-in options. The widget allows you to add an Options element, and to this you add one or more child Option elements. Each Option has a display label and value.
TDIModuleInput*For internal use only as part of the TDI mapping module.name*
required* {true|false}
modes*
tdi.hostname
tdi.port
tdi.poolsize
tdi.maxwaitingthreads
tdi.maxwaittime
tdi.config
tdi.assemblyline
Do not use this widget in your own modules.
PasswordFieldA pair of text input fields where the entered characters will be obfuscated so that they do not appear on the screen in clear text.name*
required* {true|false}
modes*
displayWidth
The displayWidth has the same meaning as for a TextField widget.

The widget will automatically prompt for the same value twice, and will validate that the input is the same for both entry fields. It will also automatically obfuscate the resulting value before storing it in configuration. When reading a password from configuration in your module code, it is necessary to call the methodcom.tivoli.am.fim.utils.Password.getUnObfuscatedPassword(obfuscatedPassword) with the value of the parameter to access the cleartext password.
RadioButtonGroupA radio button (option button) allows the user to choose one of a predefined set of options.name*
required* {true|false}
modes*
defaultButtonIndex
A RadioButtons element is added to a RadioButtonGroup, and under that an ordered list of RadioButton elements are added to build the list of options. Each RadioButton has both a label and a value (as would be expected). The ordered list is zero-indexed, so setting the defaultButtonIndex to 0 will pre-select the first radio button.

NOTE: * indicates a required field.


Token module example

The primary example used in this tutorial was a mapping module, which did a very basic transformation by adding an attribute name/value pair to an STSUniversalUser.

In the Downloads section of this tutorial you will also find a HelloToken example module that you can import as a project into your development environment, or potentially use as an example in a TFIM Runtime environment. The HelloWorldToken example is a trivial token module that supports the validate, issue and exchange modes of operation (issue and exchange are considered synonymous). It supports a trivial "HelloToken", which looks something like this:

<hw:HelloWorld xmlns:hw="urn:hello:world" name="namevalue" attribute="attributevalue" />

In validate mode, it will not perform any real syntax checking on the token, but it will set the STSUniversalUser's name to the namevalue (if found), and will set an attribute called attribute to the attributevalue if found.

Similarly in issue/exchange modes, it will create an XML token of the above format, setting the name and attribute XML attributes if found in the STSUniversalUser.

It is hoped this project may be a good starting point for developers putting together their own custom STS module which will support their own custom token types. The project also includes two example RST's in XML format that you may use to test the module.

  • To test the validate operation, create a trust chain that has two modules in it - a HelloToken instance in validate mode, followed by an STSUU token instance in issue mode. Then use curl and the rst_hw.xml to test validating the HelloWorld token.
  • To test the issue/exchange operation, create a trust chain that has two modules in it - an STSUU token instance in validate mode, followed by a HelloWorld token instance in issue mode. Then use curl and the rst_stsuu.xml to test validating the STSUU token and issuing a HelloWorld token.

Note also that these RST examples use WS-Trust 1.3 format, which requires use of the OASIS Validate URI when building your trust chain mapping criteria. You should also use the WS-Trust 1.3 endpoint as the target for the RST's, with a command like:

curl -k --verbose --header "soapaction: blah" --header "Content-type: application/soap+xml; charset=utf-8" --data-binary @rst_hw.xml http://localhost:9080/TrustServerWST13/services/RequestSecurityToken

You can obviously extrapolate this example to your own token requirements.


Downloads

DescriptionNameSize
Example mapping modulecom.tivoli.am.fim.demo.map_1.0.0.jar6KB
Skeleton TFIM SDKcom.tivoli.am.fim.sdk.zip5KB
Example HelloWorld token modulecom.tivoli.am.fim.demo.helloworld_1.0.0.jar10KB
Java Package to Jar Maposgiconnector_exports_to_jar_map.txt90KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Tivoli (service management) on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Tivoli (service management), Tivoli, Java technology, Security
ArticleID=329708
ArticleTitle=Developing a custom Java module
publish-date=09122008