You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by sm...@apache.org on 2014/10/22 17:44:54 UTC
[35/51] [partial] Rename packages from org.openldap.fortress to
org.apache.directory.fortress.core. Change default suffix to org.apache.
Switch default ldap api from unbound to apache ldap.
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/LdapCounters.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/LdapCounters.java b/src/main/java/org/apache/directory/fortress/core/ldap/LdapCounters.java
new file mode 100644
index 0000000..6f83b4d
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/LdapCounters.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ *
+ */
+public class LdapCounters
+{
+ AtomicInteger readCtr = new AtomicInteger( 0 );
+ AtomicInteger searchCtr = new AtomicInteger( 0 );
+ AtomicInteger compareCtr = new AtomicInteger( 0 );
+ AtomicInteger addCtr = new AtomicInteger( 0 );
+ AtomicInteger modCtr = new AtomicInteger( 0 );
+ AtomicInteger deleteCtr = new AtomicInteger( 0 );
+ AtomicInteger bindCtr = new AtomicInteger( 0 );
+
+
+ public void incrementSearch()
+ {
+ searchCtr.incrementAndGet();
+ }
+
+
+ public void incrementRead()
+ {
+ readCtr.incrementAndGet();
+ }
+
+
+ public void incrementCompare()
+ {
+ compareCtr.incrementAndGet();
+ }
+
+
+ public void incrementAdd()
+ {
+ addCtr.incrementAndGet();
+ }
+
+
+ public void incrementMod()
+ {
+ modCtr.incrementAndGet();
+ }
+
+
+ public void incrementDelete()
+ {
+ deleteCtr.incrementAndGet();
+ }
+
+
+ public void incrementBind()
+ {
+ bindCtr.incrementAndGet();
+ }
+
+
+ public long getSearch()
+ {
+ return searchCtr.intValue();
+ }
+
+
+ public long getRead()
+ {
+ return readCtr.intValue();
+ }
+
+
+ public long getCompare()
+ {
+ return compareCtr.intValue();
+ }
+
+
+ public long getAdd()
+ {
+ return addCtr.intValue();
+ }
+
+
+ public long getMod()
+ {
+ return modCtr.intValue();
+ }
+
+
+ public long getDelete()
+ {
+ return deleteCtr.intValue();
+ }
+
+
+ public long getBind()
+ {
+ return bindCtr.intValue();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/PoolMgr.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/PoolMgr.java b/src/main/java/org/apache/directory/fortress/core/ldap/PoolMgr.java
new file mode 100755
index 0000000..e960ad3
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/PoolMgr.java
@@ -0,0 +1,619 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.cfg.Config;
+import org.apache.directory.fortress.core.util.crypto.EncryptUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPConnection;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPException;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPControl;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPConstraints;
+
+/**
+ * This class uses {@link ConnectionPool} to manage pools of {@code com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPConnection}
+ * to supply resource connections to Fortress DAO utilities. The methods in the class are used by internal Fortress functions
+ * and are not intended for used by external clients. This class maintains 3 pools of connections.
+ * <ol>
+ * <li>Connections of type, {@link PoolMgr.ConnType#USER}, use {@link #connPoolUser} for user authentication and password change operations.</li>
+ * <li>Connections of type, {@link PoolMgr.ConnType#ADMIN}, use {@link #connPoolAdmin} and are used for maintenance and interrogation of ldap server objects.</li>
+ * <li>Connections of type, {@link PoolMgr.ConnType#LOG}, use {@link #connPoolLog} and used for pulling slapd log data from the server, </li>
+ * </ol>
+ * <p/>
+ * This class uses <a href="http://www.unboundid.com/products/ldap-sdk/">UnboundID LDAP SDK for Java</a> as client to
+ * process LDAP operations. The UnboundID SDK is distributed under 3 open source licenses and is free to use and distribute in
+ * other open source or proprietary software packages. For more info see, <a href="http://www.unboundid.com/products/ldap-sdk/docs/">LDAP SDK for Java</a>
+ * <p/>
+ * The {@link ConnectionPool} class derives source code from the Mozilla Java LDAP SDK. For more
+ * info on the license this derived code adheres, see: <a href="http://www.mozilla.org/MPL/MPL-1.1.html/">Mozilla Public License Version 1.1</a>
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author Shawn McKinney
+ */
+class PoolMgr
+{
+ // Property names for ldap connection pools:
+ private static final String LDAP_ADMIN_POOL_UID = "admin.user";
+ private static final String LDAP_ADMIN_POOL_PW = "admin.pw";
+ private static final String LDAP_LOG_POOL_UID = "log.admin.user";
+ private static final String LDAP_LOG_POOL_PW = "log.admin.pw";
+ private static final String LDAP_ADMIN_POOL_MIN = "min.admin.conn";
+ private static final String LDAP_ADMIN_POOL_MAX = "max.admin.conn";
+ private static final String LDAP_USER_POOL_MIN = "min.user.conn";
+ private static final String LDAP_USER_POOL_MAX = "max.user.conn";
+ private static final String LDAP_LOG_POOL_MIN = "min.log.conn";
+ private static final String LDAP_LOG_POOL_MAX = "max.log.conn";
+ private static final String LDAP_VERSION = "ldapVersion";
+ private static final String LDAP_CONNECTION_TIMEOUT = "connTimeout";
+ private static final String LDAP_DEBUG_FLAG = "debug.ldap.pool";
+ private static final String LDAP_HOST = "host";
+ private static final String LDAP_PORT = "port";
+
+ // 3 types of connection pools are managed by ths class:
+ static enum ConnType
+ {
+ /**
+ * Admin connections used for most of the Fortress internal operations. Internal bind on connection
+ * will be performed using config param found {@link #LDAP_ADMIN_POOL_UID}
+ */
+ ADMIN,
+
+ /**
+ * User connections for non-admin binds and password mods. Connections will not be bound
+ * to user prior to returning to caller.
+ */
+ USER,
+
+ /**
+ * All slapd log operations use this connection pool. Internal bind on connection
+ * will be performed using config param found {@link #LDAP_LOG_POOL_UID}
+ */
+ LOG
+ }
+
+ // Used to synch the getConnection method:
+ private static final Object adminSynchLock = new Object();
+ private static final Object userSynchLock = new Object();
+ private static final Object logSynchLock = new Object();
+
+ // Canaries in the coal mine:
+ private static LDAPConnection testAdminConn;
+ private static LDAPConnection testUConn;
+ private static LDAPConnection testLConn;
+
+ // Logging
+ private static final String CLS_NM = PoolMgr.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+ // Declare the index for connection pool array:
+ private static final int ADMIN = 0;
+ private static final int USER = 1;
+ private static final int AUDIT = 2;
+
+ // Contains the adminUserId LDAP connections:
+ private static final ConnectionPool connPoolAdmin = null;
+ private static final ConnectionPool connPoolUser = null;
+ private static final ConnectionPool connPoolLog = null;
+ private static final ConnectionPool[] connPool = {connPoolAdmin, connPoolUser, connPoolLog};
+
+ // this modules uses openldap pw policies
+ private static final LDAPControl pwPolicyControl = new LDAPControl(GlobalIds.OPENLDAP_PW_RESPONSE_CONTROL, false, null);
+ private static String adminPw;
+ private static String adminUserId = null;
+ private static final boolean isDebugEnabled = Config.getBoolean(LDAP_DEBUG_FLAG, false);
+ private static int connectionTimeout ;
+ private static int ldapRevision;
+
+ // Load all of the static member variables of this class & initialize the admin connection pools:
+ static
+ {
+ try
+ {
+ adminUserId = Config.getProperty(LDAP_ADMIN_POOL_UID);
+ if(EncryptUtil.isEnabled())
+ {
+ adminPw = EncryptUtil.decrypt(Config.getProperty(LDAP_ADMIN_POOL_PW));
+ }
+ else
+ {
+ adminPw = Config.getProperty(LDAP_ADMIN_POOL_PW);
+ }
+
+ // Default ldap version to v3:
+ ldapRevision = Config.getInt(LDAP_VERSION, 3);
+ // Default 10 seconds for client wait on new connection requests from pool:
+ connectionTimeout = Config.getInt(LDAP_CONNECTION_TIMEOUT, 10000);
+ createAdminPool();
+ }
+
+ // If we can't initialize the connection pools we're dead in the water.
+ catch (com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPException le)
+ {
+ String error = " Static Initializer Block caught com.unboundid.ldap.sdk.migrate.ldapjdk.LdapException=" + le;
+ LOG.error( error );
+ }
+ catch (Exception e)
+ {
+ String error = " Static Initializer Block caught java.lang.Exception=" + e;
+ LOG.error( error );
+ }
+ }
+
+
+ /**
+ * Method performs an LDAP bind for a user/password combination. This function is valid
+ * if and only if the user entity is a member of the USERS data set. The LDAP directory
+ * will return the OpenLDAP PW Policy control.
+ *
+ * @param ld connection to ldap server.
+ * @param userId contains the LDAP dn to the user entry.
+ * @param password contains the password in clear text.
+ * @return boolean value - true if bind successful, false otherwise.
+ * @throws LDAPException in the event of LDAP error.
+ */
+ static boolean bind(LDAPConnection ld, String userId, char[] password)
+ throws LDAPException
+ {
+ return bindUser(userId, password, ld);
+ }
+
+ /**
+ * Close the LDAP connection.
+ *
+ * @param ld handle to ldap connection object.
+ * @param type specifies the type of connection - ADMIN, USER Or LOG.
+ */
+ static void closeConnection(LDAPConnection ld, ConnType type)
+ {
+ switch (type)
+ {
+ case ADMIN:
+ if (ld != null)
+ {
+ connPool[ADMIN].close(ld);
+ }
+ break;
+
+ case USER:
+ if (ld != null)
+ {
+ connPool[USER].close(ld);
+ }
+ break;
+
+ case LOG:
+ if (ld != null)
+ {
+ connPool[AUDIT].close( ld );
+ }
+ break;
+ }
+ }
+
+ /**
+ * Get a connection to the LDAP server.
+ *
+ * @param type type specifies the type of connection - ADMIN, USER Or LOG.
+ * @return ldap connection.
+ * @throws LDAPException
+ */
+ static LDAPConnection getConnection(ConnType type)
+ throws LDAPException
+ {
+ LDAPConnection ld = null;
+ ConnectionPool cp = null;
+ Object lockObj = null;
+ String szType = null;
+ switch (type)
+ {
+ case ADMIN:
+ cp = connPool[ADMIN];
+ lockObj = adminSynchLock;
+ szType = "ADMIN";
+ break;
+
+ case USER:
+ cp = connPool[USER];
+ lockObj = userSynchLock;
+ szType = "USER";
+ break;
+
+ case LOG:
+ cp = connPool[AUDIT];
+ lockObj = logSynchLock;
+ szType = "LOG";
+ break;
+ }
+ try
+ {
+ synchronized (lockObj)
+ {
+ // check the connection pool reference
+ if (cp == null)
+ {
+ String info = "getConnection " + szType + " initializing pool";
+ LOG.info( info );
+ cp = recoverPool(type);
+ }
+ if (connectionTimeout > 0)
+ {
+ ld = cp.getConnection(connectionTimeout);
+ }
+ else
+ {
+ ld = cp.getConnection();
+ }
+ // Did the pool object return a null value?
+ if (ld == null)
+ {
+ String MSG_HDR = "getConnection " + szType;
+ String warning = MSG_HDR + " detected null connection";
+ LOG.warn( warning );
+ // Is the canary is still alive?
+ // todo: recheck this sequence, make sure still good.
+ if (!checkConnection(type))
+ {
+ warning += szType + " attempt to recover pool";
+ LOG.warn( warning );
+ cp = recoverPool(type);
+ ld = cp.getConnection();
+ if (ld == null || !ld.isConnected())
+ {
+ // Give up:
+ String error = MSG_HDR + " could not recover";
+ LOG.error( error );
+ throw new LDAPException(error, LDAPException.LDAP_TIMEOUT);
+ }
+ }
+ // todo: think about this scenario some more. should it attempt recovery of pool here?
+ else
+ {
+ // Cannot establish a good connection, give up:
+ String error = MSG_HDR + " could not retrieve connection";
+ LOG.error( error );
+ throw new LDAPException(error, LDAPException.CONNECT_ERROR);
+ }
+ }
+ // Did the pool object return a bad connection?
+ else if (!ld.isConnected())
+ {
+ String MSG_HDR = "getConnection " + szType;
+ String warning = MSG_HDR + " detected bad connection, retry";
+ LOG.warn( warning );
+ // attempt to reconnect:
+ ld.connect(Config.getProperty(LDAP_HOST, "localhost"), Config.getInt(LDAP_PORT, 389));
+ // if admin connection type must bind here using stored creds:
+ if(type.equals(ConnType.ADMIN))
+ {
+ ld.bind(ldapRevision, adminUserId, adminPw);
+ }
+ // Did the reconnect succeed?
+ if (!ld.isConnected())
+ {
+ warning += szType + " cannot reconnect, attempt pool recovery";
+ LOG.warn( warning );
+ // Try one last ditch effort to recover entire pool.
+ cp = recoverPool(type);
+ ld = cp.getConnection();
+ // Still bad?
+ if (ld == null || !ld.isConnected())
+ {
+ // Give up:
+ String error = MSG_HDR + " recovery failed";
+ LOG.error( error );
+ throw new LDAPException(error, LDAPException.SERVER_DOWN);
+ }
+ }
+ }
+ }
+ }
+ catch (LDAPException e)
+ {
+ String MSG_HDR = "getConnection " + szType;
+ String warning = MSG_HDR + " detected bad connection, retry caught LDAPException=" + e;
+ LOG.warn( warning );
+ // Todo: Test these scenarios:
+ // Did the pool object return a null value or bad conn?
+ if (ld != null && !ld.isConnected()
+ // Make sure this ldap exception wasn't thrown directly above:
+ && e.getLDAPResultCode() != LDAPException.SERVER_DOWN
+ && e.getLDAPResultCode() != LDAPException.CONNECT_ERROR
+ && e.getLDAPResultCode() != LDAPException.LDAP_TIMEOUT)
+ {
+ warning += " attempt to reconnect";
+ LOG.warn( warning );
+ // attempt reconnect:
+ ld.connect(Config.getProperty(LDAP_HOST, "localhost"), Config.getInt(LDAP_PORT, 389));
+ // if admin connection type must bind here using stored creds:
+ if(type.equals(ConnType.ADMIN))
+ {
+ ld.bind(ldapRevision, adminUserId, adminPw);
+ }
+ // Did it work?
+ if (!ld.isConnected())
+ {
+ // Give up:
+ warning = MSG_HDR + " failed to reconnect";
+ LOG.error( warning );
+ throw e;
+ }
+ }
+ else
+ {
+ // Give up
+ warning = MSG_HDR + " failed";
+ LOG.error( warning );
+ throw e;
+ }
+ }
+ return ld;
+ }
+
+
+ /**
+ * Internal function is used to create a new pool of admin connections to ldap server.
+ *
+ * @throws LDAPException
+ */
+ private static void createAdminPool()
+ throws LDAPException
+ {
+ String adminUserId = Config.getProperty(LDAP_ADMIN_POOL_UID);
+ String adminPw;
+ if(EncryptUtil.isEnabled())
+ {
+ adminPw = EncryptUtil.decrypt(Config.getProperty(LDAP_ADMIN_POOL_PW));
+ }
+ else
+ {
+ adminPw = Config.getProperty(LDAP_ADMIN_POOL_PW);
+ }
+
+ String host = Config.getProperty(LDAP_HOST, "localhost");
+ int port = Config.getInt(LDAP_PORT, 389);
+ int min = Config.getInt(LDAP_ADMIN_POOL_MIN, 1);
+ int max = Config.getInt(LDAP_ADMIN_POOL_MAX, 10);
+ LOG.info( "createAdminPool min [" + min + "] max [" + max + "] host [" + host + "] port [" + port
+ + "]" );
+ testAdminConn = new LDAPConnection();
+ connPool[ADMIN] = new ConnectionPool(min, max, host, port, adminUserId, adminPw);
+ if (isDebugEnabled)
+ {
+ connPool[ADMIN].setDebug(true);
+ }
+ }
+
+
+ /**
+ * Internal function is used to create a new pool of user connections to ldap server.
+ *
+ * @throws LDAPException
+ */
+ private static void createUserPool()
+ throws LDAPException
+ {
+ String host = Config.getProperty(LDAP_HOST, "localhost");
+ int port = Config.getInt(LDAP_PORT, 389);
+ int min = Config.getInt(LDAP_USER_POOL_MIN, 1);
+ int max = Config.getInt(LDAP_USER_POOL_MAX, 5);
+ String adminUserId = Config.getProperty(LDAP_ADMIN_POOL_UID);
+ String adminPw;
+ if(EncryptUtil.isEnabled())
+ {
+ adminPw = EncryptUtil.decrypt(Config.getProperty(LDAP_ADMIN_POOL_PW));
+ }
+ else
+ {
+ adminPw = Config.getProperty(LDAP_ADMIN_POOL_PW);
+ }
+
+ LOG.info( "createUserPool min [" + min + "] max [" + max + "] host [" + host + "] port [" + port + "]" );
+ connPool[USER] = new ConnectionPool(min, max, host, port, adminUserId, adminPw);
+ if (isDebugEnabled)
+ {
+ connPool[USER].setDebug(true);
+ }
+ }
+
+ /**
+ * Internal function is used to create a new pool of slapd log connections to ldap server.
+ *
+ * @throws LDAPException
+ */
+ private static void createLogPool()
+ throws LDAPException
+ {
+ String logUserId = Config.getProperty(LDAP_LOG_POOL_UID);
+ String logUserPw;
+ if(EncryptUtil.isEnabled())
+ {
+ logUserPw = EncryptUtil.decrypt(Config.getProperty(LDAP_LOG_POOL_PW));
+ }
+ else
+ {
+ logUserPw = Config.getProperty(LDAP_LOG_POOL_PW);
+ }
+
+ String host = Config.getProperty(LDAP_HOST, "localhost");
+ int port = Config.getInt(LDAP_PORT, 389);
+ int min = Config.getInt(LDAP_LOG_POOL_MIN, 1);
+ int max = Config.getInt(LDAP_LOG_POOL_MAX, 5);
+ LOG.info( "createLogPool min [" + min + "] max [" + max + "] host [" + host + "] port [" + port + "]" );
+ connPool[AUDIT] = new ConnectionPool(min, max, host, port, logUserId, logUserPw);
+ if (isDebugEnabled)
+ {
+ connPool[AUDIT].setDebug( true );
+ }
+ }
+
+ /**
+ * Method is used to perform a bind operation on the given connection object. Connection will contain the
+ * password policy control.
+ *
+ * @param userId contains the LDAP dn to the user entry.
+ * @param password contains the password in clear text.
+ * @param ld contains a valid ldap connection.
+ * @return boolean value - true if bind successful, false otherwise.
+ * @throws LDAPException in the event of LDAP error.
+ */
+ private static boolean bindUser(String userId, char[] password, LDAPConnection ld)
+ throws LDAPException
+ {
+ boolean result;
+ if (ld == null)
+ {
+ String error = "bindUser detected null ldap connection";
+ LOG.error( error );
+ throw new LDAPException(error, LDAPException.CONNECT_ERROR);
+ }
+ if (GlobalIds.IS_OPENLDAP)
+ {
+ LDAPConstraints lCon = new LDAPConstraints();
+ lCon.setServerControls(pwPolicyControl);
+ ld.authenticate(ldapRevision, userId, new String(password), lCon);
+ result = true;
+ }
+ else
+ {
+ ld.authenticate(ldapRevision, userId, new String(password));
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * This method will recover a connection pool in the event the connections become stale due to some network
+ * or system issue.
+ *
+ * @param type contains connection type of request.
+ * @return ConnectionPool reference to newly created connection pool.
+ * @throws LDAPException in the event of ldap system error or the routine fails to reestablish the pool successfully.
+ */
+ private static ConnectionPool recoverPool(ConnType type) throws LDAPException
+ {
+ ConnectionPool cp = null;
+ switch (type)
+ {
+ case ADMIN:
+ if (connPool[ADMIN] != null)
+ {
+ connPool[ADMIN].destroy();
+ }
+ createAdminPool();
+ if (connPool[ADMIN] == null)
+ {
+ String error = "recoverPool LDAP_ADMIN_POOL_UID failed";
+ LOG.error(error);
+ throw new LDAPException(error, LDAPException.CONNECT_ERROR);
+ }
+ cp = connPool[ADMIN];
+ break;
+
+ case USER:
+ if (connPool[USER] != null)
+ {
+ connPool[USER].destroy();
+ }
+ createUserPool();
+ if (connPool[USER] == null)
+ {
+ String error = "recoverPool USER failed";
+ LOG.error(error);
+ throw new LDAPException(error, LDAPException.CONNECT_ERROR);
+ }
+ cp = connPool[USER];
+ break;
+ case LOG:
+ if (connPool[AUDIT] != null)
+ {
+ connPool[AUDIT].destroy();
+ }
+ createLogPool();
+ if (connPool[AUDIT] == null)
+ {
+ String error = "recoverPool LOG failed";
+ LOG.error(error);
+ throw new LDAPException(error, LDAPException.CONNECT_ERROR);
+ }
+ cp = connPool[AUDIT];
+ break;
+ }
+ return cp;
+ }
+
+ /**
+ * System health method will determine the integrity of a given connection associated with a specified pool is good.
+ *
+ * @param type specifies the type of connection - ADMIN, USER Or LOG.
+ * @return true if connection is good, false otherwise.
+ * @throws LDAPException in the event of ldap error.
+ */
+ private static boolean checkConnection(ConnType type)
+ throws LDAPException
+ {
+ boolean rc = false;
+ LDAPConnection conn = null;
+ String szType = null;
+ switch (type)
+ {
+ case ADMIN:
+ conn = testAdminConn;
+ szType = "LDAP_ADMIN_POOL_UID";
+ break;
+ case USER:
+ conn = testUConn;
+ szType = "USER";
+ break;
+ case LOG:
+ conn = testLConn;
+ szType = "LOG";
+ break;
+ }
+ String info = "checkConnection is checking " + szType + " Connection";
+ LOG.info( info );
+ if (conn != null)
+ {
+ if (conn.isConnected())
+ {
+ LOG.debug( "checkConnection for type: {}, is good", szType );
+ rc = true;
+ }
+ else
+ {
+ info = "checkConnection - " + szType + " connection bad";
+ LOG.info( info );
+ conn.reconnect();
+ if (conn.isConnected())
+ {
+ info = "checkConnection - " + szType + " connection reestablished";
+ LOG.info( info );
+ rc = true;
+ }
+ }
+ }
+ info = "checkConnetion status code=" + rc;
+ LOG.info( info );
+ return rc;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/UnboundIdDataProvider.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/UnboundIdDataProvider.java b/src/main/java/org/apache/directory/fortress/core/ldap/UnboundIdDataProvider.java
new file mode 100644
index 0000000..944dcb0
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/UnboundIdDataProvider.java
@@ -0,0 +1,1277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap;
+
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.directory.fortress.core.GlobalIds;
+import org.apache.directory.fortress.core.cfg.Config;
+import org.apache.directory.fortress.core.rbac.FortEntity;
+import org.apache.directory.fortress.core.rbac.Hier;
+import org.apache.directory.fortress.core.rbac.Relationship;
+import org.apache.directory.fortress.core.util.attr.VUtil;
+import org.apache.directory.fortress.core.util.time.CUtil;
+import org.apache.directory.fortress.core.util.time.Constraint;
+
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPAttribute;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPAttributeSet;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPConnection;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPControl;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPDN;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPEntry;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPException;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPModification;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPModificationSet;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPReferralException;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPSearchConstraints;
+import com.unboundid.ldap.sdk.migrate.ldapjdk.LDAPSearchResults;
+
+
+/**
+ * Abstract class contains methods to perform low-level entity to ldap persistence. These methods are called by the
+ * Fortress DAO's, i.e. {@link org.apache.directory.fortress.core.rbac.dao.unboundid.UserDAO}. {@link org.apache.directory.fortress.core.rbac.dao.unboundid.RoleDAO}, {@link org.apache.directory.fortress.core.rbac.dao.unboundid.PermDAO}, ....
+ * These are low-level data utilities, very little if any data validations are performed here.
+ * <p/>
+ * This class is thread safe.
+ * <p/>
+ *
+ * @author Shawn McKinney
+ */
+public abstract class UnboundIdDataProvider
+{
+ private static final String OPENLDAP_PROXY_CONTROL = "2.16.840.1.113730.3.4.18";
+ private static final int MAX_DEPTH = 100;
+ private static final String CLS_NM = UnboundIdDataProvider.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+ private static final LdapCounters counters = new LdapCounters();
+
+
+ /**
+ * Given a contextId and a fortress param name return the LDAP dn.
+ *
+ * @param contextId is to determine what sub-tree to use.
+ * @param root contains the fortress parameter name that corresponds with a particular LDAP container.
+ * @return String contains the dn to use for operation.
+ */
+ protected String getRootDn( String contextId, String root )
+ {
+ String szDn = Config.getProperty( root );
+ StringBuilder dn = new StringBuilder();
+
+ // The contextId must not be null, or "HOME" or "null"
+ if ( VUtil.isNotNullOrEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL )
+ && !contextId.equals( GlobalIds.HOME ) )
+ {
+ int idx = szDn.indexOf( Config.getProperty( GlobalIds.SUFFIX ) );
+
+ if ( idx != -1 )
+ {
+ // Found. The DN is ,ou=<contextId>,
+ dn.append( szDn.substring( 0, idx - 1 ) ).append( "," ).append( GlobalIds.OU ).append( "=" )
+ .append( contextId ).append( "," ).append( szDn.substring( idx ) );
+ }
+ }
+ else
+ {
+ dn.append( szDn );
+ }
+
+ return dn.toString();
+ }
+
+
+ /**
+ * Given a contextId return the LDAP dn that includes the suffix.
+ *
+ * @param contextId is to determine what sub-tree to use.
+ * @return String contains the dn to use for operation.
+ */
+ protected String getRootDn( String contextId )
+ {
+ StringBuilder dn = new StringBuilder();
+ if ( VUtil.isNotNullOrEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL )
+ && !contextId.equals( GlobalIds.HOME ) )
+ {
+ dn.append( GlobalIds.OU ).append( "=" ).append( contextId ).append( "," )
+ .append( Config.getProperty( GlobalIds.SUFFIX ) );
+ }
+ else
+ {
+ dn.append( Config.getProperty( GlobalIds.SUFFIX ) );
+ }
+ return dn.toString();
+ }
+
+
+ /**
+ * Read the ldap record from specified location.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains ldap distinguished name.
+ * @param atrs array contains array names to pull back.
+ * @return ldap entry.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected LDAPEntry read( LDAPConnection ld, String dn, String[] atrs )
+ throws LDAPException
+ {
+ counters.incrementRead();
+
+ return ld.read( dn, atrs );
+ }
+
+
+ /**
+ * Read the ldap record from specified location with user assertion.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains ldap distinguished name.
+ * @param atrs array contains array names to pull back. , PoolMgr.ConnType.USER
+ * @param userDn string value represents the identity of user on who's behalf the request was initiated. The value will be stored in openldap auditsearch record AuthZID's attribute.
+ * @return ldap entry.
+ * @throws LDAPException in the event system error occurs.
+ * @throws UnsupportedEncodingException for search control errors.
+ */
+ protected LDAPEntry read( LDAPConnection ld, String dn, String[] atrs, String userDn )
+ throws LDAPException, UnsupportedEncodingException
+ {
+ counters.incrementRead();
+ LDAPControl proxyCtl = new LDAPControl( OPENLDAP_PROXY_CONTROL, true,
+ ( GlobalIds.DN + ": " + userDn ).getBytes( GlobalIds.UTF8 ) );
+ LDAPSearchConstraints opt = new LDAPSearchConstraints();
+ opt.setServerControls( proxyCtl );
+ return ld.read( dn, atrs, opt );
+ }
+
+
+ /**
+ * Add a new ldap entry to the directory. Do not add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param entry contains data to add..
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void add( LDAPConnection ld, LDAPEntry entry )
+ throws LDAPException
+ {
+ counters.incrementAdd();
+ ld.add( entry );
+ }
+
+
+ /**
+ * Add a new ldap entry to the directory. Add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param entry contains data to add..
+ * @param entity contains audit context.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void add( LDAPConnection ld, LDAPEntry entry, FortEntity entity )
+ throws LDAPException
+ {
+ counters.incrementAdd();
+
+ if ( GlobalIds.IS_AUDIT && entity != null && entity.getAdminSession() != null )
+ {
+ LDAPAttributeSet attrs = entry.getAttributeSet();
+
+ if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+ {
+ attrs.add( new LDAPAttribute( GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() ) );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+ {
+ attrs.add( new LDAPAttribute( GlobalIds.FT_MODIFIER_CODE, entity.getModCode() ) );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+ {
+ attrs.add( new LDAPAttribute( GlobalIds.FT_MODIFIER_ID, entity.getModId() ) );
+ }
+ }
+
+ ld.add( entry );
+ }
+
+
+ /**
+ * Update exiting ldap entry to the directory. Do not add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry.
+ * @param mods contains data to modify.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void modify( LDAPConnection ld, String dn, LDAPModificationSet mods )
+ throws LDAPException
+ {
+ counters.incrementMod();
+ ld.modify( dn, mods );
+ }
+
+
+ /**
+ * Update exiting ldap entry to the directory. Add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry.
+ * @param mods contains data to modify.
+ * @param entity contains audit context.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void modify( LDAPConnection ld, String dn, LDAPModificationSet mods, FortEntity entity )
+ throws LDAPException
+ {
+ counters.incrementMod();
+ audit( mods, entity );
+ ld.modify( dn, mods );
+ }
+
+
+ /**
+ * Delete exiting ldap entry from the directory. Do not add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void delete( LDAPConnection ld, String dn )
+ throws LDAPException
+ {
+ counters.incrementDelete();
+ ld.delete( dn );
+ }
+
+
+ /**
+ * Delete exiting ldap entry from the directory. Add audit context. This method will call modify prior to delete which will
+ * force corresponding audit record to be written to slapd access log.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @param entity contains audit context.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void delete( LDAPConnection ld, String dn, FortEntity entity )
+ throws LDAPException
+ {
+ counters.incrementDelete();
+ LDAPModificationSet mods = new LDAPModificationSet();
+ audit( mods, entity );
+ if ( mods.size() > 0 )
+ modify( ld, dn, mods );
+ ld.delete( dn );
+ }
+
+
+ /**
+ * Delete exiting ldap entry and all descendants from the directory. Do not add audit context.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void deleteRecursive( LDAPConnection ld, String dn )
+ throws LDAPException
+ {
+ int recursiveCount = 0;
+ deleteRecursive( dn, ld, recursiveCount );
+ }
+
+
+ /**
+ * Delete exiting ldap entry and all descendants from the directory. Add audit context. This method will call modify prior to delete which will
+ * force corresponding audit record to be written to slapd access log.
+ *
+ * @param ld handle to ldap connection.
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @param entity contains audit context.
+ * @throws LDAPException in the event system error occurs.
+ */
+ protected void deleteRecursive( LDAPConnection ld, String dn, FortEntity entity )
+ throws LDAPException
+ {
+ LDAPModificationSet mods = new LDAPModificationSet();
+ audit( mods, entity );
+ if ( mods.size() > 0 )
+ modify( ld, dn, mods );
+ deleteRecursive( ld, dn );
+ }
+
+
+ /**
+ * Used to recursively remove all nodes up to record pointed to by dn attribute.
+ *
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @param ld handle to ldap connection.
+ * @param recursiveCount keeps track of how many iterations have been performed.
+ * @throws LDAPException in the event system error occurs.
+ */
+ private void deleteRecursive( String dn, LDAPConnection ld, int recursiveCount )
+ throws LDAPException
+ {
+ String method = "deleteRecursive";
+ // Sanity check - only allow max tree depth of 100
+ if ( recursiveCount++ > MAX_DEPTH )
+ {
+ // too deep inside of a recursive sequence;
+ String error = "." + method + " dn [" + dn + "] depth error in recursive";
+ throw new LDAPException( error, LDAPException.OPERATION_ERROR );
+ }
+
+ String theDN;
+ // Find child nodes
+ LDAPSearchResults res = search( ld, dn, LDAPConnection.SCOPE_ONE, "objectclass=*", GlobalIds.NO_ATRS, false, 0 );
+
+ // Iterate over all entries under this entry
+ while ( res.hasMoreElements() )
+ {
+ try
+ {
+ // Next directory entry
+ LDAPEntry entry = res.next();
+ theDN = entry.getDN();
+ // continue down:
+ deleteRecursive( theDN, ld, recursiveCount );
+ recursiveCount--;
+ }
+ catch ( LDAPReferralException lre )
+ {
+ // cannot continue;
+ String error = "." + method + " dn [" + dn + "] caught LDAPReferralException="
+ + lre.errorCodeToString() + "=" + lre.getLDAPErrorMessage();
+ throw new LDAPException( error, lre.getLDAPResultCode() );
+ }
+ catch ( LDAPException ldape )
+ {
+ // cannot continue;
+ String error = "." + method + " dn [" + dn + "] caught LDAPException="
+ + ldape.errorCodeToString() + "=" + ldape.getLDAPErrorMessage();
+ throw new LDAPException( error, ldape.getLDAPResultCode() );
+ }
+ }
+ // delete the node:
+ counters.incrementDelete();
+ delete( ld, dn );
+ }
+
+
+ /**
+ * Add the audit context variables to the modfication set.
+ *
+ * @param mods used to update ldap attributes.
+ * @param entity contains audit context.
+ * @throws LDAPException in the event of error with ldap client.
+ */
+ private void audit( LDAPModificationSet mods, FortEntity entity )
+ {
+ if ( GlobalIds.IS_AUDIT && entity != null && entity.getAdminSession() != null )
+ {
+ if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+ {
+ LDAPAttribute modifier = new LDAPAttribute( GlobalIds.FT_MODIFIER, entity.getAdminSession()
+ .getInternalUserId() );
+ mods.add( LDAPModification.REPLACE, modifier );
+ }
+ if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+ {
+ LDAPAttribute modCode = new LDAPAttribute( GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
+ mods.add( LDAPModification.REPLACE, modCode );
+ }
+ if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+ {
+ LDAPAttribute modId = new LDAPAttribute( GlobalIds.FT_MODIFIER_ID, entity.getModId() );
+ mods.add( LDAPModification.REPLACE, modId );
+ }
+ }
+ }
+
+
+ /**
+ * Perform normal ldap search accepting default batch size.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param baseDn contains address of distinguished name to begin ldap search
+ * @param scope indicates depth of search starting at basedn. 0 (base dn), 1 (one level down) or 2 (infinite) are valid values.
+ * @param filter contains the search criteria
+ * @param atrs is the requested list of attritubutes to return from directory search.
+ * @param attrsOnly if true pull back attribute names only.
+ * @return result set containing ldap entries returned from directory.
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ */
+ protected LDAPSearchResults search( LDAPConnection ld,
+ String baseDn,
+ int scope,
+ String filter,
+ String[] atrs,
+ boolean attrsOnly )
+ throws LDAPException
+ {
+ counters.incrementSearch();
+ LDAPSearchResults result;
+ result = ld.search( baseDn, scope, filter, atrs, attrsOnly );
+ return result;
+ }
+
+
+ /**
+ * Perform normal ldap search specifying default batch size.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param baseDn contains address of distinguished name to begin ldap search
+ * @param scope indicates depth of search starting at basedn. 0 (base dn), 1 (one level down) or 2 (infinite) are valid values.
+ * @param filter contains the search criteria
+ * @param atrs is the requested list of attritubutes to return from directory search.
+ * @param attrsOnly if true pull back attribute names only.
+ * @param batchSize Will block until this many entries are ready to return from server. 0 indicates to block until all results are ready.
+ * @return result set containing ldap entries returned from directory.
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ */
+ protected LDAPSearchResults search( LDAPConnection ld,
+ String baseDn,
+ int scope,
+ String filter,
+ String[] atrs,
+ boolean attrsOnly,
+ int batchSize )
+ throws LDAPException
+ {
+ counters.incrementSearch();
+ LDAPSearchResults result;
+ LDAPSearchConstraints ldCons = new LDAPSearchConstraints();
+ // Returns the maximum number of search results that are to be returned; 0 means there is no limit.
+ ldCons.setMaxResults( 0 );
+ ldCons.setBatchSize( batchSize );
+ result = ld.search( baseDn, scope, filter, atrs, attrsOnly, ldCons );
+ return result;
+ }
+
+
+ /**
+ * Perform normal ldap search specifying default batch size and max entries to return.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param baseDn contains address of distinguished name to begin ldap search
+ * @param scope indicates depth of search starting at basedn. 0 (base dn), 1 (one level down) or 2 (infinite) are valid values.
+ * @param filter contains the search criteria
+ * @param atrs is the requested list of attritubutes to return from directory search.
+ * @param attrsOnly if true pull back attribute names only.
+ * @param batchSize Will block until this many entries are ready to return from server. 0 indicates to block until all results are ready.
+ * @param maxEntries specifies the maximum number of entries to return in this search query.
+ * @return result set containing ldap entries returned from directory.
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ */
+ protected LDAPSearchResults search( LDAPConnection ld,
+ String baseDn,
+ int scope,
+ String filter,
+ String[] atrs,
+ boolean attrsOnly,
+ int batchSize,
+ int maxEntries )
+ throws LDAPException
+ {
+ counters.incrementSearch();
+ LDAPSearchResults result;
+ LDAPSearchConstraints ldCons = new LDAPSearchConstraints();
+ // Returns the maximum number of search results that are to be returned;
+ ldCons.setMaxResults( maxEntries );
+ ldCons.setBatchSize( batchSize );
+ result = ld.search( baseDn, scope, filter, atrs, attrsOnly, ldCons );
+ return result;
+ }
+
+
+ /**
+ * This method will search the directory and return at most one record. If more than one record is found
+ * an ldap exception will be thrown.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param baseDn contains address of distinguished name to begin ldap search
+ * @param scope indicates depth of search starting at basedn. 0 (base dn), 1 (one level down) or 2 (infinite) are valid values.
+ * @param filter contains the search criteria
+ * @param atrs is the requested list of attritubutes to return from directory search.
+ * @param attrsOnly if true pull back attribute names only.
+ * @return entry containing target ldap node.
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ */
+ protected LDAPEntry searchNode( LDAPConnection ld,
+ String baseDn,
+ int scope, String filter,
+ String[] atrs,
+ boolean attrsOnly )
+ throws LDAPException
+ {
+ LDAPSearchResults result = ld.search( baseDn, scope,
+ filter, atrs, attrsOnly );
+ if ( result.getCount() > 1 )
+ {
+ throw new LDAPException( "searchNode failed to return unique record for LDAP search of base DN ["
+ + baseDn + "] filter [" + filter + "]" );
+ }
+ return result.next();
+ }
+
+
+ /**
+ * This search method uses OpenLDAP Proxy Authorization Control to assert arbitrary user identity onto connection.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param baseDn contains address of distinguished name to begin ldap search
+ * @param scope indicates depth of search starting at basedn. 0 (base dn), 1 (one level down) or 2 (infinite) are valid values.
+ * @param filter contains the search criteria
+ * @param atrs is the requested list of attritubutes to return from directory search.
+ * @param attrsOnly if true pull back attribute names only.
+ * @param userDn string value represents the identity of user on who's behalf the request was initiated. The value will be stored in openldap auditsearch record AuthZID's attribute.
+ * @return entry containing target ldap node.
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ */
+ protected LDAPEntry searchNode( LDAPConnection ld,
+ String baseDn,
+ int scope,
+ String filter,
+ String[] atrs,
+ boolean attrsOnly,
+ String userDn )
+ throws LDAPException, UnsupportedEncodingException
+ {
+ counters.incrementSearch();
+ LDAPControl proxyCtl = new LDAPControl( OPENLDAP_PROXY_CONTROL, true,
+ ( GlobalIds.DN + ": " + userDn ).getBytes( GlobalIds.UTF8 ) );
+ LDAPSearchConstraints opt = new LDAPSearchConstraints();
+ opt.setServerControls( proxyCtl );
+ LDAPSearchResults result = ld.search( baseDn, scope, filter, atrs, attrsOnly, opt );
+ if ( result.getCount() > 1 )
+ {
+ throw new LDAPException( "searchNode failed to return unique record for LDAP search of base DN ["
+ + baseDn + "] filter [" + filter + "]" );
+ }
+ return result.next();
+ }
+
+
+ /**
+ * This method uses the compare ldap func to assert audit record into the directory server's configured audit logger.
+ *
+ * @param ld is LDAPConnection object used for all communication with host.
+ * @param dn contains address of distinguished name to begin ldap search
+ * @param userDn dn for user node
+ * @param attribute attribute used for compare
+ * @return true if compare operation succeeds
+ * @throws LDAPException thrown in the event of error in ldap client or server code.
+ * @throws UnsupportedEncodingException in the event the server cannot perform the operation.
+ */
+ protected boolean compareNode( LDAPConnection ld,
+ String dn,
+ String userDn,
+ LDAPAttribute attribute )
+ throws LDAPException, UnsupportedEncodingException
+ {
+ counters.incrementCompare();
+ LDAPControl proxyCtl = new LDAPControl( OPENLDAP_PROXY_CONTROL, true,
+ ( GlobalIds.DN + ": " + userDn ).getBytes( GlobalIds.UTF8 ) );
+ LDAPSearchConstraints opt = new LDAPSearchConstraints();
+ opt.setServerControls( proxyCtl );
+ return ld.compare( dn, attribute, opt );
+ }
+
+
+ /**
+ * Method wraps ldap client to return multi-occurring attribute values by name within a given entry and returns as a list of strings.
+ *
+ * @param entry contains the target ldap entry.
+ * @param attributeName name of ldap attribute to retrieve.
+ * @return List of type string containing attribute values.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected List<String> getAttributes( LDAPEntry entry, String attributeName )
+ {
+ List<String> attrValues = new ArrayList<>();
+ LDAPAttribute attr;
+ Enumeration values;
+ attr = entry.getAttribute( attributeName );
+ if ( attr != null )
+ {
+ values = attr.getStringValues();
+ }
+ else
+ {
+ return null;
+ }
+ if ( values != null )
+ {
+ while ( values.hasMoreElements() )
+ {
+ attrValues.add( ( String ) values.nextElement() );
+ }
+ }
+ return attrValues;
+ }
+
+
+ protected byte[] getPhoto( LDAPEntry entry, String attributeName )
+ {
+ byte[] photo = null;
+ LDAPAttribute attr = entry.getAttribute( attributeName );
+ if ( attr != null )
+ {
+ // Get the values as byte arrays
+ Enumeration enumVals =
+ attr.getByteValues();
+ // Get the first value - if there's more
+ // than one
+ if ( enumVals.hasMoreElements() )
+ {
+ photo =
+ ( byte[] ) enumVals.nextElement();
+ }
+ }
+ return photo;
+ }
+
+
+ /**
+ * Method wraps ldap client to return multi-occurring attribute values by name within a given entry and returns as a set of strings.
+ *
+ * @param entry contains the target ldap entry.
+ * @param attributeName name of ldap attribute to retrieve.
+ * @return List of type string containing attribute values.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected Set<String> getAttributeSet( LDAPEntry entry, String attributeName )
+ {
+ // create Set with case insensitive comparator:
+ Set<String> attrValues = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+ LDAPAttribute attr;
+ Enumeration values;
+ attr = entry.getAttribute( attributeName );
+ if ( attr != null )
+ {
+ values = attr.getStringValues();
+ }
+ else
+ {
+ return null;
+ }
+ if ( values != null )
+ {
+ while ( values.hasMoreElements() )
+ {
+ attrValues.add( ( String ) values.nextElement() );
+ }
+ }
+ return attrValues;
+ }
+
+
+ /**
+ * Method wraps ldap client to return multi-occurring attribute values by name within a given entry and return as a list of type {@link org.apache.directory.fortress.core.rbac.Relationship}.
+ *
+ * @param entry contains the target ldap entry.
+ * @param attributeName name of ldap attribute to retrieve.
+ * @return List of type {@link org.apache.directory.fortress.core.rbac.Relationship} containing parent-child relationships.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected List<Relationship> getRelationshipAttributes( LDAPEntry entry, String attributeName )
+ {
+ List<Relationship> attrValues = new ArrayList<>();
+ LDAPAttribute attr;
+ Enumeration values;
+
+ attr = entry.getAttribute( attributeName );
+ if ( attr != null )
+ {
+ values = attr.getStringValues();
+ }
+ else
+ {
+ return null;
+ }
+ if ( values != null )
+ {
+ while ( values.hasMoreElements() )
+ {
+ String edge = ( String ) values.nextElement();
+ int indx = edge.indexOf( GlobalIds.PROP_SEP );
+ if ( indx >= 1 )
+ {
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ // Separate the parent from the child:
+ String child = edge.substring( 0, indx );
+ String parent = edge.substring( indx + 1 );
+
+ // Load the parent/child relationship values into a helper class:
+ Relationship rel = new Relationship( child, parent );
+ attrValues.add( rel );
+ }
+ else
+ {
+ String warning = "getRelAttributes detected incorrect data in role relationship field: "
+ + edge;
+ LOG.warn( warning );
+ }
+ }
+ }
+ return attrValues;
+ }
+
+
+ /**
+ * Method wraps ldap client to return attribute value by name within a given entry and returns as a string.
+ *
+ * @param entry contains the target ldap entry.
+ * @param attributeName name of ldap attribute to retrieve.
+ * @return value contained in a string variable.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected String getAttribute( LDAPEntry entry, String attributeName )
+ {
+ String attrValue = null;
+ LDAPAttribute attr;
+ Enumeration values;
+ attr = entry.getAttribute( attributeName );
+ if ( attr != null )
+ {
+ values = attr.getStringValues();
+ }
+ else
+ {
+ return null;
+ }
+ if ( values != null )
+ {
+ attrValue = ( String ) values.nextElement();
+ }
+ return attrValue;
+ }
+
+
+ /**
+ * Method will retrieve the relative distinguished name from a distinguished name variable.
+ *
+ * @param dn contains ldap distinguished name.
+ * @return rDn as string.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected String getRdn( String dn )
+ {
+ String[] dnList;
+ dnList = LDAPDN.explodeDN( dn, true );
+ return dnList[0];
+ }
+
+
+ /**
+ * Create multi-occurring ldap attribute given array of strings and attribute name.
+ *
+ * @param name contains attribute name to create.
+ * @param values array of string that contains attribute values.
+ * @return LDAPAttribute containing multi-occurring attribute set.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected LDAPAttribute createAttributes( String name, String values[] )
+ throws LDAPException
+ {
+ LDAPAttribute attr = new LDAPAttribute( name );
+ for ( String value : values )
+ {
+ encodeSafeText( value, value.length() );
+ attr.addValue( value );
+ }
+ return attr;
+ }
+
+
+ /**
+ * Create ldap attribute given an attribute name and value.
+ *
+ * @param name contains attribute name to create.
+ * @param value string contains attribute value.
+ * @return LDAPAttribute containing new ldap attribute.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected LDAPAttribute createAttribute( String name, String value )
+ throws LDAPException
+ {
+ LDAPAttribute attr = new LDAPAttribute( name );
+ encodeSafeText( value, value.length() );
+ attr.addValue( value );
+ return attr;
+ }
+
+
+ /**
+ * Convert constraint from raw ldap format to application entity.
+ *
+ * @param le ldap entry containing constraint.
+ * @param ftDateTime reference to {@link org.apache.directory.fortress.util.time.Constraint} containing formatted data.
+ * @throws LDAPException in the event of ldap client error.
+ */
+ protected void unloadTemporal( LDAPEntry le, Constraint ftDateTime )
+ {
+ String szRawData = getAttribute( le, GlobalIds.CONSTRAINT );
+
+ if ( szRawData != null && szRawData.length() > 0 )
+ {
+ CUtil.setConstraint( szRawData, ftDateTime );
+ }
+ }
+
+
+ /**
+ * Given an ldap attribute name and a list of attribute values, construct an ldap attribute set to be added to directory.
+ *
+ * @param list list of type string containing attribute values to load into attribute set.
+ * @param attrs contains ldap attribute set targeted for adding.
+ * @param attrName name of ldap attribute being added.
+ */
+ protected void loadAttrs( List<String> list, LDAPAttributeSet attrs, String attrName )
+ {
+ if ( list != null && list.size() > 0 )
+ {
+ LDAPAttribute attr = null;
+ for ( String val : list )
+ {
+ if ( attr == null )
+ {
+ attr = new LDAPAttribute( attrName, val );
+ }
+ else
+ {
+ attr.addValue( val );
+ }
+ }
+ if ( attr != null )
+ {
+ attrs.add( attr );
+ }
+ }
+ }
+
+
+ /**
+ * Given a collection of {@link org.apache.directory.fortress.core.rbac.Relationship}, convert to raw data name-value format and load into ldap attribute set in preparation for ldap add.
+ *
+ * @param list contains List of type {@link org.apache.directory.fortress.core.rbac.Relationship} targeted for adding to ldap.
+ * @param attrs collection of ldap attributes containing parent-child relationships in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be added.
+ */
+ protected void loadRelationshipAttrs( List<Relationship> list, LDAPAttributeSet attrs, String attrName )
+ {
+ if ( list != null )
+ {
+ LDAPAttribute attr = null;
+ for ( Relationship rel : list )
+ {
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ if ( attr == null )
+ {
+ attr = new LDAPAttribute( attrName, rel.getChild() + GlobalIds.PROP_SEP + rel.getParent() );
+ }
+ else
+ {
+ attr.addValue( rel.getChild() + GlobalIds.PROP_SEP + rel.getParent() );
+ }
+ }
+ if ( attr != null )
+ {
+ attrs.add( attr );
+ }
+ }
+ }
+
+
+ /**
+ * Given an ldap attribute name and a set of attribute values, construct an ldap attribute set to be added to directory.
+ *
+ * @param values set of type string containing attribute values to load into attribute set.
+ * @param attrs contains ldap attribute set targeted for adding.
+ * @param attrName name of ldap attribute being added.
+ */
+ protected void loadAttrs( Set<String> values, LDAPAttributeSet attrs, String attrName )
+ {
+ if ( values != null && values.size() > 0 )
+ {
+ LDAPAttribute attr = null;
+ for ( String value : values )
+ {
+ if ( attr == null )
+ {
+ attr = new LDAPAttribute( attrName, value );
+ }
+ else
+ {
+ attr.addValue( value );
+ }
+ }
+ if ( attr != null )
+ {
+ attrs.add( attr );
+ }
+ }
+ }
+
+
+ /**
+ * Given a multi-occurring ldap attribute name and a list of attribute values, construct an ldap modification set to be updated in directory.
+ * This function will replace all existing attributes with new values.
+ *
+ * @param list list of type string containing attribute values to load into modification set.
+ * @param mods contains ldap modification set targeted for updating.
+ * @param attrName name of ldap attribute being modified.
+ */
+ protected void loadAttrs( List<String> list, LDAPModificationSet mods, String attrName )
+ {
+ loadAttrs( list, mods, attrName, true );
+ }
+
+ /**
+ * Given a multi-occurring ldap attribute name and a list of attribute values, construct an ldap modification set to be updated in directory.
+ *
+ * @param list list of type string containing attribute values to load into modification set.
+ * @param mods contains ldap modification set targeted for updating.
+ * @param attrName name of ldap attribute being modified.
+ * @param replace boolean value if true will replace existing attributes with new..
+ */
+ protected void loadAttrs( List<String> list, LDAPModificationSet mods, String attrName, boolean replace )
+ {
+ if ( list != null && list.size() > 0 )
+ {
+ LDAPAttribute attr = new LDAPAttribute( attrName );
+ if(replace)
+ {
+ mods.add( LDAPModification.REPLACE, attr );
+ }
+
+ for ( String val : list )
+ {
+ attr = new LDAPAttribute( attrName, val );
+ mods.add( LDAPModification.ADD, attr );
+ }
+ }
+ }
+
+ /**
+ * Given a collection of {@link org.apache.directory.fortress.core.rbac.Relationship}s, convert to raw data name-value format and load into ldap modification set in preparation for ldap modify.
+ *
+ * @param list contains List of type {@link org.apache.directory.fortress.core.rbac.Relationship} targeted for updating in ldap.
+ * @param mods ldap modification set containing parent-child relationships in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be updated.
+ * @param op specifies type of mod: {@link Hier.Op#ADD}, {@link org.apache.directory.fortress.core.rbac.Hier.Op#MOD}, {@link Hier.Op#REM}
+ */
+ protected void loadRelationshipAttrs( List<Relationship> list, LDAPModificationSet mods, String attrName, Hier.Op op )
+ {
+ if ( list != null )
+ {
+ LDAPAttribute attr;
+ for ( Relationship rel : list )
+ {
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ attr = new LDAPAttribute( attrName, rel.getChild() + GlobalIds.PROP_SEP + rel.getParent() );
+ switch ( op )
+ {
+ case ADD:
+ mods.add( LDAPModification.ADD, attr );
+ break;
+ case MOD:
+ mods.add( LDAPModification.REPLACE, attr );
+ break;
+ case REM:
+ mods.add( LDAPModification.DELETE, attr );
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Given an ldap attribute name and a set of attribute values, construct an ldap modification set to be updated in directory.
+ *
+ * @param values set of type string containing attribute values to load into modification set.
+ * @param mods contains ldap modification set targeted for updating.
+ * @param attrName name of ldap attribute being updated.
+ */
+ protected void loadAttrs( Set<String> values, LDAPModificationSet mods, String attrName )
+ {
+ if ( values != null && values.size() > 0 )
+ {
+ LDAPAttribute attr = new LDAPAttribute( attrName );
+ mods.add( LDAPModification.REPLACE, attr );
+ for ( String value : values )
+ {
+ attr = new LDAPAttribute( attrName, value );
+ mods.add( LDAPModification.ADD, attr );
+ }
+ }
+ }
+
+
+ /**
+ * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap modify.
+ *
+ * @param props contains {@link java.util.Properties} targeted for updating in ldap.
+ * @param mods ldap modification set containing name-value pairs in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be updated.
+ * @param replace boolean variable, if set to true use {@link LDAPModification#REPLACE} else {@link LDAPModification#ADD}.
+ */
+ protected void loadProperties( Properties props, LDAPModificationSet mods, String attrName, boolean replace )
+ {
+ loadProperties( props, mods, attrName, GlobalIds.PROP_SEP, replace );
+ }
+
+
+ /**
+ * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap modify.
+ *
+ * @param props contains {@link java.util.Properties} targeted for updating in ldap.
+ * @param mods ldap modification set containing name-value pairs in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be updated.
+ * @param separator contains the delimiter for the property.
+ * @param replace boolean variable, if set to true use {@link LDAPModification#REPLACE} else {@link LDAPModification#ADD}.
+ */
+ protected void loadProperties( Properties props, LDAPModificationSet mods, String attrName, char separator, boolean replace )
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ LDAPAttribute prop = new LDAPAttribute( attrName );
+ if ( replace )
+ mods.add( LDAPModification.REPLACE, prop );
+
+ for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+ {
+ String key = ( String ) e.nextElement();
+ String val = props.getProperty( key );
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ prop = new LDAPAttribute( attrName, key + separator + val );
+ mods.add( LDAPModification.ADD, prop );
+ }
+ }
+ }
+
+
+ /**
+ * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap modify.
+ *
+ * @param props contains {@link java.util.Properties} targeted for removal from ldap.
+ * @param mods ldap modification set containing name-value pairs in raw ldap format to be removed.
+ * @param attrName contains the name of the ldap attribute to be removed.
+ */
+ protected void removeProperties( Properties props, LDAPModificationSet mods, String attrName )
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ LDAPAttribute prop;
+ for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+ {
+ String key = ( String ) e.nextElement();
+ String val = props.getProperty( key );
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ prop = new LDAPAttribute( attrName, key + GlobalIds.PROP_SEP + val );
+ mods.add( LDAPModification.DELETE, prop );
+ }
+ }
+ }
+
+
+ /**
+ * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap add.
+ *
+ * @param props contains {@link java.util.Properties} targeted for adding to ldap.
+ * @param attrs ldap attribute set containing name-value pairs in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be added.
+ */
+ protected void loadProperties( Properties props, LDAPAttributeSet attrs, String attrName )
+ {
+ loadProperties( props, attrs, attrName, ':' );
+ }
+
+
+ /**
+ * Given a collection of {@link java.util.Properties}, convert to raw data name-value format and load into ldap modification set in preparation for ldap add.
+ *
+ * @param props contains {@link java.util.Properties} targeted for adding to ldap.
+ * @param attrs ldap attribute set containing name-value pairs in raw ldap format.
+ * @param attrName contains the name of the ldap attribute to be added.
+ */
+ protected void loadProperties( Properties props, LDAPAttributeSet attrs, String attrName, char separator )
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ LDAPAttribute attr = null;
+ for ( Enumeration e = props.propertyNames(); e.hasMoreElements(); )
+ {
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ String key = ( String ) e.nextElement();
+ String val = props.getProperty( key );
+ String prop = key + separator + val;
+ if ( attr == null )
+ {
+ attr = new LDAPAttribute( attrName, prop );
+ }
+ else
+ {
+ attr.addValue( prop );
+ }
+ }
+ if ( attr != null )
+ {
+ attrs.add( attr );
+ }
+ }
+ }
+
+
+ /**
+ * @param value
+ * @param validLen
+ * @return String containing encoded data.
+ * @throws LDAPException
+ */
+ protected String encodeSafeText( String value, int validLen )
+ throws LDAPException
+ {
+ if ( VUtil.isNotNullOrEmpty( value ) )
+ {
+ int length = value.length();
+ if ( length > validLen )
+ {
+ String error = "encodeSafeText value [" + value + "] invalid length [" + length + "]";
+ throw new LDAPException( error, LDAPException.PARAM_ERROR );
+ }
+ if ( GlobalIds.LDAP_FILTER_SIZE_FOUND )
+ {
+ value = VUtil.escapeLDAPSearchFilter( value );
+ }
+ }
+ return value;
+ }
+
+
+ /**
+ * Calls the PoolMgr to perform an LDAP bind for a user/password combination. This function is valid
+ * if and only if the user entity is a member of the USERS data set. The LDAP directory
+ * will return the OpenLDAP PW Policy control.
+ *
+ * @param ld connection to ldap server.
+ * @param userId contains the LDAP dn to the user entry.
+ * @param password contains the password in clear text.
+ * @return boolean value - true if bind successful, false otherwise.
+ * @throws LDAPException in the event of LDAP error.
+ */
+ protected boolean bind( LDAPConnection ld, String userId, char[] password )
+ throws LDAPException
+ {
+ counters.incrementBind();
+ return PoolMgr.bind( ld, userId, password );
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the Admin LDAP connection.
+ *
+ * @param ld handle to ldap connection object.
+ */
+ protected void closeAdminConnection( LDAPConnection ld )
+ {
+ PoolMgr.closeConnection( ld, PoolMgr.ConnType.ADMIN );
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the User LDAP connection.
+ *
+ * @param ld handle to ldap connection object.
+ */
+ protected void closeUserConnection( LDAPConnection ld )
+ {
+ PoolMgr.closeConnection( ld, PoolMgr.ConnType.USER );
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the Log LDAP connection.
+ *
+ * @param ld handle to ldap connection object.
+ */
+ protected void closeLogConnection( LDAPConnection ld )
+ {
+ PoolMgr.closeConnection( ld, PoolMgr.ConnType.LOG );
+ }
+
+
+ /**
+ * Calls the PoolMgr to get a User connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LDAPException
+ */
+ protected LDAPConnection getUserConnection() throws LDAPException
+ {
+ return PoolMgr.getConnection( PoolMgr.ConnType.USER );
+ }
+
+
+ /**
+ * Calls the PoolMgr to get an Admin connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LDAPException
+ */
+ protected LDAPConnection getAdminConnection() throws LDAPException
+ {
+ return PoolMgr.getConnection( PoolMgr.ConnType.ADMIN );
+ }
+
+
+ /**
+ * Calls the PoolMgr to get a Log connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LDAPException
+ */
+ protected LDAPConnection getLogConnection() throws LDAPException
+ {
+ return PoolMgr.getConnection( PoolMgr.ConnType.LOG );
+ }
+
+
+ /**
+ * Return to call reference to dao counter object with running totals for ldap operations add, mod, delete, search, etc.
+ *
+ * @return {@link LdapCounters} contains long values of atomic ldap operations for current running process.
+ */
+ public static LdapCounters getLdapCounters()
+ {
+ return counters;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/container/OrganizationalUnit.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/container/OrganizationalUnit.java b/src/main/java/org/apache/directory/fortress/core/ldap/container/OrganizationalUnit.java
new file mode 100755
index 0000000..d701b34
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/container/OrganizationalUnit.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.directory.fortress.core.ldap.container;
+
+
+/**
+ * This class contains the container node for the OpenLDAP Directory Information Tree. A container node is used to
+ * group other related nodes, i.e. 'ou=People' or 'ou'Roles'.
+ * <br />The organizational unit object class is 'organizationalUnit' <br />
+ * <p/>
+ * organizational unit structural object class is used to organize groups of nodes within the DIT.
+ * <ul>
+ * <li> ------------------------------------------
+ * <li> <code>#Standard object class from RFC2256</code>
+ * <li> <code>objectclass ( 2.5.6.5 NAME 'organizationalUnit'</code>
+ * <li> <code>DESC 'RFC2256: an organizational unit'</code>
+ * <li> <code>SUP top STRUCTURAL</code>
+ * <li> <code>MUST ou</code>
+ * <li> <code>MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $</code>
+ * <li> <code>x121Address $ registeredAddress $ destinationIndicator $</code>
+ * <li> <code>preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $</code>
+ * <li> <code>telephoneNumber $ internationaliSDNNumber $</code>
+ * <li> <code>facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $</code>
+ * <li> <code>postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )</code>
+ * <li> ------------------------------------------
+ * </ul>
+ * <p/>
+
+ *
+ * @author Shawn McKinney
+ */
+public class OrganizationalUnit
+{
+ private String name;
+ private String parent;
+ private String description;
+ private String contextId = "";
+
+
+ public String getContextId()
+ {
+ return this.contextId;
+ }
+
+
+ public void setContextId( String contextId )
+ {
+ this.contextId = contextId;
+ }
+
+
+ /**
+ * Generate instance of organizational unit object to be loaded as container node.
+ *
+ * @param name required attribute must be unique for rDn level and maps to 'ou' attribute in 'organizationalUnit' object class.
+ * @param description maps optional attribute maps to name in 'organizationalUnit' object class.
+ */
+ public OrganizationalUnit( String name, String description )
+ {
+ this.name = name;
+ this.description = description;
+ }
+
+
+ /**
+ * Default constructor generates instance of organizational unit object to be loaded as container node.
+ * The object cannot be used until 'name' value is set.
+ */
+ public OrganizationalUnit()
+ {
+ }
+
+
+ /**
+ * Get the required name attribute from the entity. This attribute must be unique for the level of tree it is
+ * set.
+ *
+ * @return required attribute must be unique for rDn level and maps to 'ou' attribute in 'organizationalUnit' object class.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+
+ /**
+ * Set the required name attribute in the entity. This attribute must be unique for the level of tree it is
+ * set.
+ *
+ * @param name is required attribute and must be unique for rDn level and maps to 'ou' attribute in 'organizationalUnit' object class.
+ */
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+
+ /**
+ * Get the description for the organizational unit object. This value is not required or constrained
+ * but is validated on reasonability.
+ *
+ * @return field maps to same name attribute on 'organizationalUnit'.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+
+ /**
+ * Set the description for the organizational unit object. This value is not required or constrained
+ * but is validated on reasonability.
+ *
+ * @param description field maps to same name attribute on 'organizationalUnit'.
+ */
+ public void setDescription( String description )
+ {
+ this.description = description;
+ }
+
+
+ /**
+ * Get the optional parent attribute allows nesting of container nodes two levels below suffix. For example, if parent
+ * node is created it may be used to subdivide collections of related nodes, dn=ou=Roles, ou=RBAC, dc=companyName, dc=com.
+ *
+ * @return attribute that contains name of parent node that is used to construct the dn.
+ */
+ public String getParent()
+ {
+ return parent;
+ }
+
+
+ /**
+ * Set the optional parent attribute allows nesting of container nodes two levels below suffix. For example, if parent
+ * node is created it may be used to subdivide collections of related nodes, dn=ou=Roles, ou=RBAC, dc=companyName, dc=com.
+ *
+ * @param parent attribute that contains name of parent node that is used to construct the dn. This maps to 'ou'
+ * attribute in parent node's 'organizationalUnit' object class.
+ */
+ public void setParent( String parent )
+ {
+ this.parent = parent;
+ }
+
+
+ /**
+ * @see Object#toString()
+ */
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "OrganizationalUnit[" );
+ sb.append( name ).append( ", " );
+ sb.append( description ).append( ", " );
+ sb.append( parent ).append( ", " );
+ sb.append( contextId ).append( ']' );
+
+ return sb.toString();
+ }
+}