| /* |
| * 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.ArrayList; |
| import java.util.List; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.pool.PoolableObjectFactory; |
| import org.apache.commons.pool.impl.GenericObjectPool; |
| 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.exception.LdapException; |
| 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.util.Config; |
| import org.apache.directory.fortress.core.util.EncryptUtil; |
| 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.ValidatingPoolableLdapConnectionFactory; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This utility manages the LDAP connection pools and provides methods for adding / removing connections from the three pools. |
| * <ul> |
| * <li>Admin Connections - bound with ldap service account creds</li> |
| * <li>User Connections - unbound used for authentication</li> |
| * <li>Audit Log Connections - bound with slapo access log service account creds (OpenLDAP only)</li> |
| * </ul> |
| * |
| * Each connection pool is initialized on first invocation of getInstance() which stores a reference to self used by subsequent callers. |
| * <p> |
| * This class is not thread safe. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public class LdapConnectionProvider |
| { |
| |
| private static final int DEFAULT_LDAP_TIMEOUT = 2000; |
| private static final String CLS_NM = LdapConnectionProvider.class.getName(); |
| private static final Logger LOG = LoggerFactory.getLogger( CLS_NM ); |
| |
| 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_LOG_POOL_MIN = "min.log.conn"; |
| private static final String LDAP_LOG_POOL_MAX = "max.log.conn"; |
| |
| private static final String ENABLE_LDAP_STARTTLS = "enable.ldap.starttls"; |
| private boolean IS_SSL; |
| |
| /** |
| * The Admin connection pool |
| */ |
| // private static LdapConnectionPool adminPool; |
| private static LdapHAConnectionPool adminPool; |
| |
| /** |
| * The Log connection pool |
| */ |
| private static LdapHAConnectionPool logPool; |
| |
| /** |
| * The User connection pool |
| */ |
| private static LdapHAConnectionPool userPool; |
| |
| private static volatile LdapConnectionProvider sINSTANCE = null; |
| |
| /** |
| * Synchronized getter guards access to reference to self which is a singleton and only be created the first time invoked. |
| * |
| * @return reference to self. |
| */ |
| public static LdapConnectionProvider getInstance() |
| { |
| if ( sINSTANCE == null ) |
| { |
| synchronized ( LdapConnectionProvider.class ) |
| { |
| if ( sINSTANCE == null ) |
| { |
| LOG.info( "construct a new ldap connection provider" ); |
| sINSTANCE = new LdapConnectionProvider(); |
| } |
| } |
| } |
| LOG.info( "return existing ldap connection provider" ); |
| return sINSTANCE; |
| } |
| |
| /** |
| * Private constructor calls the init method which initializes the connection pools. |
| * |
| */ |
| private LdapConnectionProvider() |
| { |
| init(); |
| } |
| |
| /** |
| * Initialize the three connection pools using settings and coordinates contained in the config. |
| */ |
| private void init() |
| { |
| IS_SSL = ( Config.getInstance().getProperty( GlobalIds.ENABLE_LDAP_SSL ) != null && |
| Config.getInstance().getProperty( GlobalIds.ENABLE_LDAP_SSL ).equalsIgnoreCase( "true" ) && |
| Config.getInstance().getProperty( GlobalIds.TRUST_STORE ) != null && |
| Config.getInstance().getProperty( GlobalIds.TRUST_STORE_PW, true ) != null ); |
| |
| String host = Config.getInstance().getProperty( GlobalIds.LDAP_HOST, "localhost" ); |
| int port = Config.getInstance().getInt( GlobalIds.LDAP_PORT, 389 ); |
| int min = Config.getInstance().getInt( GlobalIds.LDAP_ADMIN_POOL_MIN, 1 ); |
| int max = Config.getInstance().getInt( GlobalIds.LDAP_ADMIN_POOL_MAX, 10 ); |
| int logmin = Config.getInstance().getInt( LDAP_LOG_POOL_MIN, 1 ); |
| int logmax = Config.getInstance().getInt( LDAP_LOG_POOL_MAX, 10 ); |
| LOG.info( "LDAP POOL: host=[{}], port=[{}], min=[{}], max=[{}]", host, port, min, max ); |
| |
| LdapConnectionConfig[] ldapConnectionConfigs = populateLdapConnectionConfigs( host, port, IS_SSL, Config.getInstance().getProperty( GlobalIds.LDAP_ADMIN_POOL_UID, "" ), Config.getInstance().getProperty( GlobalIds |
| .LDAP_ADMIN_POOL_PW, true ), Config.getInstance().getBoolean( ENABLE_LDAP_STARTTLS, false ), Config.getInstance().getProperty( GlobalIds |
| .TRUST_STORE ), Config.getInstance().getProperty( GlobalIds.TRUST_STORE_PW ) ); |
| |
| adminPool = new LdapHAConnectionPool( ldapConnectionConfigs, HAConnectionStrategy.ROUND_ROBIN, true, GenericObjectPool.WHEN_EXHAUSTED_GROW, max, min, -1, 2 ); |
| userPool = new LdapHAConnectionPool( ldapConnectionConfigs, HAConnectionStrategy.ROUND_ROBIN, true, GenericObjectPool.WHEN_EXHAUSTED_GROW, max, min, -1, 2 ); |
| |
| // This pool of access log connections is used by {@link org.apache.directory.fortress.AuditMgr}. |
| // To enable, set {@code log.admin.user} && {@code log.admin.pw} inside fortress.properties file: |
| if ( StringUtils.isNotEmpty( LDAP_LOG_POOL_UID ) && StringUtils.isNotEmpty( LDAP_LOG_POOL_PW ) ) |
| { |
| // Initializing the log pool in static block requires static props set within fortress.properties. |
| // To make this dynamic requires moving this code outside of static block AND storing the connection |
| // metadata inside fortress config node (in ldap). |
| LdapConnectionConfig logConfigs[] = populateLdapConnectionConfigs( host, port, IS_SSL, |
| Config.getInstance().getProperty( LDAP_LOG_POOL_UID, "" ), |
| Config.getInstance().getProperty( LDAP_LOG_POOL_PW, true ), |
| Config.getInstance().getBoolean( ENABLE_LDAP_STARTTLS, false ), Config.getInstance().getProperty( GlobalIds.TRUST_STORE ), Config.getInstance().getProperty( GlobalIds.TRUST_STORE_PW, true ) ); |
| |
| |
| logPool = new LdapHAConnectionPool( logConfigs, HAConnectionStrategy.ROUND_ROBIN, true, GenericObjectPool.WHEN_EXHAUSTED_GROW, logmax, logmin, -1, 2 ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to close the Admin LDAP connection. |
| * |
| * @param connection handle to ldap connection object. |
| */ |
| public void closeAdminConnection(LdapConnection connection) |
| { |
| try |
| { |
| adminPool.releaseConnection( connection ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( e.getMessage(), e ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to close the Log LDAP connection. |
| * |
| * @param connection handle to ldap connection object. |
| */ |
| public void closeLogConnection(LdapConnection connection) |
| { |
| try |
| { |
| logPool.releaseConnection( connection ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( e.getMessage(), e ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to close the User LDAP connection. |
| * |
| * @param connection handle to ldap connection object. |
| */ |
| public void closeUserConnection(LdapConnection connection) |
| { |
| try |
| { |
| userPool.releaseConnection( connection ); |
| } |
| catch ( Exception e ) |
| { |
| throw new RuntimeException( e.getMessage(), e ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to get an Admin connection to the LDAP server. |
| * |
| * @return ldap connection. |
| * @throws LdapException If we had an issue getting an LDAP connection |
| */ |
| public LdapConnection getAdminConnection() throws LdapException |
| { |
| try |
| { |
| return adminPool.getConnection(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapException( e.getMessage(), e ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to get an Log connection to the LDAP server. |
| * |
| * @return ldap connection. |
| * @throws LdapException If we had an issue getting an LDAP connection |
| */ |
| public LdapConnection getLogConnection() throws LdapException |
| { |
| try |
| { |
| return logPool.getConnection(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapException( e.getMessage(), e ); |
| } |
| } |
| |
| |
| /** |
| * Calls the PoolMgr to get an User connection to the LDAP server. |
| * |
| * @return ldap connection. |
| * @throws LdapException If we had an issue getting an LDAP connection |
| */ |
| public LdapConnection getUserConnection() throws LdapException |
| { |
| try |
| { |
| return userPool.getConnection(); |
| } |
| catch ( Exception e ) |
| { |
| throw new LdapException( e.getMessage(), e ); |
| } |
| } |
| |
| /** |
| * Closes all the ldap connection pools. |
| */ |
| public static void closeAllConnectionPools() |
| { |
| try |
| { |
| LOG.trace( "Closing admin pool" ); |
| adminPool.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.warn( "Error closing admin pool: " + e.getMessage() ); |
| } |
| |
| try |
| { |
| LOG.trace( "Closing user pool" ); |
| userPool.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.warn( "Error closing user pool: " + e.getMessage() ); |
| } |
| |
| try |
| { |
| LOG.trace( "Closing log pool" ); |
| logPool.close(); |
| } |
| catch ( Exception e ) |
| { |
| LOG.warn( "Error closing log pool: " + e.getMessage() ); |
| } |
| } |
| |
| private LdapConnectionConfig[] populateLdapConnectionConfigs(String host, int port, boolean isSSL, String ldapAdminId, String ldapAdminPassword, boolean isEnableLdapStartTLS, String trustStore, String trustStorePassword) { |
| String[] hosts = null; |
| if(host.contains( "," )) { |
| hosts = host.split( "," ); |
| } else { |
| hosts = new String[1]; |
| hosts[0] = host; |
| } |
| LdapConnectionConfig[] configs = new LdapConnectionConfig[hosts.length]; |
| for(int i = 0; i < hosts.length ; i++) { |
| configs[i] = new LdapConnectionConfig(); |
| configs[i].setLdapHost( hosts[i] ); |
| configs[i].setLdapPort( port ); |
| configs[i].setName( ldapAdminId ); |
| //TODO: later need to include at the configuration level to adjust the value. |
| configs[i].setTimeout( DEFAULT_LDAP_TIMEOUT ); |
| configs[i].setUseSsl( isSSL ); |
| //config.setTrustManagers( new NoVerificationTrustManager() ); |
| |
| if ( isEnableLdapStartTLS ) |
| { |
| configs[i].setUseTls( true ); |
| } |
| |
| if ( isSSL && StringUtils.isNotEmpty( trustStore ) && |
| StringUtils.isNotEmpty( trustStorePassword ) ) |
| { |
| // validate certificates but allow self-signed certs if within this truststore: |
| configs[i].setTrustManagers( new LdapClientTrustStoreManager( trustStore, trustStorePassword.toCharArray(), null, |
| true ) ); |
| } |
| |
| String adminPw; |
| if ( EncryptUtil.isEnabled() ) |
| { |
| adminPw = EncryptUtil.getInstance().decrypt( ldapAdminPassword ); |
| } |
| else |
| { |
| adminPw = ldapAdminPassword; |
| } |
| |
| configs[i].setCredentials( adminPw ); |
| try |
| { |
| List<String> listExOps = new ArrayList<>(); |
| listExOps.add( "org.openldap.accelerator.impl.createSession.RbacCreateSessionFactory" ); |
| listExOps.add( "org.openldap.accelerator.impl.checkAccess.RbacCheckAccessFactory" ); |
| listExOps.add( "org.openldap.accelerator.impl.addRole.RbacAddRoleFactory" ); |
| listExOps.add( "org.openldap.accelerator.impl.dropRole.RbacDropRoleFactory" ); |
| listExOps.add( "org.openldap.accelerator.impl.deleteSession.RbacDeleteSessionFactory" ); |
| listExOps.add( "org.openldap.accelerator.impl.sessionRoles.RbacSessionRolesFactory" ); |
| LdapApiService ldapApiService = new StandaloneLdapApiService( new ArrayList<String>(), listExOps ); |
| |
| if ( !LdapApiServiceFactory.isInitialized() ) |
| { |
| LdapApiServiceFactory.initialize( ldapApiService ); |
| } |
| configs[i].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 ); |
| } |
| } |
| return configs; |
| } |
| } |