/*******************************************************************************
$Source: /cvs/repositories/openii3/project/java/examples/org/any_openeai_enterprise/gateways/directoryservice/PasswordSyncCommand.java,v $
$Revision: 1.4 $
*******************************************************************************/
/**********************************************************************
This file is part of the OpenEAI sample, reference implementation,
and deployment management suite created by Tod Jackson
(tod@openeai.org) and Steve Wheat (steve@openeai.org) at
the University of Illinois Urbana-Champaign.
Copyright (C) 2004 The OpenEAI Software Foundation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For specific licensing details and examples of how this software
can be used to implement integrations for your enterprise, visit
http://www.OpenEai.org/licensing.
*/
package org.any_openeai_enterprise.gateways.directoryservice;
// Core Java
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import org.any_openeai_enterprise.moa.jmsobjects.coreapplication.v1_0.EnterpriseUser;
import org.any_openeai_enterprise.moa.jmsobjects.coreapplication.v1_0.EnterpriseUserPassword;
import org.any_openeai_enterprise.moa.jmsobjects.coreapplication.v1_0.NetId;
import org.any_openeai_enterprise.moa.objects.resources.v1_0.LightweightPerson;
import org.jdom.Document;
import org.jdom.Element;
import org.openeai.config.CommandConfig;
import org.openeai.config.EnterpriseConfigurationObjectException;
import org.openeai.layouts.EnterpriseLayoutException;
import org.openeai.moa.objects.resources.Error;
import sun.misc.BASE64Encoder;
/**
* This command handles EnterpriseUserPassword.Create-Sync messages and
* EnterpriseUserPassword.Update-Sync message toapply password changes to an
* LDAP directory server.
* <P>
*
*/
public class PasswordSyncCommand extends DirectoryServiceBase {
/**
* Constructor
*/
public PasswordSyncCommand(CommandConfig cConfig) throws InstantiationException {
super(cConfig);
// Get the enterpriseIdDomain property.
String enterpriseIdDomain = getProperties().getProperty("enterpriseIdDomain", null);
setEnterpriseIdDomain(enterpriseIdDomain.trim());
if (enterpriseIdDomain == null) {
// The enterpriseIdDomain is null, log it an throw an exception.
String errMsg = "Missing 'enterpriseIdDomain' property in the " +
"deployment descriptor. Can't continue.";
logger.fatal("[" + getServiceName() + "] " + errMsg);
throw new InstantiationException(errMsg);
}
logger.info("[" + getServiceName() + "] " + "enterpriseIdDomain is: " +
getEnterpriseIdDomain() + ".");
// Set the createMissingUsers property.
if (getProperties().getProperty("createMissingUsers", "false").equalsIgnoreCase("true")) {
setCreateMissingUsers(true);
}
// Verify that all message objects required are in AppConfig.
// Get a configured NetId object out of AppConfig.
try {
NetId netId = (NetId)getAppConfig().getObject("NetId");
EnterpriseUser entUser = (EnterpriseUser)getAppConfig()
.getObject("EnterpriseUser");
EnterpriseUserPassword entUserPassword =
(EnterpriseUserPassword)getAppConfig()
.getObject("EnterpriseUserPassword");
}
catch (EnterpriseConfigurationObjectException eoce) {
// An error occurred retrieving a required object from AppConfig. Log it
// and throw an exception.
String errMsg = "An error occurred retrieving a required object from " +
"AppConfig. The exception is: " + eoce.getMessage();
logger.fatal("[" + getServiceName() + "] " + errMsg);
throw new InstantiationException(errMsg);
}
// Get an initial context to the directory server.
try {
DirContext dirCtx = getInitialContext(getProperties());
setDirContext(dirCtx);
}
catch (NamingException ne) {
String errMsg = "Error creating initial context. The exception is: " +
ne.getMessage();
logger.fatal("[" + getServiceName() + "] " + errMsg);
throw new InstantiationException(errMsg);
}
logger.info("[" + getServiceName() + "] instantiated successfully.");
}
public void execute(int messageNumber, Message aMessage) {
// Create the input document from the JMS message passed in.
Document inDoc = null;
try { inDoc = initializeInput(messageNumber, aMessage); }
catch (JMSException jmse) {
// An error occurred creating the input document from the JMS message
// passed in. Log it and publish a sync error.
Error error = new Error();
error.setErrorNumber("OpenEAI-2101");
error.setErrorDescription("An error occurred creating the input " +
"document from the JMS message passed in. The exception is: " +
jmse.getMessage());
logger.fatal("[" + getServiceName() + ".execute]" +
error.getErrorDescription());
return;
}
// Get the message metadata.
eControlArea = getControlArea(inDoc.getRootElement());
String messageCategory = eControlArea.getAttribute("messageCategory")
.getValue();
String messageObject = eControlArea.getAttribute("messageObject")
.getValue();
String messageRelease = eControlArea.getAttribute("messageRelease")
.getValue();
String messageAction = eControlArea.getAttribute("messageAction")
.getValue();
String messageType = eControlArea.getAttribute("messageType").getValue();
logger.info("[" + getServiceName() + ".execute] Received a(n) " + messageCategory
+ "." + messageObject + "." + messageAction + "-" + messageType +
" (release " + messageRelease + ") message.");
// Verify that this is a message we support.
ArrayList errors = new ArrayList();
if (messageObject.equalsIgnoreCase("EnterpriseUserPassword") == false) {
Error error = new Error();
error.setType("application");
error.setErrorNumber("OpenEAI-1002");
error.setErrorDescription("Message object '" + messageObject +
"' is not supported.");
errors.add(error);
}
if (messageType.equalsIgnoreCase("Sync") == false) {
Error error = new Error();
error.setType("application");
error.setErrorNumber("OpenEAI-1002");
error.setErrorDescription("Message type '" + messageType + "' is not " +
"supported.");
errors.add(errors);
}
if (errors.size() > 0) {
ListIterator iterator = errors.listIterator();
while (iterator.hasNext()) {
Error error = (Error)iterator.next();
logger.fatal("[" + getServiceName() + ".execute] " +
error.getErrorDescription());
}
publishSyncError(eControlArea, errors);
return;
}
// Get two configured EnterpriseUserPassword from AppConfig.
EnterpriseUserPassword currentEntUserPassword = null;
EnterpriseUserPassword newEntUserPassword = null;
try {
currentEntUserPassword = (EnterpriseUserPassword)getAppConfig()
.getObject("EnterpriseUserPassword");
newEntUserPassword = (EnterpriseUserPassword)getAppConfig()
.getObject("EnterpriseUserPassword");
}
catch (EnterpriseConfigurationObjectException ecoe) {
// An error occurred getting an object from AppConfig. Log it and
// publish a sync error message.
Error error = new Error();
error.setType("application");
error.setErrorNumber("OpenEAI-3001");
error.setErrorDescription("An error occurred getting an object from " +
"AppConfig. the exception is: " + ecoe.getMessage());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ecoe);
return;
}
// Handle an EnterpriseUserPassword.Create-Sync
if (messageAction.equalsIgnoreCase("Create")) {
logger.info("[" + getServiceName() + ".execute] Handling an " +
"EnterpriseUserPassword.Create-Sync message.");
// Get the new state of the EnterpriseUserPassword and build an
// EnterpriseUserPassword object.
Element eNewPassword = inDoc.getRootElement().getChild("DataArea")
.getChild("NewData").getChild("EnterpriseUserPassword");
try { newEntUserPassword.buildObjectFromInput(eNewPassword); }
catch (EnterpriseLayoutException ele) {
// An error occurred building the EnterpriseUserPassword object from the
// EnterpriseUserPassword element contained in the NewData element of
// the message. Log it and publish a sync error message.
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-1002");
error.setErrorDescription("An error occurred building the " +
"EnterpriseUserPassword object from the EnterpriseUserPassword " +
"element contained in the NewData element of the message. The " +
"exception is: " + ele.getMessage());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ele);
return;
}
// Determine whether the user exists is the directory server.
LightweightPerson lPerson = newEntUserPassword.getEnterpriseUser()
.getLightweightPerson();
String userName = "";
if (lPerson.getName() != null) userName = lPerson.getName().getFirstName()
+ " " + lPerson.getName().getLastName();
logger.debug("[" + getServiceName() + ".execute] Querying the directory to " +
"determine if the user whose password has changed already exists " +
"in the directory.");
// Specify the search filter for the directory service query using
// the employeeNumber.
String filter = "employeeNumber=" + lPerson.getInstitutionalId();
logger.debug("[" + getServiceName() + ".execute] Search filter is: " + filter);
// Specify the providerUrl.
String providerUrl = getUserDirectoryTreeBase();
logger.debug("[" + getServiceName() + ".execute] providerUrl is: " +
providerUrl);
// Specify search controls that set the scope of the search.
SearchControls cons = new SearchControls();
cons.setSearchScope(SearchControls.ONELEVEL_SCOPE);
String[] attrs = new String[1];
attrs[0] = "uid";
cons.setReturningAttributes(attrs);
// Search for user entries that match the employeeNumber, retrieving
// the uid.
NamingEnumeration results = null;
boolean userExists = false;
try {
logger.debug("[" + getServiceName() + ".execute] Querying the directory " +
"server.");
results = getDirContext().search(providerUrl, filter, cons);
if (results != null && results.hasMore() == true) userExists = true;
}
catch (NamingException ne) {
// An error occurred querying the directory server to determine whether
// the user exists. Log it, publich a error message, and return.
String errMsg = "An error occurred querying the directory server to " +
"determine whether the user exists.";
logger.debug("[" + getServiceName() + ".execute] " + errMsg);
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-2002");
error.setErrorDescription(errMsg);
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ne);
return;
}
// If there are no matching entries, the user does not exist. If the
// createMissingUsers property is true, create the user with attribute
// and password indicated in the new data of the message. If the
// createMissingUsers property is false, publish a Sync.Error-Sync
// indicating that the password cannot be set for this user, because
// the user does not exist.
if (userExists == false) {
logger.info("[" + getServiceName() + ".execute] The user " + userName +
"(" + lPerson.getInstitutionalId() +
") does not exist in the directory.");
if (getCreateMissingUsers() == true) {
// Create the user.
// -- Build the attributes for the new directory entry.
BasicAttributes attributes = buildDirectoryUser(newEntUserPassword);
// -- Create the new subcontext for the entry.
String dn = "uid=" + getEnterpriseId(newEntUserPassword
.getEnterpriseUser()).getPrincipal() + "," +
getUserDirectoryTreeBase();
logger.debug("[" + getServiceName() + ".execute] dn for the new " +
"directory context is: " + dn);
try {
logger.debug("[" + getServiceName() + ".execute] Creating directory " +
"user with attributes: " + attributes.toString());
Context result = getDirContext().createSubcontext(dn, attributes);
logger.info("[" + getServiceName() + ".execute] Created directory " +
"user.");
}
catch (NamingException ne) {
// An error occurred creating the entry for the user in the
// directory server. Log it, publich a error message, and return.
String errMsg = "An error occurred creating the entry for the " +
"user in the directory server.";
logger.debug("[" + getServiceName() + ".execute] " + errMsg);
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-2002");
error.setErrorDescription(errMsg);
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ne);
return;
}
return;
}
else {
// The createMissingUsers property is false, so we will not create
// the missing user.
logger.info("[" + getServiceName() + ".execute] createMissingUsers is " +
"false, so the missing user will not be created and the " +
"cannot be set.");
return;
}
}
else {
// Otherwise, the user already exists, so the user does not have to be
// created. Log it and return.
logger.info("[" + getServiceName() + ".execute] The directory user already "
+ "exists, so the user does not need to be created.");
return;
}
}
// Handle an EnterpriseUserPassword.Update-Sync
if (messageAction.equalsIgnoreCase("Update")) {
// Get the baseline state of the EnterpriseUserPassword and build an
// EnterpriseUserPassword object.
Element eBaselinePassword = inDoc.getRootElement().getChild("DataArea")
.getChild("BaselineData").getChild("EnterpriseUserPassword");
try {
currentEntUserPassword.buildObjectFromInput(eBaselinePassword);
}
catch (EnterpriseLayoutException ele) {
// An error occurred building the EnterpriseUserPassword object from the
// EnterpriseUserPassword element contained in the BaselineData element
// of the message. Log it and publish a sync error message.
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-1001");
error.setErrorDescription("An error occurred building the " +
"EnterpriseUserPassword object from the EnterpriseUserPassword " +
"element contained in the BaselineData element of the message. The " +
"exception is: " + ele.getMessage());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ele);
return;
}
// Get the new state of the EnterpriseUserPassword and build an
// EnterpriseUserPassword object.
Element eNewPassword = inDoc.getRootElement().getChild("DataArea")
.getChild("NewData").getChild("EnterpriseUserPassword");
try {
newEntUserPassword.buildObjectFromInput(eNewPassword);
}
catch (EnterpriseLayoutException ele) {
// An error occurred building the EnterpriseUserPassword object from the
// EnterpriseUserPassword element contained in the NewData element of
// the message. Log it and publish a sync error message.
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-1002");
error.setErrorDescription("An error occurred building the " +
"EnterpriseUserPassword object from the EnterpriseUserPassword " +
"element contained in the NewData element of the message. The " +
"exception is: " + ele.getMessage());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ele);
return;
}
// Determine whether the user exists is the directory server.
LightweightPerson lPerson = newEntUserPassword.getEnterpriseUser()
.getLightweightPerson();
String userName = "";
if (lPerson.getName() != null) userName = lPerson.getName().getFirstName()
+ " " + lPerson.getName().getLastName();
logger.debug("[" + getServiceName() + ".execute] Querying the directory to " +
"see if the user whose password has changed already exists in the " +
"directory.");
// Specify the search filter for the directory service query using
// the uniqueMember and the uniquePermission built above.
String filter = "employeeNumber=" + lPerson.getInstitutionalId();
logger.debug("[" + getServiceName() + ".execute] Search filter is: " + filter);
// Specify the providerUrl.
String providerUrl = getUserDirectoryTreeBase();
logger.debug("[" + getServiceName() + ".execute] providerUrl is: " +
providerUrl);
// Specify search controls that set the scope of the search.
SearchControls cons = new SearchControls();
cons.setSearchScope(SearchControls.ONELEVEL_SCOPE);
String[] attrs = new String[1];
attrs[0] = "uid";
cons.setReturningAttributes(attrs);
// Search for user entries that match the employeeNumber, retrieving
// the uid.
try {
logger.debug("[" + getServiceName() + ".execute] Querying the directory " +
"server.");
NamingEnumeration results = getDirContext().search(providerUrl, filter,
cons);
// If there are no matching entries, publish a error message indicating
// that the user does not exist.
if (results == null || results.hasMore() == false) {
Error error = new Error();
error.setType("application");
error.setErrorNumber("DirectoryServiceGateway-1005");
error.setErrorDescription("No user " + userName + "(" +
lPerson.getInstitutionalId() + ") exists in the directory. " +
"Cannot reset the password for this user.");
logger.fatal("[" + getServiceName() + ".execute] " +
error.getErrorDescription());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors);
return;
}
// Otherwise, the user already exists in the directory, so reset the
// user's password to be the new value indicated in the message.
else {
// -- Get the dn for the EnterpriseUser.
NetId enterpriseId = new NetId();
enterpriseId = getEnterpriseId(newEntUserPassword.getEnterpriseUser());
String dn = "uid=" + enterpriseId.getPrincipal() + "," + getUserDirectoryTreeBase();
// Specify the modification.
String newPassword = newEntUserPassword.getPassword().getValue();
// SHA algorithm is assumed here. Maybe this should be made configurable.
String shaPw = makeSHA(newPassword);
Attribute userPassword = new BasicAttribute("userPassword");
userPassword.add(shaPw);
ModificationItem[] mods = new ModificationItem[1];
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, userPassword);
// Perform the modification.
try {
logger.debug("[" + getServiceName() + ".execute] Modifying the "
+ "userPassword attribute for user " + dn + " to be '" +
newPassword + "'...");
getDirContext().modifyAttributes(dn, mods);
logger.info("[" + getServiceName() + ".execute] Modified the " +
"userPassword attribute for user " + dn + ".");
}
catch (NamingException ne) {
// An error occurred resetting the user's password. Log it and
// publish a sync error message.
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-1003");
error.setErrorDescription("An error occurred resetting the user's " +
"password. The exception is: " + ne.getMessage());
logger.fatal("[" + getServiceName() + ".execute] " +
error.getErrorDescription());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ne);
return;
}
return;
}
}
catch (NamingException ne) {
// An error occurred determining whether the user exists in the
// directory. Log it and publish a sync error message.
Error error = new Error();
error.setType("system");
error.setErrorNumber("DirectoryServiceGateway-1004");
error.setErrorDescription("An error occurred determining whether the " +
"user exists in the directory. The exception is: " + ne.getMessage());
logger.fatal("[" + getServiceName() + ".execute] " +
error.getErrorDescription());
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors, ne);
return;
}
}
// Otherwise, the message is unsupported. Log it and publish a sync error.
else {
Error error = new Error();
error.setType("application");
error.setErrorNumber("OpenEAI-1002");
error.setErrorDescription("Unsupported message action. This command only "
+ "supports an action of 'update'");
errors = new ArrayList();
errors.add(error);
publishSyncError(eControlArea, errors);
return;
}
}
/**
*
*<P>
* @param EnterpriseUser, an EnterpriseUser.
*<P>
*/
public final NetId getEnterpriseId (EnterpriseUser entUser) {
// If EnterpriseUser is null, return null.
if (entUser == null) return null;
logger.debug("[" + getServiceName() + ".getEnterpriseId] Getting the " +
"EnterpriseId for the EnterpriseUser.");
logger.debug("[" + getServiceName() + ".getEnterpriseId] The " +
"enterpriseIdDomain is: " + getEnterpriseIdDomain());
// Get a list of NetIds.
List netIds = entUser.getNetId();
// Iterate through the NetIds for the EnterpriseUser and find the
// EnterpriseId and return it.
NetId netId = new NetId();
for (int i=0; i < netIds.size(); i++) {
netId = (NetId)netIds.get(i);
logger.debug("[" + getServiceName() + ".getEnterpriseId] Found NetId: "
+ toString(netId));
if (netId.getDomain().equals(getEnterpriseIdDomain())) {
logger.debug("[" + getServiceName() + ".getEnterpriseId] Found " +
"EnterpriseId: " + toString(netId));
return netId;
}
}
logger.debug("[" + getServiceName() + ".getEnterpriseId] Did not find an " +
"EnterpriseId.");
return null;
}
private final javax.naming.directory.BasicAttributes buildDirectoryUser
(EnterpriseUserPassword entUserPassword) {
logger.debug("[" + getServiceName() + ".buildDirectoryUser] Building the " +
"directory person from the EnterpriseUserPassword...");
// Create attributes to be associated with the new context.
BasicAttributes attrs = new BasicAttributes(true); // case-ignore
// Set the values for objectClass.
Attribute objclass = new BasicAttribute("objectClass");
objclass.add("top");
objclass.add("person");
objclass.add("organizationalPerson");
objclass.add("inetOrgPerson");
objclass.add("eduPerson");
attrs.put(objclass);
// Set the value for givenName.
EnterpriseUser entUser = entUserPassword.getEnterpriseUser();
String firstName = null;
if (entUser.getLightweightPerson().getName() != null)
firstName = entUser.getLightweightPerson().getName().getFirstName();
if (firstName == null) firstName = "NoFirstName";
if (firstName != null) {
Attribute givenName = new BasicAttribute("givenName");
givenName.add(firstName);
attrs.put(givenName);
}
// Set the value for sn.
String lastName = null;
if (entUser.getLightweightPerson().getName() != null)
lastName = entUser.getLightweightPerson().getName().getLastName();
if (lastName == null) lastName = "NoLastName";
if (lastName != null) {
Attribute sn = new BasicAttribute("sn");
sn.add(lastName);
attrs.put(sn);
}
// Set the values for cn.
String commonName = null;
if (firstName != null && lastName != null) {
commonName = firstName + " " + lastName;
}
if (firstName == null && lastName != null) {
commonName = lastName;
}
if (firstName != null && lastName == null) {
commonName = firstName;
}
if (firstName == null && lastName == null) {
commonName = null;
}
if (commonName != null) {
Attribute cn = new BasicAttribute("cn");
cn.add(commonName);
attrs.put(cn);
}
// Set the value for uid.
String principal = getEnterpriseId(entUser).getPrincipal();
if (principal != null) {
Attribute uid = new BasicAttribute("uid");
uid.add(principal);
attrs.put(uid);
}
// Set the value for employeeNumber.
String instId = entUser.getLightweightPerson().getInstitutionalId();
if (instId != null) {
Attribute employeeNumber = new BasicAttribute("employeeNumber");
employeeNumber.add(instId);
attrs.put(employeeNumber);
}
// Set the value for userPassword.
String password = entUserPassword.getPassword().getValue();
if (password != null) {
// SHA algorithm is assumed here. Maybe this should be made configurable.
String hashedPassword = makeSHA(password);
Attribute userPassword = new BasicAttribute("userPassword");
userPassword.add(hashedPassword);
attrs.put(userPassword);
}
logger.debug("[" + getServiceName() + ".buildDirectoryUser] Built the " +
"directory person with attributes: " + attrs.toString());
return attrs;
}
private String toString(NetId netId) {
String sNetId = netId.getPrincipal() + "@" + netId.getDomain();
return sNetId;
}
@Override
protected String getServiceName()
{
return "PasswordSyncCommand";
}
/**
* @param password cleartext password
* @return SHA hash String of the password
*/
public static String makeSHA(String password) {
String outputString = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] bytes = password.getBytes();
md.update(bytes);
// Create the digest
byte[] hashedResult = md.digest();
outputString = "{SHA}" + new BASE64Encoder().encode(hashedResult);
}
catch (Exception e) {
System.out.println("Error" + e);
}
return outputString;
}
}