Service Provisioning Security Pattern


This section introduces a security pattern that synchronizes authentication and authorization credentials across different Provisioning Service Targets (application systems). Many application systems have their own user authentication and access control mechanisms. They may not share common user credentials. Users may have to use different user passwords for different application systems. Thus, it would be useful to synchronize all user credentials specific to application systems.

Password Synchronizer Pattern

Problem

You want to synchronize the user passwords (or user credentials used for authentication and authorization) across different application systems using a programmatic interface.

In a heterogeneous application security environment, different application systems use different user account management mechanisms. If security administrators need to modify the user account management policies or to reset user passwords for all application systems to which a user is entitled, it requires considerable administrative effort. This applies to user credentials (such as certificates, smart card tokens or even biometrics samples) used for authentication and authorization as well.

As discussed earlier, the administrative effort to reset user passwords is expensive. In earlier days, some security administrators built a proprietary programmatic interface with a text file to store the user passwords and the application system identifier. However, this is not easily scalable and maintainable for a system with a large number of user accounts and growing application systems. Additionally, it is highly insecure to store sensitive user account information in text files.

If all applications are home-grown, security architects and developers may want to standardize all user account management mechanisms and centralize the user account policy system. By standardizing and centralizing the user account policy, architects and developers can easily manage provisioning user accountsthey will not have a password synchronization issue. In reality, many organizations have a mix of home-grown applications, off-the-shelf packages, and legacy systems. Thus, this user account centralization approach would not be sufficient to process user account provisioning or password synchronization in the legacy systems without heavy customization.

Single sign-on security allows a user to sign on once and access application systems across a diverse security infrastructure using unique security assertions. However, this does not provide any mechanism to provision user accounts or to synchronize user passwords. If security administrators need to reset the user passwords for a user, they need to automate resetting user passwords in a programmatic manner instead of using a manual user password reset.

Forces
  • You want to use a programmatic interface that can work with different password administration and management mechanisms of each application system to synchronize user account passwords.

  • You want to hide the details of handling heterogeneous application protocols (for example, proprietary message format) and service configurations of multiple security service components (for example, directory server) that underlie password administration and management mechanisms.

  • You want to standardize the processing control of the return codes and error codes. Different Provisioning Service Targets (application systems) may have their own style and naming convention for the return codes and error codes. When synchronizing user passwords with multiple Provisioning Service Targets, security architects and developers may want to use a common and standard interface to encapsulate these return codes and error codes or to translate different types of codes into a common code system. For example, application A may use "20" to denote service failure due to invalid user identifier, but application B may use "30" to denote the same error. The Password Synchronizer may translate these error codes (such as "20" and "30") into its common code system (such as "40").

Solution

Use a Password Synchronizer to centralize management of synchronizing user credentials (including user passwords) across different application systems via programmatic interfaces.

A Password Synchronizer is a convenient control center for synchronizing user account services across multiple application systems. It acts like a hub that issues user account password service commands (including password setting, password resetting, and synchronizing the user passwords) to all application systems that are connected to it. Each of the application systems receives the user account password service request, verifies the authenticity, and processes the request. If the request is successful, the application system will respond with a positive return code. Otherwise, it will return a response with the details of any unsuccessful condition or failure reason.

A Password Synchronizer can manage user credential (such as user account password) activities in a programmatic manner. All user account password service requests are logged and tracked. If the target application system is unavailable, the Password Synchronizer can reissue the service request. A Password Synchronizer is extremely important when there are a large number of target application systems and administrators need to synchronize the user account passwords within a short time window. Operational efficiency and accuracy are keys to success.

To provide a flexible user account password service, architects and developers may need a number of logical architecture components, as discussed earlier in this chapter. Figure 13-8 shows a simple adaptation of the logical components as they appear in the Password Synchronizer. These logical components are depicted as shown in the figure.

Figure 13-8. Password Synchronizer components


Password Synchronizer Manager. The Password Synchronizer Manager acts as a façade that directs user account password service requests to the provisioning service targets (that is, target application systems). It performs the roles of the provisioning server and password manager in Figure 13-6.

Ledger. The Ledger logs each user account password service request. Once the service request is complete, the ledger will mark the request as successful. If the provisioning service target (target application system) is not available, the Password Synchronizer Manager will reissue the service request after the provisioning service target resumes service. It also performs the role of the monitoring component as in Figure 13-6.

PSTID-ID Mapping Table. The provisioning service target ID (PSTID) to user ID mapping table references the unique user account ID with the target application systems (provisioning service targets). This table provides information to the Password Synchronizer Manager for issuing any provisioning service requests to the target application systems.

For a large-scale deployment environment with a high volume of user account service requests, architects and developers would probably require the Password Synchronizer Manager to handle a large number of requests simultaneously using multithread processing. Typically, asynchronous messaging would be a good design approach. User account password service requests can be placed in a queue, where the Password Synchronizer Manager can create multiple threads to process these requests simultaneously.

Because each target application system may be running a different application protocol, the Password Synchronizer Manager must be flexible enough to handle multiple protocols by shielding the client tier from the underlying protocol. Doing this may require the use of connectors or adapters that can transform different underlying protocols.

Structure

Figure 13-9 shows a class diagram for the Password Synchronizer pattern. The core Password Synchronizer service consists of three important classes: PasswordSyncManager, ServiceConfig, PasswordSyncListener and PasswordSyncLedger.

Figure 13-9. Password Synchronizer class diagram


The PasswordSyncManager class is the main process engine that handles user account password requests. The user account password request is created by the class PasswordSyncRequest, which loads the user profile (for example, user name, password) via the class ProvisioningUserProfile. The PasswordSyncManager creates a secure session, connects to each provisioning service target, and issues the relevant user account password request.

The ServiceConfig class loads the PSTID mapping file, which stores a list of the provisioning service targets and the underlying application protocol (in a context object called ServiceConfigContext for each provisioning target system) used to process the user account password service requests, such as RMI-IIOP and SOAP/HTTP. This avoids tightly coupling the data transport layer processing logic with the application processing logic in the program codes.

The PSTID Mapping file defines the mapping between the unique provisioning service target ID and the user IDs in each application system. Flexibility is increased by using a unique provisioning service target ID, which may be an arbitrary, system-generated reference number that references other user IDs. Using a unique provisioning service target ID allows user IDs to be added or removed from the mapping table without those actions impacting other systems or the application infrastructure. If architects and developers use any of the existing user IDs to map to other user IDs, any change to the user IDs will break the referential integrity (or mapping relationship). In that case, the PasswordSyncManager will not be able to complete the user account password service requests.

The PasswordSyncListener class resembles the target provisioning system that receives and processes the user account service request.

The PasswordSyncLedger class denotes the system entity that checks whether all user account service requests have been completed.

Participants and Responsibilities

Figure 13-10 depicts a use case scenario for synchronizing user account passwords across multiple provisioning service targets. The ProvisioningServicePoint denotes an administrative client that initiates a password synchronization request. The PasswordSyncManager is the core engine that issues user account password service requests to all provisioning service targets. The ServiceConfig is an internal table that indicates the underlying application protocol. The PasswordSyncManager writes unsuccessful service requests to a ledger, for example, when the provisioning service targets are offline and unavailable to process the service request. The Ledger records the outstanding service requests that need to be reprocessed, for example, after the provisioning service targets resume operation. The following sequence describes the password synchronization process:

1.

ProvisioningServicePoint verifies if the client is authorized.

2.

ProvisioningServicepoint creates an instance of the Password Synchronizer.

3.

PasswordSyncManager retrieves service protocols and bindings.

4.

ServiceConfig sends service protocols and bindings to PasswordSyncManager.

5.

PasswordSyncManager creates session variables.

6.

ProvisioningServicePoint initiates request to synchronize the user account password service.

7.

PasswordSyncManager creates a handle for the password synchronization request.

8.

PasswordSyncManager adds handle to the session information.

9.

PasswordSyncManager issues an SPML add operation request to each individual ProvisioningServiceTarget.

10.

ProvisioningServiceTarget returns the result.

11.

PasswordSyncManager writes events to Ledger.

12.

PasswordSyncManager updates status by the handle ID.

13.

PasswordSyncManager returns the result to ProvisioningServicePoint.

Figure 13-10. Password Synchronizer sequence diagram


Figure 13-11 shows how the Password Synchronizer pattern can reissue or reprocess the user account password service requests until they are successfully completed. This is useful if architects and developers require the reliability and resilience of handling provisioning requests. The capability of reprocessing service requests is essential for ensuring that all user passwords are synchronized, even if some of the target systems are offline. It is also important that the Password Synchronizer have the capability to roll back to the original user account password after any unsuccessful password synchronization operation. The following sequence shows the reprocessing capability of synchronizing user account passwords.

Figure 13-11. Reprocessing user account password requests after target system resumes operation


1.

ProvisioningServicepoint creates an instance of the Password Synchronizer.

2.

PasswordSyncManager retrieves service protocols and bindings.

3.

ServiceConfig sends service protocols and bindings to PasswordSyncManager.

4.

PasswordSyncManager creates session variables.

5.

ProvisioningServicePoint initiates a request to synchronize user account password service.

6.

ProvisioningServicePoint retrieves a list of outstanding user account password service requests from Ledger.

7.

Ledger returns a list of outstanding requests.

8.

PasswordSyncManager creates a handle for the password synchronization request.

9.

PasswordSyncManager adds a handle to the session information.

10.

PasswordSyncManager issues an SPML add operation request to each individual ProvisioningServiceTarget.

11.

ProvisioningServiceTarget returns the result.

12.

PasswordSyncManager writes events to Ledger.

13.

PasswordSyncManager updates status by the handle ID.

14.

PasswordSyncManager returns result to ProvisioningServicePoint.

Strategies

A Password Synchronizer pattern provides a consistent and structured way to handle service provisioning functions and a flexible way to handle multiple protocol bindings. The following are scenarios discussing important design strategies for use with the Password Synchronizer pattern.

  • Multithreading strategy. The Password Synchronizer should be flexible enough to support sequential and simultaneous processing. Sequential processing denotes that the Password Synchronizer processes each provisioning service target one at a time in a sequential order. However, this will not be scalable if there are a large number of provisioning service targets or service requests to handle. Simultaneous processing denotes the capability to create multiple threads for service request processing. Multiple threads require complex application design when implementing the Password Synchronizer to create them and handle synchronization among them.

  • Post-synchronization event strategy. Architects and developers can invoke a script or a series of actions after processing the user account password service request. For example, the Password Synchronizer can invoke a user-defined service (for example, using EJB or a UNIX script) to notify the client that the password synchronization is unsuccessful and provide details of the problematic provisioning service target's status. This allows timely event notification or the alerting of the administrator upon completion of the service request. However, the Password Synchronizer should not be confused with a work-flow engine, which provides more flexibility of control processing.

  • Automated back-out strategy. If the provisioning service target is unable to process any user account password service request after several attempts, architects and developers can define threshold parameters such as TIMEOUT and MAX_RETRIES to determine whether they want to back out of the user account password service request for the rest of the provisioning service targets. Backing out of the service request is similar to processing a user account password request and synchronizing the user passwords across systems.

    However, backing out of the user account password service request requires retrieving or storing the previous user account password temporarily. One challenge is that the Password Synchronizer pattern needs to retrieve the current user account password from the security infrastructure. Some security infrastructures (for example, the Solaris operating environment) do not allow retrieving user account passwords in clear text; they store the user account passwords in an encrypted format. Additionally, retrieving and storing the current user account password for a back-out operation may create several security risks. For example, security administrators need to determine a secure and safe mechanism for storing the user account password temporarily (for example, in encrypted text), which hackers may be able to access.

  • Protocol binding strategy. It is possible that the administrative client may be using a mixture of protocols (SOAP, RMI) under different use case scenarios. Developers can build administrative clients for each different protocol, for example, a dedicated SOAP client for SOAP-based messaging and an EJB client for the RMI-IIOP protocol. However, it would be more desirable to separate the administrative processing logic from the underlying protocols. Doing so allows a single client to support more than one underlying protocol.

Consequences

By employing the Password Synchronizer pattern, developers can benefit in the following ways:

  • Addressing insecure data storage. The Password Synchronizer pattern uses a secure data store such as Secure LDAP to store the ID mapping table. Using a secure data store is important for addressing any security vulnerability caused by insecure data storage.

  • Addressing broken authentication. If each application system has its own user password, security hackers may easily break into an application system that uses weak user passwords. A possible risk mitigation is to synchronize the user passwords in a timely fashion across all application systems using a strong password policy. This measure can help in addressing the potential security vulnerability of broken authentication caused by weak user passwords.

  • Reusable programmatic interfaces that encapsulate different application protocols to set or reset user account passwords. The Password Synchronizer pattern uses programmatic interfaces (such as SPML) to encapsulate the interface that instructs the provisioning service targets to set or reset user account passwords. It reduces the complexity by using standard interfaces, not custom-built proprietary connectors or interfaces. The programmatic interfaces can be highly reusable for similar provisioning service targets.

  • Automated retry if the provisioning service target is offline. The Password Synchronizer pattern will retry sending the user account service requests to the provisioning service targets using a ledger after they resume operations. It ensures that all provisioning service targets are synchronized. This is an essential feature of a reliable and resilient user account provisioning service.

  • Automated back-out during password synchronization. After a number of retries (such as three times) of resending the requests to a specific provisioning service target, administrators can decide to back out of the user account password service request. It is an important design decision, because the back-out operation denotes undoing previously successful user account password service requests from a potentially large number of provisioning service targets. This may be implemented by archiving the user credential data store from each provisioning service target or by storing the current user account password securely and temporarily before executing the current service request. (Nevertheless, if the user account passwords are not securely maintained, they may be vulnerable to security exploits).

Sample Code

This section introduces sample program code for creating a Password Synchronizer to initiate user account password requests. The Password Synchronizer consists of two key components: PasswordSyncManager (administrative client that initiates a number of user password synchronization requests to the provisioning service targets) and PasswordSyncLedger (a manager component that monitors the status of the service provisioning requests from a predefined JMS topic). Each of the service provisioning requests is intercepted and processed by PasswordSyncListener, which resides in each provisioning service target. JMS is used because it provides a reliable message delivery mechanism and allows better scalability with multiple listeners processing the requests simultaneously.

PasswordSyncManager resembles a slight adaptation of the PasswordSyncManager in the Password Synchronizer Pattern section earlier in this chapter. Similarly, PasswordSyncLedger takes the role of Ledger. pstidMapping.xml is an adaptation of PSTIDMapping and is used by the methods in the class ServiceConfig.

Figure 13-12 shows the logical architecture for the sample program codes. In Step 1, PasswordSyncManager reads from the provisioning service target mapping table pstidMapping.xml and publishes user password synchronization requests to different JMS topics. It renders the service provisioning request in SPML message format if the provisioning service target supports SOAP, according to the service configuration information defined in the mapping table using the methods defined in the class ServiceConfig. Otherwise, it generates a delimited text. PasswordSyncManager uses the utility PasswordSyncRequest to transform the SOAP message (or the delimited text) to an object and writes to the JMS topic name. Currently, the JMS topic name uses the application resource name of the provisioning service target.

Figure 13-12. Sample code logical architecture


In Step 2, each provisioning service target uses a JMS listener, PasswordSyncListener. PasswordSyncListener intercepts any JMS objects published to the associated JMS topic name. Upon receipt, the listener processes the service provisioning requests in the takeAction method and notifies the PasswordSyncLedger of successfully synchronized requests.

In Step 3, PasswordSyncLedger is a Password Synchronization Manager ledger process that listens to a predefined JMS topic (such as PASSWORDSYNC_PROVIDER_OK_LIST). It keeps track of the original list of provisioning service targets (from the mapping table pstidMapping.xml). If all passwords are synchronized, then PasswordSyncLedger displays a message stating the completion of the user password synchronization requests.

The core component of the Password Synchronizer is the PasswordSyncManager. Example 13-4 shows a program excerpt for PasswordSyncManager. It uses a hash table (LinkedHashMap) to store the user password profile. Upon initialization and loading the system configuration, the PasswordSyncManager retrieves a list of applications from pstidMapping.xml using the class ServiceConfig. Then it publishes the user password synchronization requests in either SOAP or delimited text based on the service configuration information.

Example 13-4. Sample PasswordSyncManager
package com.csp.provisioning; import java.util.Date; import java.util.LinkedHashMap; public class PasswordSyncManager {     protected ProvisioningUserProfile userProfile = null;     protected String protocolBinding;     protected String topicName;     protected String fullName;     protected String firstName;     protected String lastName;     protected String emailAddress;     protected String userId;     protected String password;     protected ServiceConfig serviceConfig;     protected ServiceConfigContext context;     protected LinkedHashMap<String,ServiceConfigContext>         serviceConfigHashMap = new LinkedHashMap();     protected String timeStamp;     /** Creates a new instance of PasswordSyncManager */     public PasswordSyncManager() {         setupDefaultUserProfile();         this.serviceConfig = new ServiceConfig();         this.serviceConfigHashMap = serviceConfig.getAllConfigContext();         processPasswordSyncRequests();     }     /**      * set up default user profile for the password sync requests      *      */     private void setupDefaultUserProfile() {         // set up default user profile         String fullName = "Mary Jo Parker";         String firstName = "Mary Jo";         String lastName = "Parker";         String userId = "mjparker";         String emailAddress = "mjparker@namredips.com";         String password = "secret";         this.userProfile = new             ProvisioningUserProfile(fullName, firstName, lastName, userId,             emailAddress, password);     }     /**      * process password sync requests      *      */     private void processPasswordSyncRequests() {         this.timeStamp = new Date().toString();         for(ServiceConfigContext configContext: this.serviceConfigHashMap.values()) {             this.topicName = configContext.getTopicName();             this.protocolBinding = configContext.getProtocolBinding();             System.out.println(this.timeStamp + "- " + configContext.getApplicationId() + " is being processed under " + this.topicName + " using " + this.protocolBinding);             new PasswordSyncRequest(this.userProfile, this.topicName, this.protocolBinding);         }     }     public static void main(String args[]) {         new PasswordSyncManager();     } } 

The service configuration for the Password Synchronizer allows different data transportation protocols to be used. Example 13-5 shows a program excerpt for ServiceConfig. The program first retrieves a list of applications from pstidMapping.xml using the class ServiceConfig. The service configuration is stored in a system properties file and is used to indicate the underlying data transport protocol for the password synchronization service, for example, SOAP or JMS.

Example 13-5. Sample ServiceConfig
package com.csp.provisioning; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; public class ServiceConfig {     protected LinkedHashMap<String,ServiceConfigContext>         serviceConfigHashMap = new LinkedHashMap();     protected Document doc = null;     private static final String configFile =         "config/pstidMapping.xml";     protected String requesterId = "passwordSynManagerUser";  // default         // service requester id for audit log     protected Log log;     /** Creates a new instance of ServiceConfig */     public ServiceConfig() {         log = LogFactory.getLog(ServiceConfig.class              .getPackage().getName());         try {             if (configFile == null) {                 log.fatal("Invalid Password Synchronizer"                  + " configuration file name");             } else {                 SAXBuilder builder = new SAXBuilder(false);                 doc = builder.build(new File(this.getConfigFile()));                 initConfig(doc); // ensure we can get components config even components not initialized             }         } catch (IOException ie) {             log.fatal("ServiceConfig constructor  cannot"             + " find file/file not readable");             ie.printStackTrace();         } catch (JDOMException je) {             log.fatal("cannot parse Password Synchronizer"               + " config file");             je.printStackTrace();         }     }     /**      * Get  config file from JVM options      * if  the file does not exist, use the default one under config/config.xml      *      * @return String config file      */     private String getConfigFile() {         String localConfigFile = new String();         localConfigFile = System.getProperty("config.file");         if (localConfigFile == null) {             localConfigFile = this.configFile;             return localConfigFile;         } else {             return localConfigFile;         }     }     /**      *  Initialize configuration by loading the ulyssesConfig.xml into the      *  LinkedHashMap      *  This will include:      *  1. Load configFile      *  2. Extract global config      *  3. Extract private config for each component into UlyssesConfig      *  4. Store private config info in LinkedHashMap      *      *  @param String configFile Ulysses config file      */     private void initConfig(Document doc) {         setComponentsConfig(doc);     }     /**      * Create pstidMapping.xml from LinkedHashMap      *      * Assumption - must load pstd mapping file and create LinkedHashMap first      *      * @param Document doc      */     private synchronized void setComponentsConfig(Document doc) {         String parentElement = "service";         String applicationIdElement = "applicationId";         String applicationClassNameElement =             "applicationClassName";         String applicationURIElement = "applicationURI";         String protocolBindingElement = "protocolBinding";         String topicNameElement = "topicName";         int state = com.csp.provisioning.ServiceConfigContext.UNKNOWN_STATE;         String applicationId = new String();         String applicationClassName = new String();         String applicationURI = new String();         String protocolBinding = new String();         String topicName = new String();         //String requesterId = new String();         String requesterId = this.requesterId;         ServiceConfigContext context = null;         Element root = doc.getRootElement();         List components = root.getChildren(parentElement);         Iterator i = components.iterator();         while (i.hasNext()) {             Element component = (Element)i.next();             applicationId = component.getChild                 (applicationIdElement).getText();             applicationClassName = component.getChild                 (applicationClassNameElement).getText();             applicationURI = component.getChild                 (applicationURIElement).getText();             protocolBinding = component.getChild                 (protocolBindingElement).getText();             topicName = component.getChild                 (topicNameElement).getText();             //System.out.println("topic name = " + topicName);             context = new ServiceConfigContext(applicationId,               applicationClassName, applicationURI, protocolBinding, state,                 requesterId, topicName);             this.serviceConfigHashMap.put(applicationId,                 context);         }     }     /**      * Retrieve private config in a list      *      * @param String componentName      * @return List a list containing the private config of a Ulysses component      */     public ServiceConfigContext getContext       (String applicationId) {         ServiceConfigContext context;         if (this.serviceConfigHashMap == null) {             try {                 if (configFile == null) {                     log.fatal("Invalid  configuration "                      + "file name");                 } else {                     SAXBuilder builder =                        new SAXBuilder(false);                     Document doc = builder.build                        (new File(configFile));                     initConfig(doc); // ensure we can get components config even components not initialized                     //dumpComponentMap();                     context = this.serviceConfigHashMap.get(applicationId);                     return context;                 }             } catch (IOException ie) {                 log.fatal("ServiceConfig constructor  "                  + " cannot find file/file not readable");                 ie.printStackTrace();             } catch (JDOMException je) {                 log.fatal("cannot parse  config file");                 je.printStackTrace();             }             return null;         } else {             context = this.serviceConfigHashMap.get(applicationId);             return context;         }     }     /**      * Fetch service config context of all components      *      * @return LinkedHashMap serviceConfigHashMap      *      **/     public LinkedHashMap getAllConfigContext() {         return this.serviceConfigHashMap;     } } 

The Password Synchronizer pattern uses the SPML addRequest message to create a new user account and synchronize user passwords across application systems. Example 13-6 shows a program excerpt of PasswordSyncRequest, which creates a SPML service request. The method createSPMLRequest constructs a SOAP message encapsulating the SPML request. It can be modified to add or change user account details.

Example 13-6. PasswordSyncRequest
package com.csp.provisioning; import java.net.URL; import java.util.Hashtable; import javax.activation.DataHandler; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.xml.soap.AttachmentPart; import javax.xml.soap.MessageFactory; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPBodyElement; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; import com.sun.messaging.xml.MessageTransformer; import com.sun.messaging.TopicConnectionFactory; import com.csp.provisioning.ProvisioningUserProfile; public class PasswordSyncRequest {     protected TopicConnectionFactory          topicConnectionFactory = null;     protected TopicConnection         topicConnection         = null;     protected TopicSession            topicSession = null;     protected Topic                   topic = null;     protected TopicPublisher          topicPublisher         =  null;     protected Message                 msg = null;     protected TextMessage             textMsg = null;     protected ProvisioningUserProfile userProfile = null;     protected String protocolBinding;     protected String topicName;     protected String fullName;     protected String firstName;     protected String lastName;     protected String emailAddress;     protected String userId;     protected String password;     /** Constructor - Creates a new instance of PasswordSyncRequest      *  Default constructor to call. This default will use a default user profile Mary Jo Parker for demo.      *      */     public PasswordSyncRequest() {         // default values if not specified         this.protocolBinding = "SOAP";         this.topicName = "PROD_FINANCIAL_FRONTOFFICE";         this.fullName = "Mary Jo Parker";         this.firstName = "Mary Jo";         this.lastName = "Parker";         this.userId = "mjparker";         this.emailAddress = "mjparker@namredips.com";         this.topicName = "prod_application1";         this.password = "secret";         init(topicName);         try {             createSPMLRequest();             start();         } catch (Exception ex) {             ex.printStackTrace();         }     }     /** Constructor - Creates a new instance of PasswordSyncRequest */     public PasswordSyncRequest(ProvisioningUserProfile userProfile,         String topicName, String protocolBinding) {         this.protocolBinding = protocolBinding;         this.topicName = topicName;         this.fullName = userProfile.getFullName();         this.firstName = userProfile.getFirstName();         this.lastName = userProfile.getLastName();         this.userId = userProfile.getUserId();         this.emailAddress = userProfile.getEmailAddress();         this.password = userProfile.getToken();         init(this.topicName);         try {             createSPMLRequest();             start();         } catch (Exception ex) {             ex.printStackTrace();         }     }     /**      * Initializes JMS settings      *      * @param String topicName JMS topic name      * @exception JMSException ex    JMSException      */     private void init(String topicName) {         try {             // Create JMS topic and settings             //  Can be replaced by ServiceLocator pattern when available             topicConnectionFactory =                 new TopicConnectionFactory();             topicConnection =            topicConnectionFactory.createTopicConnection();             topicSession = topicConnection                 .createTopicSession(false, Session.AUTO_ACKNOWLEDGE);             // topic = null;             topic = topicSession.createTopic(topicName);         } catch (JMSException je) {             je.printStackTrace();             System.out.println("Cannot create topics "                + " or topic names");             System.out.println              ("Connection problem: " + je.toString());             if (topicConnection != null) {                 try {                     topicConnection.close();                 } // try                 catch (JMSException moreEx) {                     moreEx.printStackTrace();                 }             } // catch             System.exit(1);         } // catch     } // init()     /**      * Create SPML add request message in SOAP, and bind to JMS      *      *  This example uses SPML 1.0 syntax for illustration      *      * @param TopicSession session TopicSession JMS topic session      * @param Message message Message JMS message      * @param Hashtable PasswordUserProfile Hashtable user password info for      * the SPML message      * @exception JMSException ex Exception JAXM/SAAJ exception      */     private void createSPMLRequest() throws Exception {         try {             // Create a SOAP envelope             MessageFactory mf =                MessageFactory.newInstance();             SOAPMessage soapMessage = mf.createMessage();             SOAPPart soapPart = soapMessage.getSOAPPart();             SOAPEnvelope soapEnvelope =                soapPart.getEnvelope();             SOAPHeader soapHeader =                soapMessage.getSOAPHeader();             SOAPBody soapBody = soapEnvelope.getBody();             // create addRequest SPML message             Name name =        soapEnvelope.createName("addRequest", "spml",              "http://www.coresecuritypattern.com");             SOAPElement element =               soapBody.addChildElement(name);             SOAPBodyElement addRequest =               soapBody.addBodyElement(name);             Name childName =               soapEnvelope.createName("xmlns");             addRequest.addAttribute(childName,               "urn:oasis:names:tc:SPML:1:0");             childName = soapEnvelope.createName("spml");             addRequest.addAttribute(childName,                "urn:oasis:names:tc:DSML:2:0:core");             // create identifier             childName =             soapEnvelope.createName("identifier");             SOAPElement spmlIdentifier =             addRequest.addChildElement(childName);             childName = soapEnvelope.createName("type");             spmlIdentifier.addAttribute(childName,                "urn:oasis:names:tc:SPML:1:0#GUID");             // create user account id             childName = soapEnvelope.createName("id");             SOAPElement spmlID =             spmlIdentifier.addChildElement(childName);             spmlIdentifier.addTextNode(this.userId);             // create user account password             childName =              soapEnvelope.createName("attributes");             SOAPElement attributes =              addRequest.addChildElement(childName);             childName = soapEnvelope.createName("attr", "dsml",                "http://www.sun.com/imq");             childName = soapEnvelope.createName("name");             SOAPElement attr1 =             attributes.addChildElement(childName);             attributes.addAttribute(childName,                 "objectclass");             childName = soapEnvelope.createName("value");             SOAPElement attrObjclass = attr1.addChildElement(childName);             attrObjclass.addTextNode("user");             childName = soapEnvelope.createName("name");             SOAPElement attr2 =             attributes.addChildElement(childName);             attributes.addAttribute(childName,             "fullname");             childName = soapEnvelope.createName("value");             SOAPElement attrFullname = attr2.addChildElement(childName);             attrFullname.addTextNode(this.fullName);             childName = soapEnvelope.createName("name");             SOAPElement attr3 =             attributes.addChildElement(childName);             attributes.addAttribute(childName, "email");             childName = soapEnvelope.createName("value");             SOAPElement attrEmail = attr3.addChildElement(childName);             attrEmail.addTextNode(this.emailAddress);             childName = soapEnvelope.createName("name");             SOAPElement attr4 =             attributes.addChildElement(childName);             attributes.addAttribute(childName,             "password");             childName = soapEnvelope.createName("value");             SOAPElement attrPassword = attr4.addChildElement(childName);             attrPassword.addTextNode(this.password);             childName = soapEnvelope.createName("name");             SOAPElement attr5 =             attributes.addChildElement(childName);             attributes.addAttribute(childName,             "lastname");             childName = soapEnvelope.createName("value");             SOAPElement attrLastname = attr5.addChildElement(childName);             attrLastname.addTextNode(this.lastName);             childName = soapEnvelope.createName("name");             SOAPElement attr6 =             attributes.addChildElement(childName);             attributes.addAttribute(childName, "firstname");             childName = soapEnvelope.createName("value");             SOAPElement attrFirstname = attr6.addChildElement(childName);             attrFirstname.addTextNode(this.firstName);             // Attach a local file             URL url = new URL("http://localhost:8080");             DataHandler dHandler = new DataHandler(url);             AttachmentPart soapAttach =             soapMessage.createAttachmentPart(dHandler);             soapAttach.setContentType("text/html");             soapAttach.setContentId("cid-001");             //soapMessage.addAttachmentPart(soapAttach);             soapMessage.saveChanges();             // Convert SOAP message to JMS             this.msg =                 MessageTransformer.SOAPMessageIntoJMSMessage(soapMessage,                 this.topicSession);         }  // try         catch (Exception ex) {             ex.printStackTrace();             System.out.println("Exception occurred: " + ex.toString());         } // catch     }     /**      * Create a string in plain text to encapsulate password sync request      *      * @return String a string that concatenates userId, fullName,      * emailAddress, password, lastName, firstName      */     private String createPasswordRequest() {         String PasswordRequest = null;         PasswordRequest = this.userId + ":" + this.fullName + ":" +             this.emailAddress + ":" + this.password + ":" + this.lastName +             ":" + this.firstName;         return PasswordRequest;     }     /**      * Start processing SPML message, given the JMS topic name,      *    and the SPML user password info      * It is intended that the start() will start and close JMS connection.      * For better efficiency, create a batch loop to process multiple requests.      *      * @param String topicName String JMS topic name      * @param Message message   Message JMS message content      * @param Hashtable PasswordUserProfile Hashtable user password info for      * the SPML message      * @param String protocol      */     private void start() {         String PasswordRequestText = null;         try {             topicPublisher = this.topicSession.createPublisher(topic);             if (this.protocolBinding.equals("SOAP")) {                 try {                     createSPMLRequest();                     this.topicPublisher.publish(this.msg);                 } catch (Exception ex) {         System.out.println("Cannot create SOAP message");         System.out.println("Message creation error: "                                         + ex.toString());        System.exit(1);                 } // catch      } else if (this.protocolBinding.equals("JMS")) {      this.textMsg = this.topicSession.createTextMessage();                 PasswordRequestText =                 createPasswordRequest();                 this.textMsg.setText(PasswordRequestText);                 this.topicPublisher.publish(textMsg);             } else {                 System.out.println("Request protocol "           + this.protocolBinding + " is not supported");                 System.exit(1);             } // if protocol         } // try         catch (JMSException je) {             je.printStackTrace();             System.out.println("Cannot publish                                     SOAP message");             System.out.println("Exception occurred: "                                      + je.toString());         } finally {             if (topicConnection != null) {                 try {                     topicConnection.close();                 } // try topicConnection                 catch (JMSException jex) {                     jex.printStackTrace();       System.out.println("Cannot close topicConnection");       System.out.println("Connection problem: "                                      + jex.toString());                 } // catch             } // if topicConnection         } // finally     } // try     /**      * set protocol binding for the service request      *      * @param String protocolBinding      */     public void setProtocolBinding(String protocolBinding) {         this.protocolBinding = protocolBinding;     } } 

Using the JMS infrastructure, a small footprint listener program is required for each Provisioning Service Target to intercept the user password synchronization request. Example 13-7 shows a program excerpt of PasswordSyncListener. PasswordSyncListener listens to the predefined JMS topic. Once the password synchronization request is received, the listener processes the service request and notifies the PasswordSyncLedger when the service request is complete (or fails).

Example 13-7. Sample PasswordSyncListener
package com.csp.provisioning; import java.io.IOException; import java.io.InputStreamReader; import javax.jms.JMSException; import javax.jms.TopicConnection; import javax.jms.TopicSession; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicSubscriber; import javax.xml.soap.MessageFactory; import com.sun.messaging.TopicConnectionFactory; import com.sun.messaging.xml.MessageTransformer; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import javax.xml.soap.AttachmentPart; import javax.xml.soap.Name; import javax.xml.soap.Node; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPFactory; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPHeaderElement; import javax.xml.soap.SOAPMessage; import javax.xml.soap.Text; public class PasswordSyncListener implements javax.jms.MessageListener {     protected TopicConnectionFactory        topicConnectionFactory = null;     protected TopicConnection                      topicConnection = null;     protected TopicSession                       topicSession = null;     protected Topic                        topic = null;     protected TopicSubscriber                    topicSubscriber = null;     protected TextMessage                         message = null;     protected InputStreamReader                     inputStreamReader = null;     protected String              topicName = "prod_application1";     protected String        notifyTopicName = "PASSWORDSYNC_PROVIDER_OK_LIST";     protected MessageFactory                       messageFactory = null;     protected String timeStamp;     protected ServiceConfig serviceConfig;     protected ServiceConfigContext context;     protected LinkedHashMap<String,ServiceConfigContext>         serviceConfigHashMap = new LinkedHashMap();     /**      * Constructor - create new instance of Password Synchronizer listener      * This is a default constructor if no param is given at run-time      */     public PasswordSyncListener() {         serviceConfig = new ServiceConfig();         serviceConfigHashMap =            serviceConfig.getAllConfigContext();         System.out.println("PasswordSyncListener - processing password synchronization requests  from JMS topic '" + this.topicName + "'");         System.out.println("Note - completed request will be notified under the JMS topic '" + this.notifyTopicName + "'");         init();  // initialize environment         snoop(); // listen for SPML requests     }     /**      * Constructor - create new instance of Password Synchronizer listener      */     public PasswordSyncListener(String newTopicName, String newNotifyTopicName) {         serviceConfig = new ServiceConfig();         serviceConfigHashMap =            serviceConfig.getAllConfigContext();         this.topicName = newTopicName;         this.notifyTopicName = newNotifyTopicName;         System.out.println("PasswordSyncListener - processing password synchronization requests from JMS topic '" + this.topicName + "'");         System.out.println("Note - completed request will be notified under the JMS topic '" + this.notifyTopicName + "'");         init();         snoop();     }  /*   * Initializes the JMS settings   * @exception ex  Exception   */     public void init() {         // for future enhancement,         // use serviceLocator pattern here         try {             this.messageFactory =                MessageFactory.newInstance();             this.topicConnectionFactory                 = new com.sun.messaging.TopicConnectionFactory();             this.topicConnection                 = this.topicConnectionFactory.createTopicConnection();             this.topicSession                 = this.topicConnection.createTopicSession(false,             Session.AUTO_ACKNOWLEDGE);             this.topic                 = this.topicSession.createTopic(this.topicName);         } // try         catch (Exception ex) {             ex.printStackTrace();             System.out.println("Cannot create                                 topics or topic names");             System.out.println("Connection problem: "                                      + ex.toString());             if (topicConnection != null) {                 try {                     topicConnection.close();                 } catch (JMSException moreEx) {                     moreEx.printStackTrace();                 }             } // if topicConnection             System.exit(1);         } // catch     } // init()  /*   * Displays SOAP header   * @param header SOAP header   * @exception ex Exception   */     private void dumpHeaderContents(SOAPHeader header) {         try {             Iterator allHeaders                 = header.examineAllHeaderElements();             while (allHeaders.hasNext()) {                 SOAPHeaderElement headerElement =                         (SOAPHeaderElement)                          allHeaders.next();                 Name headerName =                    headerElement.getElementName();                 System.out.print("<" +                      headerName.getQualifiedName() + ">");                 System.out.print("actor='"                 + headerElement.getActor() + "' ");                 System.out.print("mustUnderstand='" +                headerElement.getMustUnderstand() + "' ");                 System.out.println("</" +                  headerName.getQualifiedName() + ">");             } // while addHeaders.hasNext         } catch (Exception ex) {             ex.printStackTrace();         } // catch     } // dumpHeaderContents  /*   * Retrieves SOAP message contents, and displays   *   in indented XML format   *   * @param iterator   Iterator for the SOAP message node   * @param indent     indent space for displaying   *  XML messages on screen   */     private void getContents(Iterator iterator,         String indent) {         while (iterator.hasNext()) {             Node node = (Node) iterator.next();             SOAPElement element = null;             Text text = null;             if (node instanceof SOAPElement) {                 element = (SOAPElement)node;                 Name name = element.getElementName();                 System.out.print(indent + "<" + name.getQualifiedName());                 Iterator attrs =                     element.getAllAttributes();                 while (attrs.hasNext()){                     Name attrName = (Name)attrs.next();                     System.out.print(" "                       + attrName.getQualifiedName() + "='" +                       element.getAttributeValue(attrName) + "'");                 } // while attrs.hasNext                 System.out.println(">");                 Iterator iter2                   = element.getChildElements();                 getContents(iter2, indent + " ");                 System.out.println(indent + "</"                      + name.getQualifiedName() + ">");             } // if node instanceof             else {                 text = (Text) node;                 String content = text.getValue();                 System.out.println(indent + " "                     + content);             } // else         } // while     } // getContents  /*   * Processes each JMS message when received   * from the JMS topic   * @param message   JMS message in SOAP format   * @exception ex    Exception   */     public void onMessage(Message message) {         try {             this.timeStamp = new Date().toString();             MessageFactory messageFactory =                  MessageFactory.newInstance();             // Should invoke other Web services messages             //  to unmarshall encrypted SOAP messages,             SOAPMessage soapMessage =                     MessageTransformer .SOAPMessageFromJMSMessage( message, messageFactory );             System.out.println(timeStamp + "- Message received! Converting the JMS message to SOAP message...");             SOAPFactory soapFactory =                SOAPFactory.newInstance();             SOAPHeader thisSoapHeader =                soapMessage.getSOAPHeader();             dumpHeaderContents(thisSoapHeader);             SOAPBody thisSoapBody =                 soapMessage.getSOAPBody();             Iterator soapContent =                 thisSoapBody.getChildElements();             System.out.println();             System.out.println(timeStamp + "- Rendering SOAP Message Content");             getContents(soapContent, "");             System.out.println("Attachment counts: " + soapMessage.countAttachments());             Iterator iterator =                soapMessage.getAttachments();             while ( iterator.hasNext() ) {                 AttachmentPart soapAttach =                  (AttachmentPart) iterator.next();                 String contentType =                   soapAttach.getContentType();                 String contentId = soapAttach.getContentId();                 if ( contentType.indexOf("text") >=0 ) {                     String content = (String)                            soapAttach.getContent();                 } // if contentType             } // while             // take action to notify Password Synchroniza //tion Manager about OK status             TakeAction action = new TakeAction();             action.init(notifyTopicName);             String tempTopicName =                 findApplicationId(topicName);             if (tempTopicName != null) {                 action .publishPasswordSyncResult(tempTopicName, "SOAP");             } else {               System.out.println("ERROR - Mismatch between applicationId and topicName. Please check pstidMapping.xml");             }         } // try         catch (Exception ex) {             try {                 TextMessage textMessage = (TextMessage) message;                 String text = textMessage.getText();                 System.out.println(timeStamp + "- Password sync request in delimited text: " + text);                 // take action to notify Password Synchronization Manager about OK status                 TakeAction action = new TakeAction();                 action.init(notifyTopicName);                 String tempTopicName =                    findApplicationId(topicName);                 if (tempTopicName != null) {                     action.publishPasswordSyncResult(tempTopicName, "JMS");                 } else {   System.out.println("ERROR - Mismatch between       applicationId and topicName.              Please check pstidMapping.xml");                 }             } catch (Exception anotherEx) {                 anotherEx.printStackTrace();             }         } // catch     }  /*   * Starts listening to the JMS topic   * @exception ex  JMSException   */     private void snoop() {         char           answer = '\0';         final boolean  NOLOCAL = true;         try {             topicSubscriber =                topicSession.createSubscriber(topic,                                    null, NOLOCAL);             topicSession.createSubscriber(topic);             topicSubscriber.setMessageListener(this);             topicConnection.start();             System.out.println("Command Option :                      Q=quit, then <return>");             System.out.println();             inputStreamReader = new                InputStreamReader(System.in);             while (!((answer == 'q') || (answer == 'Q'))) {                 try {                     answer = (char)                       inputStreamReader.read();                 } catch (IOException e) {                     System.out.println("I/O exception: "                             + e.toString());                 } // catch             } // while !answer         } // try         catch (JMSException ex) {             System.out.println("Cannot                               subscribe message");             System.out.println("Exception occurred: "                                 + ex.toString());             System.exit(1);         } finally {             if (topicConnection != null) {                 try {                     topicConnection.close();                 } catch (JMSException ex) {                     System.out.println("Cannot                           close topicConnection");                     System.out.println("Connection                            problem: " + ex.toString());                     System.exit(1);                 }             }  // if topicConnection         }// finally     }     /**      * Helper class to look up the applicationId      * when given a topicName      *      * @param String targetTopicName      * @return String applicationId      */     private String findApplicationId(String         targetTopicName) {         for(ServiceConfigContext configContext: this.serviceConfigHashMap.values()) {             if (configContext.getTopicName() .equals(targetTopicName)) {                 return configContext.getApplicationId();             }         }         return null;     }     public static void main(String[] args) {         String newTopicName = new String();         String newNotifyTopicName = new String();         // Command syntax helper         if (args.length != 2) {             // take default topic name and notify topic             //name if no param is given at runtime             new PasswordSyncListener();         } else {             newTopicName = args[0];             newNotifyTopicName = args[1];             new PasswordSyncListener(newTopicName,               newNotifyTopicName);         }     } // public main } 

The listener (PasswordSyncListener) of each Provisioning Service Target uses a class called TakeAction to implement how the Provisioning Service Target should handle the user password synchronization request. This may include resetting the user password and notifying the service requester when completed. Example 13-8 shows an implementation of a simple notification action. The class TakeAction can be expanded and modified to include additional processes in the future.

Example 13-8. Sample TakeAction
package com.csp.provisioning; import javax.jms.JMSException; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; import javax.jms.TopicPublisher; import javax.jms.TopicSession; public class TakeAction {     protected TopicConnectionFactory  topicConnectionFactory = null;     protected TopicConnection         topicConnection = null;     protected TopicSession            topicSession = null;     protected Topic                   topic = null;     protected String                  topicName = null;     /** Constructor - Creates a new instance of TakeAction */     public TakeAction() {     }     /*      * Set up the JMS topic connection      *      * @param String topicName String JMS topic name      * @exception Exception ex      */     public  void init(String topicName) {         try {             this.topicConnectionFactory =                 new com.sun.messaging.TopicConnectionFactory();             this.topicConnection =                 this.topicConnectionFactory.createTopicConnection();             this.topicSession =                 this.topicConnection.createTopicSession(false,                         Session.AUTO_ACKNOWLEDGE);             this.topic = this.topicSession.createTopic(topicName);         } // try         catch (Exception ex) {             ex.printStackTrace();             System.out.println("Cannot create topics or                                          topic names");             System.out.println("Connection problem: "                                       + ex.toString());             if (this.topicConnection != null) {                 try {                     this.topicConnection.close();                 } catch (JMSException moreEx) {                     moreEx.printStackTrace();                 }                 System.exit(1);             } // if topicConnection         } // catch     }  // init    /*     * Publish successfully completed application     *    list to a pre-defined topic.     *    The structure of the message is simply <application>     *     * @param String application application name where     * password is synch     * @param String protocol either SOAP or JMS     * @exception JMSException ex     */     public void publishPasswordSyncResult(String application, String        protocol) {         TextMessage    sentMessage = null;         TopicPublisher topicPublisher =  null;         try {             topicPublisher = this.topicSession.createPublisher(topic);             sentMessage = this.topicSession.createTextMessage();             sentMessage.setText(application);             topicPublisher.publish(sentMessage);         } // try         catch (JMSException jmsex) {             jmsex.printStackTrace();             System.out.println("Cannot publish SOAP message");             System.out.println("Exception occurred: " + jmsex.toString());         } // catch         finally {             if (topicConnection != null) {                 try {                     this.topicConnection.close();                 } catch (JMSException jmsex) {                     jmsex.printStackTrace();                     System.out.println("Cannot close topicConnection");                     System.out.println("Connection problem: " + jmsex.toString());                 } // catch             } // if topicConnection         } // finally     } } 

A ledger (PasswordSyncLedger) is required to track the status of user password synchronization requests. Example 13-9 shows a program excerpt of using Java Message Service to implement the ledger.

Example 13-9. Sample PasswordSyncLedger
package com.csp.provisioning; import java.io.IOException; import java.io.InputStreamReader; import java.util.LinkedHashMap; import javax.jms.JMSException; import javax.jms.MessageListener; import javax.jms.TopicConnection; import javax.jms.TopicSession; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicSubscriber; import javax.xml.soap.MessageFactory; import com.sun.messaging.TopicConnectionFactory; import java.util.Date; public class PasswordSyncLedger implements MessageListener {     protected TopicConnectionFactory  topicConnectionFactory;     protected TopicConnection         topicConnection;     protected TopicSession            topicSession;     protected Topic                   topic;     protected TopicSubscriber         topicSubscriber;     protected TextMessage             inMessage;     protected TextMessage             message;     protected Message                 receivedMessage;     protected InputStreamReader       inputStreamReader;     protected String                  topicName;     protected char                    answer = '\0';     protected final boolean           NOLOCAL = true;     protected ServiceConfig serviceConfig;     protected ServiceConfigContext context;     protected LinkedHashMap<String,ServiceConfigContext>         serviceConfigHashMap = new LinkedHashMap();     protected MessageFactory messageFactory = null;     protected String timeStamp;     /** Creates a new instance of PasswordSyncLedger */     public PasswordSyncLedger()  {         // set default topic name         this.topicName = "PASSWORDSYNC_PROVIDER_OK_LIST";         // load Password Synchronizer config file         serviceConfig = new ServiceConfig();         serviceConfigHashMap =             serviceConfig.getAllConfigContext();         System.out.println("Password Synchronizer Ledger starts.");         // set up JMS connection factory         init(this.topicName);         start();     }    /* Set up JMS topic connection, and     *    initialize the JMS set-up     *     * @exception ex Exception     */     public void init(String topicName) {         try {             this.topicConnectionFactory =                 new com.sun.messaging.TopicConnectionFactory();             this.topicConnection =                 this.topicConnectionFactory.createTopicConnection();             this.topicSession =                this.topicConnection.createTopicSession(false,                 Session.AUTO_ACKNOWLEDGE);             this.topic = this.topicSession.createTopic(topicName);             messageFactory = MessageFactory.newInstance();         } // try         catch (Exception ex) {             ex.printStackTrace();             System.out.println("Cannot create topics or topic names");             System.out.println("Connection problem: " + ex.toString());             if (topicConnection != null) {                 try {                     topicConnection.close();                 } catch (JMSException moreEx) {                     //                 } // catch             } // if topicConnection             System.exit(1);         } // catch     }    /*     * Process each message received     *    from the listener.     *     * @exception ex JMSException     */     public void onMessage(Message message) {         int foundAny = -1;         try {             TextMessage textMessage = (TextMessage) message;             String syncResult = textMessage.getText();             // assume the application is synchronized             this.timeStamp = new Date().toString();             System.out.println(this.timeStamp + "- just complete password synchronization for '" + syncResult + "'");             if (this.serviceConfig.getContext(syncResult).getApplicationId().equals (syncResult)) {                 // set state to SYNC_STATE                this.serviceConfig.getContext(syncResult).setState (ServiceConfigContext.SYNC_STATE);                viewResult();             }         } // try         catch (JMSException jmsex) {             jmsex.printStackTrace();         }  // catch     }    /*     * Start listening to the topic (passed in args[0])     *    under a loop. It will only stop when users press Q     *     * @exception ex JMSException     */     public void start() {         try {            topicSubscriber = this.topicSession.createSubscriber(topic, null, NOLOCAL);             this.topicSession.createSubscriber(this.topic);             this.topicSubscriber.setMessageListener(this);             this.topicConnection.start();             System.out.println("Command Option : Q=quit, then <return>");             inputStreamReader = new               InputStreamReader(System.in);             while (!((answer == 'q') || (answer == 'Q'))) {                 try {                     answer = (char) inputStreamReader.read();                 } catch (IOException e) {                     e.printStackTrace();                     System.out.println("I/O exception: " + e.toString());                 } // catch             } // while         } // try         catch (JMSException ex) {             ex.printStackTrace();             System.out.println("Cannot subscribe message");             System.out.println("Exception occurred: " + ex.toString());         } finally {             if (topicConnection != null) {                 try {                     topicConnection.close();                 } catch (JMSException ex) {                     ex.printStackTrace();                     System.out.println("Cannot close topicConnection");                     System.out.println("Connection problem: " + ex.toString());                 } // catch             }  // if topicConnection         }// finally     }     /**      * verify whether all provisioning target systems are synchronized      */     private void viewResult() {         int totalSync = 0;         for(ServiceConfigContext configContext: this.serviceConfigHashMap.values()) {            if (configContext.getState() != (configContext.SYNC_STATE)) {               totalSync++;            }         }         this.timeStamp = new Date().toString();         if (totalSync == 0) {             System.out.println(this.timeStamp + "- Notification - all passwords are synchronized in all systems.");             System.out.println("Password Synchronizer Ledger is stopped.");             System.exit(0);         }         else {             System.out.println(this.timeStamp + "- " + totalSync + " applications need to be synchronized.");         }     }     public static void main(String args[]) {         new PasswordSyncLedger();     } } 

Example 13-10 shows the screen display messages when invoking PasswordSyncManager. The system properties file specifies there are four password synchronization requests. There are three Java Message Service topics (prod_application1, prod_application2, prod_application3 and prod_application4) defined.

Example 13-10. Screen display message from PasswordSyncManager
circinus:~/work> java cp ./PasswordSync_Lib.jar;./PasswordSync.jar com. csp.provisioning.PasswordSyncManager Password synchronization requests start. Thu Jun 02 07:54:07 PDT 2005- application1 is being processed under prod_application1 using SOAP Thu Jun 02 07:54:07 PDT 2005- application2 is being processed under prod_application2 using SOAP Thu Jun 02 07:54:07 PDT 2005- application3 is being processed under prod_application3 using JMS Thu Jun 02 07:54:07 PDT 2005- application4 is being processed under prod_application4 using JMS Password synchronization requests completed. 

Example 13-11 shows the screen display messages from a local instance of PasswordSyncListener. In this example, PasswordSyncListener subscribes to the Java Message Service topic "prod_application1," which corresponds to a specific Provisioning Service Target. Upon receipt of the SPML service request, PasswordSyncListener will display the content of the SOAP message on the screen.

Example 13-11. Screen display messages from PasswordSyncListener
circinus:~/work> java -cp ./PasswordSync_Lib.jar;./PasswordSync.jar com. csp.provisioning.PasswordSyncListener prod_application1 PASSWORDSYNC_PROVIDER_OK_LIST PasswordSyncListener - processing password synchronization requests from JMS topic 'prod_application1' Note - completed request will be notified under the JMS topic 'PASSWORDSYNC_PROVIDER_OK_LIST' Command Option : Q=quit, then <return> Thu Jun 02 07:51:18 PDT 2005- Message received! Converting the JMS message to SOAP message... Thu Jun 02 07:51:18 PDT 2005- Rendering SOAP Message Content <spml:addRequest> </spml:addRequest> <spml:addRequest spml='urn:oasis:names:tc:DSML:2:0:core' xmlns='urn:oasis:names: tc:SPML:1:0'>  <identifier type='urn:oasis:names:tc:SPML:1:0#GUID'>   <id>   </id>    mjparker  </identifier>  <attributes name='firstname'>   <name> ...   </spml:addRequest> Attachment counts: 1 

Example 13-12 shows the notification messages from PasswordSyncLedger. This sample program excerpt acts as a console that shows the total number of Provisioning Service Targets whose passwords have been synchronized.

Example 13-12. Notification messages from PasswordSyncLedger
circinus:~/work> java cp ./PasswordSync_Lib.jar;./PasswordSync.jar com. csp.provisioning.PasswordSyncLedger Password Synchronizer Ledger starts. Command Option : Q=quit, then <return> Thu Jun 02 07:51:19 PDT 2005- just complete password synchronization for 'application1' Thu Jun 02 07:51:19 PDT 2005- 3 applications need to be synchronized. Thu Jun 02 07:51:23 PDT 2005- just complete password synchronization for 'application2' Thu Jun 02 07:51:23 PDT 2005- 2 applications need to be synchronized. Thu Jun 02 07:51:25 PDT 2005- just complete password synchronization for 'application3' Thu Jun 02 07:51:25 PDT 2005- 1 application needs to be synchronized. Thu Jun 02 07:51:27 PDT 2005- just complete password synchronization for 'application4' Thu Jun 02 07:51:27 PDT 2005- Notification - all passwords are synchronized in all systems. Password Synchronizer Ledger is stopped. 

Security Factors and Risks
  • Infrastructure of the provisioning server (or the Password Synchronizer Manager). If the provisioning server or the Password Synchronizer Manager resides outside the demilitarized zone (DMZ), there is a higher risk of being attacked by unauthorized public intruders or internal hackers. Access to sensitive back-end processes such as password management and synchronization processing is more restrictive in the management LAN or behind the DMZ.

  • Infrastructure of provisioning service targets. Interfacing with external provisioning service targets may impose high security risks depending on whether there is a strong trust relationship between the provisioning server and the provisioning service targets. Direct interface between external hosts and the provisioning server, if residing behind the DMZ or in the management LAN, is highly risky, because it opens itself to host scanning and unauthorized footprinting. A direct programmatic interface can also expose host information or infrastructure details to potential hackers or intruders via host scanning. To mitigate the risks, security architects can allow only the delegated administration function (the Password Synchronizer Manager), upon successful authentication and authorization, to initiate user account password service requests. The Password Synchronizer Manager should disallow any application system (service target) to initiate user account password service requests.

  • Client device interface. If the client device (for example, user password token, mobile personal digital organizer, and so forth) is compromised, intruders or hackers may be able to exploit the current client device interface to access the provisioning server or the Password Synchronizer Manager. Security architects may review and assess the strength of a client device when securing the user account passwordthat is, how the client device stores the user credentials and initiates the interface to connect to the provisioning server (or the Password Synchronizer Manager).

  • Logging. The log files for user account provisioning or user account password synchronization may contain sensitive user account information, which may be a target for host scanning or hacking. Security architects may want to segregate the provisioning event log from the normal system log and ensure the log files are only accessible to the administrative user account (for example, file attribute 700 on UNIX), or store the log events in a database or directory server (which has additional security protection of the logging data). The Secure Logger pattern would be useful in this context. These security measures can reduce the risk of unauthorized access and potential security intrusion.

  • Processing logic of user account password changes. The processing logic of creating or changing a user account password is usually customized in local provisioning service targets. This is typically implemented by reusing existing APIs or the delegated administration interface. The security interface to initiating a user account password change may incur security risks. Security architects need to understand the legacy security integration requirements, such as how the underlying interface connects to the application system and how the service requester is authenticated. It is possible that the provisioning service target exposes an API to handle the processing logic of creating or changing a user account password, but it does not authenticate the service requester. In such a case, security architects need to provide a custom authentication module that can mitigate the security risk of a legacy security environment. This is usually done on a case-by-case basis, and it is difficult to prescribe a specific security protection mechanism.

  • Integration with legacy environment. Password Synchronizer (or the provisioning server) may have requirements for integrating with a legacy operating environment. These may include propagating security credentials in order to access systems in the legacy operating environment. The existing legacy operating environment may not have sophisticated or sufficient security protection. For example, it may not encrypt the data communication channel with external trading partners. Thus, the legacy operating environment will become a hacking target and will be exploited. Security architects may need to harden the legacy operating environment and allow only specific actions that can be performed by the Password Synchronizer (or provisioning server).

  • Protection for SOAP messaging. If the programmatic interface for synchronizing user account passwords uses SOAP messaging, security architects may want to ensure that the SOAP message containing the user account password request is securely protected with a digital signature or encryption. XML encryption and XML digital signature would be essential to secure the SOAP messages.

  • Identity management strategy. A sufficiently secure connection mechanism between the Password Synchronizer server (or the provisioning server) and the provisioning service targets is imperative. The trust relationship established determines whether the provisioning service target (application system) should accept the user account password service requests from the service requester. An insufficient authentication or authorization mechanism between the server and the provisioning service targets may expose a high security risk of unauthorized access. Security architects may adopt an identity management strategy that authenticates the service requester with an identity provider (as discussed in Chapters 7 and 12) that uses a stronger authentication mechanism for user credentials. This can mitigate the security risks associated with user account provisioning services.

  • Single Sign-on and Sign-out. If security architects use a Point-to-Point synchronous communication to connect the Password Synchronizer server to the provisioning service targets while synchronizing user account passwords, the Password Synchronizer will need to perform a sign-on and maintain a secure session with each system. In such a case, a single sign-on and single sign-out process control is essential. Security architects would need to consider any potential security risks that might allow the session information to be tampered with, or any hanging session that can be exploited. The Single Sign-on Delegator pattern discussed in Chapter 12 will be useful then. On the other hand, if the Password Synchronizer is implemented using asynchronous messaging, there is no need to sign on the provisioning service targets and maintain session information.

Reality Check

Should you build Password Synchronizer code from scratch? A few service provisioning vendor products (including OpenSPML initiatives) have an out-of-the-box service provisioning capability. These products provide some basic service provisioning application infrastructure such as error handling and logging. It may not be practical to build the password synchronization functionality from scratch.

Related Patterns

There are other design patterns that are related to the Password Synchronizer pattern. These include:

Single Sign-on Delegator. Single Sign-on Delegator provides a delegate design approach to connecting to remote security service providers. Using Password Synchronizer with Single Sign-on Delegator, architects and developers do not need to sign on to each provisioning service target individually in order to initiate user account password service requests.

Business Delegate. Business Delegate [CJP2] is a Business tier J2EE pattern that encapsulates access to a remote business service. Password Synchronizer shares some similarities with Business Delegate in encapsulating the complexity of invoking remote business services, but the former is specialized in dealing with user account password service requests using standards-based programmatic interfaces.




Core Security Patterns. Best Practices and Strategies for J2EE, Web Services, and Identity Management
Core Security Patterns: Best Practices and Strategies for J2EE, Web Services, and Identity Management
ISBN: 0131463071
EAN: 2147483647
Year: 2005
Pages: 204

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net