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:55 UTC
[36/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/ApacheDsDataProvider.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java b/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java
new file mode 100644
index 0000000..8a499be
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/ApacheDsDataProvider.java
@@ -0,0 +1,1384 @@
+/*
+ * 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.IOException;
+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.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.SearchCursor;
+import org.apache.directory.api.ldap.codec.api.LdapApiService;
+import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
+import org.apache.directory.api.ldap.codec.standalone.StandaloneLdapApiService;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Modification;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
+import org.apache.directory.api.ldap.model.entry.Value;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.exception.LdapOperationErrorException;
+import org.apache.directory.api.ldap.model.exception.LdapReferralException;
+import org.apache.directory.api.ldap.model.message.CompareRequest;
+import org.apache.directory.api.ldap.model.message.CompareRequestImpl;
+import org.apache.directory.api.ldap.model.message.CompareResponse;
+import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.SearchRequest;
+import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.apache.directory.ldap.client.api.LdapConnectionConfig;
+import org.apache.directory.ldap.client.api.LdapConnectionPool;
+import org.apache.directory.ldap.client.api.PoolableLdapConnectionFactory;
+
+import org.apache.directory.fortress.core.CfgRuntimeException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+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.crypto.EncryptUtil;
+import org.apache.directory.fortress.core.util.time.CUtil;
+import org.apache.directory.fortress.core.util.time.Constraint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * 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.apache.UserDAO}. {@link org.apache.directory.fortress.core.rbac.dao.apache
+ * .RoleDAO}, {@link org.apache.directory.fortress.core.rbac.dao.apache.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 ApacheDsDataProvider
+{
+ // Logging
+ private static final String CLS_NM = ApacheDsDataProvider.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+ private static final int MAX_DEPTH = 100;
+ private static final LdapCounters counters = new LdapCounters();
+ private static final String LDAP_HOST = "host";
+ private static final String LDAP_PORT = "port";
+ 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_ADMIN_POOL_UID = "admin.user";
+ private static final String LDAP_ADMIN_POOL_PW = "admin.pw";
+
+ // Used for TLS/SSL client-side configs:
+ private static final String ENABLE_LDAP_SSL = "enable.ldap.ssl";
+ private static final String ENABLE_LDAP_SSL_DEBUG = "enable.ldap.ssl.debug";
+ private static final String TRUST_STORE = Config.getProperty( "trust.store" );
+ private static final String TRUST_STORE_PW = Config.getProperty( "trust.store.password" );
+ private static final boolean IS_SSL = (
+ Config.getProperty( ENABLE_LDAP_SSL ) != null &&
+ Config.getProperty( ENABLE_LDAP_SSL ).equalsIgnoreCase( "true" ) &&
+ TRUST_STORE != null &&
+ TRUST_STORE_PW != null );
+
+ private static final String SET_TRUST_STORE_PROP = "trust.store.set.prop";
+ private static final boolean IS_SET_TRUST_STORE_PROP = (
+ IS_SSL &&
+ Config.getProperty( SET_TRUST_STORE_PROP ) != null &&
+ Config.getProperty( SET_TRUST_STORE_PROP ).equalsIgnoreCase( "true" ));
+
+ private static final boolean IS_SSL_DEBUG = ( ( Config.getProperty( ENABLE_LDAP_SSL_DEBUG ) != null ) && ( Config
+ .getProperty( ENABLE_LDAP_SSL_DEBUG ).equalsIgnoreCase( "true" ) ) );
+
+ /**
+ * The Admin connection pool
+ */
+ private static LdapConnectionPool adminPool;
+
+ /**
+ * The Log connection pool
+ */
+ private static LdapConnectionPool logPool;
+
+ /**
+ * The User connection pool
+ */
+ private static LdapConnectionPool userPool;
+
+ static
+ {
+ String host = Config.getProperty( LDAP_HOST, "localhost" );
+ int port = Config.getInt( LDAP_PORT, 10389 );
+ int min = Config.getInt( LDAP_ADMIN_POOL_MIN, 1 );
+ int max = Config.getInt( LDAP_ADMIN_POOL_MAX, 10 );
+
+ if(IS_SET_TRUST_STORE_PROP)
+ {
+ LOG.info( "Set JSSE truststore properties in Apache LDAP client:");
+ LOG.info( "javax.net.ssl.trustStore: " + TRUST_STORE );
+ LOG.info( "javax.net.debug: " + new Boolean( IS_SSL_DEBUG ).toString());
+ System.setProperty( "javax.net.ssl.trustStore", TRUST_STORE );
+ System.setProperty( "javax.net.ssl.trustStorePassword", TRUST_STORE_PW );
+ System.setProperty( "javax.net.debug", new Boolean( IS_SSL_DEBUG ).toString() );
+ }
+
+ LdapConnectionConfig config = new LdapConnectionConfig();
+ config.setLdapHost( host );
+ config.setLdapPort( port );
+ config.setName( Config.getProperty( LDAP_ADMIN_POOL_UID, "" ) );
+
+ // added by smckinney for TLS/SSL config:
+ config.setUseSsl( IS_SSL );
+ //config.setTrustManagers( new NoVerificationTrustManager() );
+
+ config.setTrustManagers( new LdapClientTrustStoreManager(
+ TRUST_STORE,
+ TRUST_STORE_PW.toCharArray() , null, true ) );
+
+ String adminPw = null;
+
+ if ( EncryptUtil.isEnabled() )
+ {
+ adminPw = EncryptUtil.decrypt( Config.getProperty( LDAP_ADMIN_POOL_PW ) );
+ }
+ else
+ {
+ adminPw = Config.getProperty( LDAP_ADMIN_POOL_PW );
+ }
+
+ config.setCredentials( adminPw );
+ try
+ {
+ System.setProperty( StandaloneLdapApiService.EXTENDED_OPERATIONS_LIST,
+ "org.openldap.accelerator.impl.createSession.RbacCreateSessionFactory,"
+ + "org.openldap.accelerator.impl.checkAccess.RbacCheckAccessFactory,"
+ + "org.openldap.accelerator.impl.addRole.RbacAddRoleFactory,"
+ + "org.openldap.accelerator.impl.dropRole.RbacDropRoleFactory,"
+ + "org.openldap.accelerator.impl.deleteSession.RbacDeleteSessionFactory,"
+ + "org.openldap.accelerator.impl.sessionRoles.RbacSessionRolesFactory"
+ );
+
+ LdapApiService ldapApiService = new StandaloneLdapApiService();
+ if ( LdapApiServiceFactory.isInitialized() == false )
+ {
+ LdapApiServiceFactory.initialize( ldapApiService );
+ }
+ config.setLdapApiService( ldapApiService );
+ }
+ catch ( Exception ex )
+ {
+ String error = "Exception caught initializing Admin Pool: " + ex;
+ throw new CfgRuntimeException( GlobalErrIds.FT_APACHE_LDAP_POOL_INIT_FAILED, error, ex );
+ }
+
+ PoolableLdapConnectionFactory factory = new PoolableLdapConnectionFactory( config );
+
+ // Create the Admin pool
+ adminPool = new LdapConnectionPool( factory );
+ adminPool.setTestOnBorrow( true );
+ adminPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+ adminPool.setMaxActive( max );
+ adminPool.setMinIdle( min );
+
+ // Create the Log pool
+ logPool = new LdapConnectionPool( factory );
+ logPool.setTestOnBorrow( true );
+ logPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+ logPool.setMaxActive( max );
+ logPool.setMinIdle( min );
+
+ // Create the User pool
+ userPool = new LdapConnectionPool( factory );
+ userPool.setTestOnBorrow( true );
+ userPool.setWhenExhaustedAction( GenericObjectPool.WHEN_EXHAUSTED_GROW );
+ userPool.setMaxActive( max );
+ userPool.setMinIdle( min );
+ }
+
+
+ /**
+ * 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 connection handle to ldap connection.
+ * @param dn contains ldap distinguished name.
+ * @param attrs array contains array names to pull back.
+ * @return ldap entry.
+ * @throws LdapException in the event system error occurs.
+ */
+ protected Entry read( LdapConnection connection, String dn, String[] attrs ) throws LdapException
+ {
+ counters.incrementRead();
+
+ return connection.lookup( dn, attrs );
+ }
+
+
+ /**
+ * Read the ldap record from specified location with user assertion.
+ *
+ * @param connection handle to ldap connection.
+ * @param dn contains ldap distinguished name.
+ * @param attrs 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 Entry read( LdapConnection connection, String dn, String[] attrs, String userDn ) throws LdapException
+ {
+ counters.incrementRead();
+
+ return connection.lookup( dn, attrs );
+ }
+
+
+ /**
+ * Add a new ldap entry to the directory. Do not add audit context.
+ *
+ * @param connection handle to ldap connection.
+ * @param entry contains data to add..
+ * @throws LdapException in the event system error occurs.
+ */
+ protected void add( LdapConnection connection, Entry entry ) throws LdapException
+ {
+ counters.incrementAdd();
+ connection.add( entry );
+ }
+
+
+ /**
+ * Add a new ldap entry to the directory. Add audit context.
+ *
+ * @param connection 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 connection, Entry entry, FortEntity entity ) throws LdapException
+ {
+ counters.incrementAdd();
+
+ if ( GlobalIds.IS_AUDIT && entity != null && entity.getAdminSession() != null )
+ {
+ if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+ {
+ entry.add( GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+ {
+ entry.add( GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+ {
+ entry.add( GlobalIds.FT_MODIFIER_ID, entity.getModId() );
+ }
+ }
+
+ connection.add( entry );
+ }
+
+
+ /**
+ * Update exiting ldap entry to the directory. Do not add audit context.
+ *
+ * @param connection 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 connection, String dn, List<Modification> mods ) throws LdapException
+ {
+ counters.incrementMod();
+ connection.modify( dn, mods.toArray( new Modification[]{} ) );
+ }
+
+
+ /**
+ * Update exiting ldap entry to the directory. Add audit context.
+ *
+ * @param connection 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 connection, String dn, List<Modification> mods,
+ FortEntity entity ) throws LdapException
+ {
+ counters.incrementMod();
+ audit( mods, entity );
+ connection.modify( dn, mods.toArray( new Modification[]{} ) );
+ }
+
+
+ /**
+ * Delete exiting ldap entry from the directory. Do not add audit context.
+ *
+ * @param connection 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 connection, String dn ) throws LdapException
+ {
+ counters.incrementDelete();
+ connection.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 connection 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 connection, String dn, FortEntity entity ) throws LdapException
+ {
+ counters.incrementDelete();
+ List<Modification> mods = new ArrayList<Modification>();
+ audit( mods, entity );
+
+ if ( mods.size() > 0 )
+ {
+ modify( connection, dn, mods );
+ }
+
+ connection.delete( dn );
+ }
+
+
+ /**
+ * Delete exiting ldap entry and all descendants from the directory. Do not add audit context.
+ *
+ * @param connection handle to ldap connection.
+ * @param dn contains distinguished node of entry targeted for removal..
+ * @throws LdapException in the event system error occurs.
+ * @throws IOException
+ * @throws CursorException
+ */
+ protected void deleteRecursive( LdapConnection connection, String dn ) throws LdapException, CursorException
+ {
+ int recursiveCount = 0;
+ deleteRecursive( dn, connection, 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 connection 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.
+ * @throws IOException
+ * @throws CursorException
+ */
+ protected void deleteRecursive( LdapConnection connection, String dn, FortEntity entity ) throws LdapException,
+ CursorException
+ {
+ List<Modification> mods = new ArrayList<Modification>();
+ audit( mods, entity );
+
+ if ( mods.size() > 0 )
+ {
+ modify( connection, dn, mods );
+ }
+
+ deleteRecursive( connection, 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 connection handle to ldap connection.
+ * @param recursiveCount keeps track of how many iterations have been performed.
+ * @throws LdapException in the event system error occurs.
+ * @throws IOException
+ * @throws CursorException
+ */
+ private void deleteRecursive( String dn, LdapConnection connection, int recursiveCount ) throws LdapException,
+ CursorException
+ {
+ 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 LdapOperationErrorException( error );
+ }
+
+ String theDN;
+
+ // Find child nodes
+ SearchCursor cursor = search( connection, dn, SearchScope.ONELEVEL, "(objectclass=*)", GlobalIds.NO_ATRS,
+ false, 0 );
+
+ // Iterate over all entries under this entry
+ while ( cursor.next() )
+ {
+ try
+ {
+ // Next directory entry
+ Entry entry = cursor.getEntry();
+ theDN = entry.getDn().getName();
+ // continue down:
+ deleteRecursive( theDN, connection, recursiveCount );
+ recursiveCount--;
+ }
+ catch ( LdapReferralException lre )
+ {
+ // cannot continue;
+ String error = "." + method + " dn [" + dn + "] caught LDAPReferralException=" + lre.getMessage() +
+ "=" + lre.getReferralInfo();
+ throw lre;
+ }
+ catch ( LdapException le )
+ {
+ // cannot continue;
+ String error = "." + method + " dn [" + dn + "] caught LdapException=" + le.getMessage();
+ throw new LdapException( error );
+ }
+ }
+
+ // delete the node:
+ counters.incrementDelete();
+ delete( connection, 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( List<Modification> mods, FortEntity entity )
+ {
+ if ( GlobalIds.IS_AUDIT && ( entity != null ) && ( entity.getAdminSession() != null ) )
+ {
+ if ( VUtil.isNotNullOrEmpty( entity.getAdminSession().getInternalUserId() ) )
+ {
+ Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+ GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
+ mods.add( modification );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModCode() ) )
+ {
+ Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+ GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
+ mods.add( modification );
+ }
+
+ if ( VUtil.isNotNullOrEmpty( entity.getModId() ) )
+ {
+ Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
+ GlobalIds.FT_MODIFIER_ID, entity.getModId() );
+ mods.add( modification );
+ }
+ }
+ }
+
+
+ /**
+ * Perform normal ldap search accepting default batch size.
+ *
+ * @param connection 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 attrs 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 SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+ String[] attrs, boolean attrsOnly ) throws LdapException
+ {
+ counters.incrementSearch();
+
+ SearchRequest searchRequest = new SearchRequestImpl();
+ searchRequest.setBase( new Dn( baseDn ) );
+ searchRequest.setScope( scope );
+ searchRequest.setFilter( filter );
+ searchRequest.setTypesOnly( attrsOnly );
+ searchRequest.addAttributes( attrs );
+ SearchCursor result = connection.search( searchRequest );
+
+ return result;
+ }
+
+
+ /**
+ * Perform normal ldap search specifying default batch size.
+ *
+ * @param connection 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 attrs is the requested list of attributes 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 SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+ String[] attrs, boolean attrsOnly, int batchSize ) throws LdapException
+ {
+ counters.incrementSearch();
+
+ SearchRequest searchRequest = new SearchRequestImpl();
+
+ searchRequest.setBase( new Dn( baseDn ) );
+ searchRequest.setFilter( filter );
+ searchRequest.setScope( scope );
+ searchRequest.setSizeLimit( batchSize );
+ searchRequest.setTypesOnly( attrsOnly );
+ searchRequest.addAttributes( attrs );
+
+ SearchCursor result = connection.search( searchRequest );
+
+ return result;
+ }
+
+
+ /**
+ * Perform normal ldap search specifying default batch size and max entries to return.
+ *
+ * @param connection 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 attrs 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 SearchCursor search( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+ String[] attrs, boolean attrsOnly, int batchSize, int maxEntries ) throws LdapException
+ {
+ counters.incrementSearch();
+
+ SearchRequest searchRequest = new SearchRequestImpl();
+
+ searchRequest.setBase( new Dn( baseDn ) );
+ searchRequest.setFilter( filter );
+ searchRequest.setScope( scope );
+ searchRequest.setSizeLimit( batchSize );
+ searchRequest.setTypesOnly( attrsOnly );
+ searchRequest.addAttributes( attrs );
+
+ SearchCursor result = connection.search( searchRequest );
+
+ 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 connection 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 attrs 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.
+ * @throws IOException
+ * @throws CursorException
+ */
+ protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+ String[] attrs, boolean attrsOnly ) throws LdapException, CursorException, IOException
+ {
+ SearchRequest searchRequest = new SearchRequestImpl();
+
+ searchRequest.setBase( new Dn( baseDn ) );
+ searchRequest.setFilter( filter );
+ searchRequest.setScope( scope );
+ searchRequest.setTypesOnly( attrsOnly );
+ searchRequest.addAttributes( attrs );
+
+ SearchCursor result = connection.search( searchRequest );
+
+ Entry entry = result.getEntry();
+
+ if ( result.next() )
+ {
+ throw new LdapException( "searchNode failed to return unique record for LDAP search of base DN [" +
+ baseDn + "] filter [" + filter + "]" );
+ }
+
+ return entry;
+ }
+
+
+ /**
+ * This search method uses OpenLDAP Proxy Authorization Control to assert arbitrary user identity onto connection.
+ *
+ * @param connection 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 attrs 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.
+ * @throws IOException
+ * @throws CursorException
+ */
+ protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
+ String[] attrs, boolean attrsOnly, String userDn ) throws LdapException, CursorException, IOException
+ {
+ counters.incrementSearch();
+
+ SearchRequest searchRequest = new SearchRequestImpl();
+
+ searchRequest.setBase( new Dn( baseDn ) );
+ searchRequest.setFilter( filter );
+ searchRequest.setScope( scope );
+ searchRequest.setTypesOnly( attrsOnly );
+ searchRequest.addAttributes( attrs );
+
+ SearchCursor result = connection.search( searchRequest );
+
+ Entry entry = result.getEntry();
+
+ if ( result.next() )
+ {
+ throw new LdapException( "searchNode failed to return unique record for LDAP search of base DN [" +
+ baseDn + "] filter [" + filter + "]" );
+ }
+
+ return entry;
+ }
+
+
+ /**
+ * This method uses the compare ldap func to assert audit record into the directory server's configured audit
+ * logger.
+ *
+ * @param connection 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 connection, String dn, String userDn,
+ Attribute attribute ) throws LdapException, UnsupportedEncodingException
+ {
+ counters.incrementCompare();
+
+ CompareRequest compareRequest = new CompareRequestImpl();
+ compareRequest.setName( new Dn( dn ) );
+ compareRequest.setAttributeId( attribute.getId() );
+ compareRequest.setAssertionValue( attribute.getString() );
+
+ CompareResponse response = connection.compare( compareRequest );
+
+ return response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS;
+ }
+
+
+ /**
+ * 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( Entry entry, String attributeName )
+ {
+ List<String> attrValues = new ArrayList<>();
+ if ( entry != null )
+ {
+ Attribute attr = entry.get( attributeName );
+ if ( attr != null )
+ {
+ for ( Value<?> value : attr )
+ {
+ attrValues.add( value.getString() );
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ return attrValues;
+ }
+
+
+ protected byte[] getPhoto( Entry entry, String attributeName ) throws LdapInvalidAttributeValueException
+ {
+ byte[] photo = null;
+ Attribute attr = entry.get( attributeName );
+
+ if ( attr != null )
+ {
+ photo = attr.getBytes();
+ }
+
+ 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( Entry entry, String attributeName )
+ {
+ // create Set with case insensitive comparator:
+ Set<String> attrValues = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
+
+ if ( entry != null && entry.containsAttribute( attributeName ) )
+ {
+ for ( Value<?> value : entry.get( attributeName ) )
+ {
+ attrValues.add( value.getString() );
+ }
+ }
+
+ 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 LdapInvalidAttributeValueException
+ *
+ * @throws LdapException in the event of ldap client error.
+ */
+ protected String getAttribute( Entry entry, String attributeName ) throws LdapInvalidAttributeValueException
+ {
+ if ( entry != null )
+ {
+ Attribute attr = entry.get( attributeName );
+
+ if ( attr != null )
+ {
+ return attr.getString();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+ /**
+ * 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 )
+ {
+ try
+ {
+ return new Dn( dn ).getRdn().getName();
+ }
+ catch ( LdapInvalidDnException lide )
+ {
+ return null;
+ }
+ }
+
+
+ /**
+ * 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 Attribute containing multi-occurring attribute set.
+ * @throws LdapException in the event of ldap client error.
+ */
+ protected Attribute createAttributes( String name, String values[] ) throws LdapException
+ {
+ Attribute attr = new DefaultAttribute( name, values );
+
+ 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 LdapInvalidAttributeValueException
+ *
+ * @throws LdapException in the event of ldap client error.
+ */
+ protected void unloadTemporal( Entry le, Constraint ftDateTime ) throws LdapInvalidAttributeValueException
+ {
+ 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 entry contains ldap attribute set targeted for adding.
+ * @param attrName name of ldap attribute being added.
+ */
+ protected void loadAttrs( List<String> list, Entry entry, String attrName ) throws LdapException
+ {
+ if ( list != null && list.size() > 0 )
+ {
+ entry.add( attrName, list.toArray( new String[]{} ) );
+ }
+ }
+
+
+ /**
+ * Given an 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.
+ */
+ protected void loadAttrs( List<String> list, List<Modification> mods, String attrName )
+ {
+ if ( ( list != null ) && ( list.size() > 0 ) )
+ {
+ mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName,
+ list.toArray( new String[]{} ) ) );
+ }
+ }
+
+
+ /**
+ * 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, List<Modification> mods, String attrName,
+ Hier.Op op )
+ {
+ if ( list != null )
+ {
+ Attribute attr;
+
+ for ( Relationship rel : list )
+ {
+ // This LDAP attr is stored as a name-value pair separated by a ':'.
+ attr = new DefaultAttribute( attrName, rel.getChild() + GlobalIds.PROP_SEP + rel.getParent() );
+
+ switch ( op )
+ {
+ case ADD:
+ mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attr ) );
+ break;
+
+ case MOD:
+ mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attr ) );
+ break;
+
+ case REM:
+ mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, 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, List<Modification> mods, String attrName )
+ {
+ if ( ( values != null ) && ( values.size() > 0 ) )
+ {
+ mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName,
+ values.toArray( new String[]{} ) ) );
+ }
+ }
+
+
+ /**
+ * 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 entry contains ldap entry to pull attrs from.
+ * @param attrName name of ldap attribute being added.
+ * @throws LdapException
+ */
+ protected void loadAttrs( Set<String> values, Entry entry, String attrName ) throws LdapException
+ {
+ if ( ( values != null ) && ( values.size() > 0 ) )
+ {
+ entry.add( attrName, values.toArray( new String[]{} ) );
+ }
+ }
+
+
+ /**
+ * 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 ModificationOperation#REPLACE_ATTRIBUTE} else {@link
+ * ModificationOperation#ADD_ATTRIBUTE}.
+ */
+ protected void loadProperties( Properties props, List<Modification> mods, String attrName, boolean replace )
+ {
+ loadProperties( props, mods, attrName, replace, GlobalIds.PROP_SEP );
+ }
+
+
+ /**
+ * 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 ModificationOperation#REPLACE_ATTRIBUTE} else {@link
+ * ModificationOperation#ADD_ATTRIBUTE}.
+ * @param separator contains the char value used to separate name and value in ldap raw format.
+ */
+ protected void loadProperties( Properties props, List<Modification> mods, String attrName, boolean replace, char separator )
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ if ( replace )
+ {
+ mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, attrName ) );
+ }
+
+ 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 ':'.
+ mods.add( new DefaultModification( ModificationOperation.ADD_ATTRIBUTE, attrName,
+ key + separator + val ) );
+ }
+ }
+ }
+
+
+ /**
+ * 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, List<Modification> mods, String attrName )
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ Attribute 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 ':'.
+ mods.add( new DefaultModification( ModificationOperation.REMOVE_ATTRIBUTE, attrName,
+ key + GlobalIds.PROP_SEP + val ) );
+ }
+ }
+ }
+
+
+ /**
+ * 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 entry contains ldap entry to pull attrs from.
+ * @param attrName contains the name of the ldap attribute to be added.
+ * @throws LdapException
+ */
+ protected void loadProperties( Properties props, Entry entry, String attrName ) throws LdapException
+ {
+ if ( ( props != null ) && ( props.size() > 0 ) )
+ {
+ Attribute attr = new DefaultAttribute( attrName );
+
+ 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 + GlobalIds.PROP_SEP + val;
+
+ attr.add( prop );
+ }
+
+ if ( attr.size() != 0 )
+ {
+ entry.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 add.
+ *
+ * @param props contains {@link java.util.Properties} targeted for adding to ldap.
+ * @param entry contains ldap entry to push attrs into.
+ * @param attrName contains the name of the ldap attribute to be added.
+ * @param separator contains the char value used to separate name and value in ldap raw format.
+ * @throws LdapException
+ */
+ protected void loadProperties( Properties props, Entry entry, String attrName, char separator ) throws LdapException
+ {
+ if ( props != null && props.size() > 0 )
+ {
+ Attribute 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 DefaultAttribute( attrName );
+ }
+ else
+ {
+ attr.add( prop );
+ }
+ }
+ if ( attr != null )
+ {
+ entry.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 );
+ }
+ 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 connection connection to ldap server.
+ * @param userDn 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 connection, String userDn, char[] password ) throws LdapException
+ {
+ counters.incrementBind();
+
+ connection.bind( userDn, new String( password ) );
+ return true;
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the Admin LDAP connection.
+ *
+ * @param connection handle to ldap connection object.
+ * @throws Exception
+ */
+ protected void closeAdminConnection( LdapConnection connection )
+ {
+ try
+ {
+ adminPool.releaseConnection( connection );
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the Log LDAP connection.
+ *
+ * @param connection handle to ldap connection object.
+ * @throws Exception
+ */
+ protected void closeLogConnection( LdapConnection connection )
+ {
+ try
+ {
+ logPool.releaseConnection( connection );
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * Calls the PoolMgr to close the User LDAP connection.
+ *
+ * @param connection handle to ldap connection object.
+ * @throws Exception
+ */
+ protected void closeUserConnection( LdapConnection connection )
+ {
+ try
+ {
+ userPool.releaseConnection( connection );
+ }
+ catch ( Exception e )
+ {
+ throw new RuntimeException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * Calls the PoolMgr to get an Admin connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LdapException
+ */
+ protected LdapConnection getAdminConnection() throws LdapException
+ {
+ try
+ {
+ return adminPool.getConnection();
+ }
+ catch ( Exception e )
+ {
+ throw new LdapException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * Calls the PoolMgr to get an Log connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LdapException
+ */
+ protected LdapConnection getLogConnection() throws LdapException
+ {
+ try
+ {
+ return logPool.getConnection();
+ }
+ catch ( Exception e )
+ {
+ throw new LdapException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * Calls the PoolMgr to get an User connection to the LDAP server.
+ *
+ * @return ldap connection.
+ * @throws LdapException
+ */
+ protected LdapConnection getUserConnection() throws LdapException
+ {
+ try
+ {
+ return userPool.getConnection();
+ }
+ catch ( Exception e )
+ {
+ throw new LdapException( e.getMessage() );
+ }
+ }
+
+
+ /**
+ * 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/ConnectionPool.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java b/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java
new file mode 100755
index 0000000..c6dc457
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/ConnectionPool.java
@@ -0,0 +1,664 @@
+/*
+ * 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.security.GeneralSecurityException;
+import java.util.Date;
+
+import com.unboundid.ldap.sdk.migrate.ldapjdk.JavaToLDAPSocketFactory;
+import com.unboundid.util.ssl.SSLUtil;
+import com.unboundid.util.ssl.TrustStoreTrustManager;
+import org.apache.directory.fortress.core.cfg.Config;
+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 javax.net.ssl.SSLSocketFactory;
+
+
+/**
+ * This connection pool class is used by Fortress {@link PoolMgr}.
+ * PoolMgr operations utilize multiple instances of this class to connections for different purposes.
+ * For example the 'admin' pool contains connections that have privileges to make modifications to the directory data during administrative operations {@link org.apache.directory.fortress.core.AdminMgr}.
+ * The 'user' pool contain unprivileged connections used for authentication processing only, {@link org.apache.directory.fortress.core.AccessMgr}.
+ * A 3rd pool, may be used to interrogate data stored by OpenLDAP's slapo access log info, This is used interrogating the fortress audit log events, {@link org.apache.directory.fortress.core.AuditMgr}.
+ * The contents of this file have been derived from the original, Mozilla Java LDAP SDK, and are subject to the Netscape Public License Version 1.1 (the "License")
+ * as described at the top of this file;
+ * The code mods include additional functionality to enable SSL connections in pool. There have been other updates to the original functions to integrate with UnboundID's Java LDAP SDK.
+ * </p>
+ * Original Mozilla javadoc:
+ * Class to maintain a pool of individual connections to the
+ * same server. Specify the initial size and the max size
+ * when constructing a pool. Call getConnection() to obtain
+ * a connection from the pool and close() to return it. If
+ * the pool is fully extended and there are no free connections,
+ * getConnection() blocks until a connection has been returned
+ * to the pool.<BR>
+ * Call destroy() to release all connections.
+ * <BR><BR>Example:<BR>
+ * <PRE>
+ * ConnectionPool pool = null;
+ * try {
+ * pool = new ConnectionPool( 10, 30,
+ * "foo.acme.com",389,
+ * "uid=me, o=acme.com",
+ * "password" );
+ * } catch ( LDAPException e ) {
+ * System.err.println( "Unable to create connection pool" );
+ * System.exit( 1 );
+ * }
+ * while ( clientsKnocking ) {
+ * String filter = getSearchFilter();
+ * LDAPConnection ld = pool.getConnection();
+ * try {
+ * LDAPSearchResults res = ld.search( BASE, ld.SCOPE_SUB,
+ * filter, attrs,
+ * false );
+ * pool.close( ld );
+ * while( res.hasMoreElements() ) {
+ * ...
+ * </PRE>
+ */
+class ConnectionPool
+{
+ // Logging
+ private static final String CLS_NM = ConnectionPool.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+
+ /**
+ * Create a new instance of connection pool with specified parameters. These connections will be used by the Fortress DAO
+ * methods for processing ldap server operations.
+ *
+ * @param min initial number of connections
+ * @param max maximum number of connections
+ * @param host hostname of LDAP server
+ * @param port port number of LDAP server
+ * @param authdn DN to authenticate as
+ * @param authpw password for authentication
+ * @throws LDAPException on failure to create connections
+ */
+ ConnectionPool( int min, int max,
+ String host, int port,
+ String authdn, String authpw )
+ throws LDAPException
+ {
+ this( min, max, host, port, authdn, authpw, null );
+ }
+
+
+ /*
+ * Constructor for using an existing connection to clone
+ * from
+ *
+ * @param min initial number of connections
+ * @param max maximum number of connections
+ * @param host hostname of LDAP server
+ * @param port port number of LDAP server
+ * @param authdn DN to authenticate as
+ * @param authpw password for authentication
+ * @param ldc connection to clone
+ * @exception LDAPException on failure to create connections
+ */
+ private ConnectionPool( int min, int max,
+ String host, int port,
+ String authdn, String authpw,
+ LDAPConnection ldc )
+ throws LDAPException
+ {
+ this.poolSize = min;
+ this.poolMax = max;
+ this.host = host;
+ this.port = port;
+ this.authdn = authdn;
+ this.authpw = authpw;
+ this.ldc = ldc;
+ this.debugMode = false;
+ createPool();
+ }
+
+
+ /**
+ * Destroy the whole pool - called during a shutdown
+ */
+ void destroy()
+ {
+ for ( int i = 0; i < pool.size(); i++ )
+ {
+ disconnect( ( LDAPConnectionObject ) pool.elementAt( i ) );
+ }
+ pool.removeAllElements();
+ }
+
+
+ /**
+ * Gets a connection from the pool
+ * <p/>
+ * If no connections are available, the pool will be
+ * extended if the number of connections is less than
+ * the maximum; if the pool cannot be extended, the method
+ * blocks until a free connection becomes available.
+ *
+ * @return an active connection.
+ */
+ LDAPConnection getConnection()
+ {
+ LDAPConnection con;
+
+ while ( ( con = getConnFromPool() ) == null )
+ {
+ synchronized ( pool )
+ {
+ try
+ {
+ pool.wait();
+ }
+ catch ( InterruptedException e )
+ {
+ LOG.warn( "getConnection caught InterruptedException" );
+ }
+ }
+ }
+ return con;
+ }
+
+
+ /**
+ * Gets a connection from the pool within a time limit.
+ * <p/>
+ * If no connections are available, the pool will be
+ * extended if the number of connections is less than
+ * the maximum; if the pool cannot be extended, the method
+ * blocks until a free connection becomes available or the
+ * time limit is exceeded.
+ *
+ * @param timeout timeout in milliseconds
+ * @return an active connection or <CODE>null</CODE> if timed out.
+ */
+ LDAPConnection getConnection( int timeout )
+ {
+ LDAPConnection con;
+
+ while ( ( con = getConnFromPool() ) == null )
+ {
+ long t1, t0 = System.currentTimeMillis();
+
+ if ( timeout <= 0 )
+ {
+ return con;
+ }
+
+ synchronized ( pool )
+ {
+ try
+ {
+ pool.wait( timeout );
+ }
+ catch ( InterruptedException e )
+ {
+ LOG.warn( "getConnection caught InterruptedException for timeout: " + timeout );
+ return null;
+ }
+ }
+
+ t1 = System.currentTimeMillis();
+ timeout -= ( t1 - t0 );
+ }
+ return con;
+ }
+
+
+ /**
+ * Gets a connection from the pool
+ * <p/>
+ * If no connections are available, the pool will be
+ * extended if the number of connections is less than
+ * the maximum; if the pool cannot be extended, the method
+ * returns null.
+ *
+ * @return an active connection or null.
+ */
+ synchronized LDAPConnection getConnFromPool()
+ {
+ LDAPConnection con = null;
+ LDAPConnectionObject ldapconnobj = null;
+
+ int pSize = pool.size();
+
+ // Get an available connection
+ for ( int i = 0; i < pSize; i++ )
+ {
+
+ // Get the ConnectionObject from the pool
+ LDAPConnectionObject co =
+ ( LDAPConnectionObject ) pool.elementAt( i );
+
+ if ( co.isAvailable() )
+ { // Conn available?
+ ldapconnobj = co;
+ break;
+ }
+ }
+
+ if ( ldapconnobj == null )
+ {
+ // If there there were no conns in pool, can we grow
+ // the pool?
+ if ( ( poolMax < 0 ) ||
+ ( ( poolMax > 0 ) &&
+ ( pSize < poolMax ) ) )
+ {
+
+ // Yes we can grow it
+ int i = addConnection();
+
+ // If a new connection was created, use it
+ if ( i >= 0 )
+ {
+ ldapconnobj =
+ ( LDAPConnectionObject ) pool.elementAt( i );
+ }
+ }
+ else
+ {
+ debug( "All pool connections in use" );
+ }
+ }
+
+ if ( ldapconnobj != null )
+ {
+ ldapconnobj.setInUse( true ); // Mark as in use
+ con = ldapconnobj.getLDAPConn();
+ }
+ return con;
+ }
+
+
+ /**
+ * This is our soft close - all we do is mark
+ * the connection as available for others to use.
+ * We also reset the auth credentials in case
+ * they were changed by the caller.
+ *
+ * @param ld a connection to return to the pool
+ */
+ synchronized void close( LDAPConnection ld )
+ {
+
+ int index = find( ld );
+ if ( index != -1 )
+ {
+ LDAPConnectionObject co =
+ ( LDAPConnectionObject ) pool.elementAt( index );
+ // Reset the auth if necessary
+ if ( ldc == null )
+ {
+ boolean reauth = false;
+ //if user bound anon then getAuthenticationDN is null
+ if ( ld.getAuthenticationDN() == null )
+ {
+ reauth = ( authdn != null );
+ }
+ else if ( !ld.getAuthenticationDN().equalsIgnoreCase( authdn ) )
+ {
+ reauth = true;
+ }
+ }
+ co.setInUse( false ); // Mark as available
+ synchronized ( pool )
+ {
+ pool.notifyAll();
+ }
+ }
+ }
+
+
+ /**
+ * Debug method to print the contents of the pool
+ */
+ public void printPool()
+ {
+ System.out.println( "--ConnectionPool--" );
+ for ( int i = 0; i < pool.size(); i++ )
+ {
+ LDAPConnectionObject co =
+ ( LDAPConnectionObject ) pool.elementAt( i );
+ String msg = "" + i + "=" + co;
+ LOG.info( "printPool: " + msg );
+ }
+ }
+
+
+ private void disconnect(
+ LDAPConnectionObject ldapconnObject )
+ {
+ if ( ldapconnObject != null )
+ {
+ if ( ldapconnObject.isAvailable() )
+ {
+ LDAPConnection ld = ldapconnObject.getLDAPConn();
+ if ( ( ld != null ) && ( ld.isConnected() ) )
+ {
+ try
+ {
+ ld.disconnect();
+ }
+ catch ( LDAPException e )
+ {
+ debug( "disconnect: " + e.toString() );
+ LOG.warn( "disconnect caught LDAPException: " + e.getMessage() );
+ }
+ }
+ ldapconnObject.setLDAPConn( null ); // Clear conn
+ }
+ }
+ }
+
+
+ private void createPool() throws LDAPException
+ {
+ // Called by the constructors
+ if ( poolSize <= 0 )
+ {
+ throw new LDAPException( "ConnectionPoolSize invalid" );
+ }
+ if ( poolMax < poolSize )
+ {
+ debug( "ConnectionPoolMax is invalid, set to " +
+ poolSize );
+ poolMax = poolSize;
+ }
+
+ debug( "****Initializing LDAP Pool****" );
+ debug( "LDAP host = " + host + " on port " + port );
+ debug( "Number of connections=" + poolSize );
+ debug( "Maximum number of connections=" + poolMax );
+ debug( "******" );
+
+ pool = new java.util.Vector(); // Create pool vector
+ setUpPool( poolSize ); // Initialize it
+ }
+
+
+ private int addConnection()
+ {
+ int index = -1;
+
+ debug( "adding a connection to pool..." );
+ try
+ {
+ int size = pool.size() + 1; // Add one connection
+ setUpPool( size );
+
+ if ( size == pool.size() )
+ {
+ // New size is size requested?
+ index = size - 1;
+ }
+ }
+ catch ( Exception ex )
+ {
+ debug( "Adding a connection: " + ex.toString() );
+ LOG.warn( "addConnection caught Exception: " + ex.getMessage() );
+ }
+ return index;
+ }
+
+
+ /**
+ * *** FORTRESS MOD ****
+ *
+ * Create pool of LDAP connections to server. Add SSL capability using unboundId's compatibility utility.
+ *
+ * @param size number of connections to generate and store in pool
+ * @throws LDAPException in the event of system error.
+ */
+ private synchronized void setUpPool( int size )
+ throws LDAPException
+ {
+ // Loop on creating connections
+ while ( pool.size() < size )
+ {
+ LDAPConnectionObject co =
+ new LDAPConnectionObject();
+
+ LDAPConnection newConn = createConnection( );
+ newConn.connect( host, port, authdn, authpw );
+ co.setLDAPConn( newConn );
+ co.setInUse( false ); // Mark not in use
+ pool.addElement( co );
+ }
+ }
+
+ /**
+ * Used to manage trust store properties. If enabled, create SSL connection.
+ *
+ */
+ private static final String ENABLE_LDAP_SSL = "enable.ldap.ssl";
+ private static final String ENABLE_LDAP_SSL_DEBUG = "enable.ldap.ssl.debug";
+ private static final String TRUST_STORE = Config.getProperty( "trust.store" );
+ private static final String TRUST_STORE_PW = Config.getProperty( "trust.store.password" );
+ private static final boolean IS_SSL = (
+ Config.getProperty( ENABLE_LDAP_SSL ) != null &&
+ Config.getProperty( ENABLE_LDAP_SSL ).equalsIgnoreCase( "true" ) &&
+ TRUST_STORE != null &&
+ TRUST_STORE_PW != null );
+
+ private static final String SET_TRUST_STORE_PROP = "trust.store.set.prop";
+ private static final boolean IS_SET_TRUST_STORE_PROP = (
+ IS_SSL &&
+ Config.getProperty( SET_TRUST_STORE_PROP ) != null &&
+ Config.getProperty( SET_TRUST_STORE_PROP ).equalsIgnoreCase( "true" ));
+
+ private static final boolean IS_SSL_DEBUG = ( ( Config.getProperty( ENABLE_LDAP_SSL_DEBUG ) != null ) && ( Config
+ .getProperty( ENABLE_LDAP_SSL_DEBUG ).equalsIgnoreCase( "true" ) ) );
+
+ static
+ {
+ if(IS_SET_TRUST_STORE_PROP)
+ {
+ LOG.info( "Set JSSE truststore properties:");
+ LOG.info( "javax.net.ssl.trustStore: " + TRUST_STORE );
+ LOG.info( "javax.net.debug: " + new Boolean( IS_SSL_DEBUG ).toString());
+ System.setProperty( "javax.net.ssl.trustStore", TRUST_STORE );
+ System.setProperty( "javax.net.ssl.trustStorePassword", TRUST_STORE_PW );
+ System.setProperty( "javax.net.debug", new Boolean( IS_SSL_DEBUG ).toString() );
+ }
+ }
+
+ /**
+ * *** FORTRESS MOD ****
+ *
+ * If enabled, use Unbound compatibility lib to create SSL connection.
+ *
+ * @return handle to LDAPConnection
+ * @throws LDAPException wrap GeneralSecurityException or throws ldapexcep.
+ */
+ private LDAPConnection createConnection() throws LDAPException
+ {
+ LDAPConnection newConn = null;
+ if( IS_SSL)
+ {
+ // Generate SSL Connection using Unbound compatibility lib utils:
+ // http://stackoverflow.com/questions/22672477/unboundid-ldap-jdk-migration
+ SSLSocketFactory sslSocketFactory;
+ //SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
+ // These config values set in fortress.properties
+ SSLUtil sslUtil = new SSLUtil(
+ new TrustStoreTrustManager(
+ TRUST_STORE,
+ TRUST_STORE_PW.toCharArray() , null, true ) );
+ try
+ {
+ sslSocketFactory = sslUtil.createSSLSocketFactory();
+ }
+ catch(GeneralSecurityException e)
+ {
+ String error = "GeneralSecurityException while creating SSL socket factory=" + e;
+ throw new LDAPException( error, LDAPException.CONNECT_ERROR );
+ }
+ JavaToLDAPSocketFactory ldapSocketFactory =
+ new JavaToLDAPSocketFactory(sslSocketFactory);
+ newConn = new LDAPConnection(ldapSocketFactory);
+ }
+ else
+ {
+ // Make LDAP connection, using template if available
+ newConn = new LDAPConnection();
+ }
+ return newConn;
+ }
+
+ private int find( LDAPConnection con )
+ {
+ // Find the matching Connection in the pool
+ if ( con != null )
+ {
+ for ( int i = 0; i < pool.size(); i++ )
+ {
+ LDAPConnectionObject co =
+ ( LDAPConnectionObject ) pool.elementAt( i );
+ if ( co.getLDAPConn() == con )
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * Sets the debug printout mode.
+ *
+ * @param mode debug mode to use
+ */
+ public synchronized void setDebug( boolean mode )
+ {
+ debugMode = mode;
+ }
+
+
+ /**
+ * Reports the debug printout mode.
+ *
+ * @return debug mode in use.
+ */
+ public boolean getDebug()
+ {
+ return debugMode;
+ }
+
+
+ private void debug( String s )
+ {
+ if ( debugMode )
+ System.out.println( "ConnectionPool (" +
+ new Date() + ") : " + s );
+ }
+
+
+ private void debug( String s, boolean severe )
+ {
+ if ( debugMode || severe )
+ {
+ System.out.println( "ConnectionPool (" +
+ new Date() + ") : " + s );
+ }
+ }
+
+ /**
+ * Wrapper for LDAPConnection object in pool
+ */
+ class LDAPConnectionObject
+ {
+
+ /**
+ * Returns the associated LDAPConnection.
+ *
+ * @return the LDAPConnection.
+ */
+ LDAPConnection getLDAPConn()
+ {
+ return this.ld;
+ }
+
+
+ /**
+ * Sets the associated LDAPConnection
+ *
+ * @param ld the LDAPConnection
+ */
+ void setLDAPConn( LDAPConnection ld )
+ {
+ this.ld = ld;
+ }
+
+
+ /**
+ * Marks a connection in use or available
+ *
+ * @param inUse <code>true</code> to mark in use, <code>false</code> if available
+ */
+ void setInUse( boolean inUse )
+ {
+ this.inUse = inUse;
+ }
+
+
+ /**
+ * Returns whether the connection is available
+ * for use by another user.
+ *
+ * @return <code>true</code> if available.
+ */
+ boolean isAvailable()
+ {
+ return !inUse;
+ }
+
+
+ /**
+ * Debug method
+ *
+ * @return s user-friendly rendering of the object.
+ */
+ public String toString()
+ {
+ return "LDAPConnection=" + ld + ",inUse=" + inUse;
+ }
+
+ private LDAPConnection ld; // LDAP Connection
+ private boolean inUse; // In use? (true = yes)
+ }
+
+ private final int poolSize; // Min pool size
+ private int poolMax; // Max pool size
+ private final String host; // LDAP host
+ private final int port; // Port to connect at
+ private final String authdn; // Identity of connections
+ private final String authpw; // Password for authdn
+ private LDAPConnection ldc = null; // Connection to clone
+ private java.util.Vector pool; // the actual pool
+ private boolean debugMode;
+}
http://git-wip-us.apache.org/repos/asf/directory-fortress-core/blob/687ee1ad/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java b/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
new file mode 100644
index 0000000..6819150
--- /dev/null
+++ b/src/main/java/org/apache/directory/fortress/core/ldap/LdapClientTrustStoreManager.java
@@ -0,0 +1,251 @@
+/*
+ * 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.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.directory.fortress.core.CfgRuntimeException;
+import org.apache.directory.fortress.core.GlobalErrIds;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implement the X509TrustManager interface which will be used during JSSE truststore manager initialization for LDAP
+ * client-to-server communications over TLS/SSL.
+ * It is used during certificate validation operations within JSSE.
+ * <p/>
+ * Note: This class allows self-signed certificates to pass the validation checks.
+ *
+ * @author Shawn McKinney
+ */
+public final class LdapClientTrustStoreManager implements X509TrustManager, Serializable
+{
+ // Logging
+ private static final String CLS_NM = LdapClientTrustStoreManager.class.getName();
+ private static final Logger LOG = LoggerFactory.getLogger( CLS_NM );
+
+ // Config variables
+ private final boolean isExamineValidityDates;
+ private final char[] trustStorePw;
+ private final String trustStoreFile;
+ private final String trustStoreFormat;
+
+ /**
+ * Constructor used by connection configuration utility to load trust store manager.
+ *
+ * @param trustStoreFile contains fully qualified name of trust store file.
+ * @param trustStorePw contains the password for trust store
+ * @param trustStoreFormat contains the format for trust store
+ * @param isExamineValidity boolean var determines if certificate will be examined for valid dates on load.
+ */
+ public LdapClientTrustStoreManager( final String trustStoreFile, final char[] trustStorePw,
+ final String trustStoreFormat, final boolean isExamineValidity )
+ {
+ if ( trustStoreFile == null )
+ {
+ // Cannot continue, throw an unchecked exception:
+ throw new CfgRuntimeException( GlobalErrIds.FT_CONFIG_JSSE_TRUSTSTORE_NULL,
+ "FortressTrustStoreManager constructor : input file name is null" );
+ }
+ // contains the fully-qualified file name of a valid JSSE TrustStore on local file system:
+ this.trustStoreFile = trustStoreFile;
+ // the password to the JSSE TrustStore:
+ this.trustStorePw = trustStorePw;
+ // If true, verify the current date is within the validity period for every certificate in the TrustStore:
+ this.isExamineValidityDates = isExamineValidity;
+ if ( trustStoreFormat == null )
+ {
+ this.trustStoreFormat = KeyStore.getDefaultType();
+ }
+ else
+ {
+ this.trustStoreFormat = trustStoreFormat;
+ }
+ }
+
+ /**
+ * Determine if client certificate is to be trusted.
+ *
+ * @param x509Chain
+ * @param authNType
+ * @throws CertificateException
+ */
+ public synchronized void checkClientTrusted( final X509Certificate[] x509Chain,
+ final String authNType ) throws CertificateException
+ {
+ // For each certificate in the chain, check validity:
+ for ( final X509TrustManager trustMgr : getTrustManagers( x509Chain ) )
+ {
+ trustMgr.checkClientTrusted( x509Chain, authNType );
+ }
+ }
+
+ /**
+ * Determine if server certificate is to be trusted.
+ *
+ * @param x509Chain
+ * @param authNType
+ * @throws CertificateException
+ */
+ public synchronized void checkServerTrusted( final X509Certificate[] x509Chain, final String authNType ) throws
+ CertificateException
+ {
+ for ( final X509TrustManager trustManager : getTrustManagers( x509Chain ) )
+ {
+ trustManager.checkServerTrusted( x509Chain, authNType );
+ }
+ }
+
+ /**
+ * Return the list of accepted issuers for this trust manager.
+ *
+ * @return array of accepted issuers
+ */
+ public synchronized X509Certificate[] getAcceptedIssuers()
+ {
+ return new X509Certificate[0];
+ }
+
+ /**
+ * Return array of trust managers to caller. Will verify that current date is within certs validity period.
+ *
+ * @param x509Chain contains input X.509 certificate chain.
+ * @return array of X.509 trust managers.
+ * @throws CertificateException if trustStoreFile instance variable is null.
+ */
+ private synchronized X509TrustManager[] getTrustManagers( final X509Certificate[] x509Chain ) throws
+ CertificateException
+ {
+ // If true, verify the current date is within each certificates validity period.
+ if ( isExamineValidityDates )
+ {
+ final Date currentDate = new Date();
+ for ( final X509Certificate x509Cert : x509Chain )
+ {
+ x509Cert.checkValidity( currentDate );
+ }
+ }
+ // The trustStoreFile should contain the fully-qualified name of a Java TrustStore on local file system.
+ final File trustStoreFile = new File( this.trustStoreFile );
+ if ( !trustStoreFile.exists() )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.getTrustManagers : file not found" );
+ }
+ return loadTrustManagers( getTrustStore() );
+ }
+
+ /**
+ * Return an array of X.509 TrustManagers.
+ *
+ * @param trustStore handle to input trustStore
+ * @return array of trust managers
+ * @throws CertificateException if problem occurs during TrustManager initialization.
+ */
+ private X509TrustManager[] loadTrustManagers( final KeyStore trustStore ) throws CertificateException
+ {
+ final X509TrustManager[] x509TrustManagers;
+ try
+ {
+ final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory
+ .getDefaultAlgorithm() );
+ trustManagerFactory.init( trustStore );
+ final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ x509TrustManagers = new X509TrustManager[trustManagers.length];
+ for ( int i = 0; i < trustManagers.length; i++ )
+ {
+ x509TrustManagers[i] = ( X509TrustManager ) trustManagers[i];
+ }
+ }
+ catch ( NoSuchAlgorithmException e )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.loadTrustManagers caught " +
+ "NoSuchAlgorithmException", e );
+ }
+ catch ( KeyStoreException e )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.loadTrustManagers caught KeyStoreException", e );
+ }
+ return x509TrustManagers;
+ }
+
+ /**
+ * Load the TrustStore file into JSSE KeyStore instance.
+ *
+ * @return instance of JSSE KeyStore containing the LDAP Client's TrustStore file info. *
+ * @throws CertificateException if cannot process file load.
+ */
+ private KeyStore getTrustStore() throws CertificateException
+ {
+ final KeyStore trustStore;
+ try
+ {
+ trustStore = KeyStore.getInstance( trustStoreFormat );
+ }
+ catch ( KeyStoreException e )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught KeyStoreException", e );
+ }
+ FileInputStream trustStoreInputStream = null;
+ try
+ {
+ trustStoreInputStream = new FileInputStream( trustStoreFile );
+ trustStore.load( trustStoreInputStream, trustStorePw );
+ }
+ catch ( NoSuchAlgorithmException e )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught " +
+ "NoSuchAlgorithmException", e );
+ }
+ catch ( IOException e )
+ {
+ throw new CertificateException( "FortressTrustStoreManager.getTrustManagers caught KeyStoreException", e );
+ }
+ finally
+ {
+ // Close the input stream.
+ if ( trustStoreInputStream != null )
+ {
+ try
+ {
+ trustStoreInputStream.close();
+ }
+ catch ( IOException e )
+ {
+ // Eat this ioexception because it shouldn't be a problem, but log just in case:
+ LOG.warn( "FortressTrustStoreManager.getTrustManagers finally block on input stream close " +
+ "operation caught IOException=" + e.getMessage() );
+ }
+ }
+ }
+ return trustStore;
+ }
+}