blob: 270a37af296675d671e29ea6339eaf505ce5aae7 [file] [log] [blame]
/*
* 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.lang.StringUtils;
import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicy;
import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyImpl;
import org.apache.directory.api.ldap.extras.controls.ppolicy_impl.PasswordPolicyDecorator;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.SearchCursor;
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.message.BindRequest;
import org.apache.directory.api.ldap.model.message.BindRequestImpl;
import org.apache.directory.api.ldap.model.message.BindResponse;
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.Control;
import org.apache.directory.api.ldap.model.message.Response;
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.message.controls.ProxiedAuthz;
import org.apache.directory.api.ldap.model.message.controls.ProxiedAuthzImpl;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.fortress.core.GlobalIds;
import org.apache.directory.fortress.core.model.Constraint;
import org.apache.directory.fortress.core.model.ConstraintUtil;
import org.apache.directory.fortress.core.model.FortEntity;
import org.apache.directory.fortress.core.model.Hier;
import org.apache.directory.fortress.core.model.Relationship;
import org.apache.directory.fortress.core.util.Config;
import org.apache.directory.fortress.core.util.LdapUtil;
import org.apache.directory.ldap.client.api.LdapConnection;
/**
* 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.impl.UserDAO}. {@link org.apache.directory.fortress.core.impl.RoleDAO},
* {@link org.apache.directory.fortress.core.impl.PermDAO}, ....
* These are low-level data utilities, very little if any data validations are performed here.
* <p>
* This class is thread safe.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public abstract class LdapDataProvider
{
// Logging
private static final String CLS_NM = LdapDataProvider.class.getName();
private static final int MAX_DEPTH = 100;
private static final LdapCounters COUNTERS = new LdapCounters();
private static final PasswordPolicy PP_REQ_CTRL = new PasswordPolicyImpl();
/**
* 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 static String getRootDn( String contextId, String root )
{
String szDn = Config.getInstance().getProperty( root );
// The contextId must not be null, or "HOME" or "null"
if ( StringUtils.isNotEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) && !contextId
.equals( GlobalIds.HOME ) )
{
int idx = szDn.indexOf( Config.getInstance().getProperty( GlobalIds.SUFFIX ) );
if ( idx > 0 )
{
// Found. The DN is ,ou=<contextId>,
StringBuilder dn = new StringBuilder();
dn.append( szDn.substring( 0, idx - 1 ) ).append( "," ).append( SchemaConstants.OU_AT ).append( "=" )
.append(
contextId ).append( "," ).append( szDn.substring( idx ) );
return dn.toString();
}
else
{
return "";
}
}
else
{
return szDn;
}
}
/**
* 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 ( StringUtils.isNotEmpty( contextId ) && !contextId.equalsIgnoreCase( GlobalIds.NULL ) && !contextId
.equals( GlobalIds.HOME ) )
{
dn.append( SchemaConstants.OU_AT ).append( "=" ).append( contextId ).append( "," +
"" ).append( Config.getInstance().getProperty( GlobalIds.SUFFIX ) );
}
else
{
dn.append( Config.getInstance().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.
*
* @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, Dn 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 ( !Config.getInstance().isAuditDisabled() && ( entity != null ) && ( entity.getAdminSession() != null ) )
{
if ( StringUtils.isNotEmpty( entity.getAdminSession().getInternalUserId() ) )
{
entry.add( GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
}
if ( StringUtils.isNotEmpty( entity.getModCode() ) )
{
entry.add( GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
}
if ( StringUtils.isNotEmpty( 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. 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, Dn 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[]
{} ) );
}
/**
* 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, Dn 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 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, Dn 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 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 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=*)",
SchemaConstants.NO_ATTRIBUTE_ARRAY,
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 ( 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.
*/
private void audit( List<Modification> mods, FortEntity entity )
{
if ( !Config.getInstance().isAuditDisabled() && ( entity != null ) && ( entity.getAdminSession() != null ) )
{
if ( StringUtils.isNotEmpty( entity.getAdminSession().getInternalUserId() ) )
{
Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
GlobalIds.FT_MODIFIER, entity.getAdminSession().getInternalUserId() );
mods.add( modification );
}
if ( StringUtils.isNotEmpty( entity.getModCode() ) )
{
Modification modification = new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE,
GlobalIds.FT_MODIFIER_CODE, entity.getModCode() );
mods.add( modification );
}
if ( StringUtils.isNotEmpty( 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 );
return connection.search( searchRequest );
}
/**
* 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 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 maxEntries ) throws LdapException
{
COUNTERS.incrementSearch();
SearchRequest searchRequest = new SearchRequestImpl();
searchRequest.setBase( new Dn( baseDn ) );
searchRequest.setFilter( filter );
searchRequest.setScope( scope );
searchRequest.setSizeLimit( maxEntries );
searchRequest.setTypesOnly( attrsOnly );
searchRequest.addAttributes( attrs );
return connection.search( searchRequest );
}
/**
* 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 CursorException If we weren't able to fetch an element from the search result
*/
protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
String[] attrs, boolean attrsOnly ) throws LdapException, CursorException
{
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 CursorException If we weren't able to fetch an element from the search result
*/
protected Entry searchNode( LdapConnection connection, String baseDn, SearchScope scope, String filter,
String[] attrs, boolean attrsOnly, String userDn ) throws LdapException, CursorException
{
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.
*
* This is for one reason - to force the ldap server to maintain an audit trail on checkAccess api.
*
* Use proxy authz control (RFC4370) to assert the caller's id onto the record.
*
* @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() );
// Assert the end user's dn onto the reqest using proxy authZ control so openldap can log who the user was (for authZ audit trail)
ProxiedAuthz proxiedAuthzControl = new ProxiedAuthzImpl();
proxiedAuthzControl.setAuthzId( "dn: " + userDn );
compareRequest.addControl( proxiedAuthzControl );
CompareResponse response = connection.compare( compareRequest );
return response.getLdapResult().getResultCode() == ResultCodeEnum.SUCCESS;
}
/**
* Method wraps ldap client to return multivalued attribute 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.
*/
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;
}
/**
* Return the image stored on the entry.
*
* @param entry contains the image target.
* @param attributeName to be retrieved.
* @return byte array containing image.
* @throws LdapInvalidAttributeValueException contains the system error.
*/
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 multivalued attribute 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.
*/
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 When we weren't able to get the attribute from the entry
*/
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.
*/
protected String getRdn( String dn )
{
try
{
return new Dn( dn ).getRdn().getName();
}
catch ( LdapInvalidDnException lide )
{
return null;
}
}
protected String getRdnValue( String dn )
{
try
{
return new Dn( dn ).getRdn().getNormValue();
}
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 multivalued attribute set.
* @throws LdapException in the event of ldap client error.
*/
protected Attribute createAttributes( String name, String values[] ) throws LdapException
{
return new DefaultAttribute( name, values );
}
/**
* Convert constraint from raw ldap format to application entity.
*
* @param le ldap entry containing constraint.
* @param ftDateTime reference to {@link org.apache.directory.fortress.core.model.Constraint} containing formatted data.
* @throws LdapInvalidAttributeValueException
*
* @throws LdapInvalidAttributeValueException when we weren't able to retrieve the attribute from the entry
*/
protected void unloadTemporal( Entry le, Constraint ftDateTime ) throws LdapInvalidAttributeValueException
{
String szRawData = getAttribute( le, GlobalIds.CONSTRAINT );
if ( szRawData != null && szRawData.length() > 0 )
{
ConstraintUtil.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.
* @throws LdapException If we weren't able to add the attributes into the entry
*/
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.model.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.model.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 org.apache.directory.fortress.core.model.Hier.Op#ADD},
* {@link org.apache.directory.fortress.core.model.Hier.Op#MOD}, {@link org.apache.directory.fortress.core.model.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 If we weren't able to add the values into the entry
*/
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 )
{
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 If we weren't able to add the properies into the entry
*/
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 If we weren't able to add the properies into the entry
*/
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 );
}
}
}
/**
* Encode some text so that it can be used in a LDAP filter.
*
* @param value The value to encode
* @param validLen The maximum accepted length of the value.
* @return String containing encoded data.
* @throws LdapException If the value is longer than the maximum value
*/
protected String encodeSafeText( String value, int validLen ) throws LdapException
{
if ( StringUtils.isNotEmpty( value ) )
{
int length = value.length();
if ( length > validLen )
{
String error = "encodeSafeText value [" + value + "] invalid length [" + length + "]";
throw new LdapException( error );
}
if ( LdapUtil.getInstance().isLdapfilterSizeFound() )
{
value = escapeLDAPSearchFilter( value );
}
}
return value;
}
/**
* Get Password Policy Response Control from LDAP client.
*
* @param resp contains reference to LDAP pw policy response.
* @return PasswordPolicy response control.
*/
protected PasswordPolicy getPwdRespCtrl( Response resp )
{
Control control = resp.getControls().get( PP_REQ_CTRL.getOid() );
if ( control == null )
{
return null;
}
return ( ( PasswordPolicyDecorator ) control ).getDecorated();
}
/**
* 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.
*
* @param connection connection to ldap server.
* @param szUserDn contains the LDAP dn to the user entry in String format.
* @param password contains the password in clear text.
* @return bindResponse contains the result of the operation.
* @throws LdapException in the event of LDAP error.
*/
protected BindResponse bind( LdapConnection connection, String szUserDn, String password ) throws LdapException
{
COUNTERS.incrementBind();
Dn userDn = new Dn( szUserDn );
BindRequest bindReq = new BindRequestImpl();
bindReq.setDn( userDn );
bindReq.setCredentials( password );
bindReq.addControl( PP_REQ_CTRL );
return connection.bind( bindReq );
}
/**
* Calls the PoolMgr to close the Admin LDAP connection.
*
* @param connection handle to ldap connection object.
*/
public void closeAdminConnection( LdapConnection connection )
{
LdapConnectionProvider.getInstance().closeAdminConnection(connection);
}
/**
* Calls the PoolMgr to close the Log LDAP connection.
*
* @param connection handle to ldap connection object.
*/
protected void closeLogConnection( LdapConnection connection )
{
LdapConnectionProvider.getInstance().closeLogConnection(connection);
}
/**
* Calls the PoolMgr to close the User LDAP connection.
*
* @param connection handle to ldap connection object.
*/
protected void closeUserConnection( LdapConnection connection )
{
LdapConnectionProvider.getInstance().closeUserConnection(connection);
}
/**
* 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
{
return LdapConnectionProvider.getInstance().getAdminConnection();
}
/**
* 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
*/
protected LdapConnection getLogConnection() throws LdapException
{
return LdapConnectionProvider.getInstance().getLogConnection();
}
/**
* 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
*/
protected LdapConnection getUserConnection() throws LdapException
{
return LdapConnectionProvider.getInstance().getUserConnection();
}
/**
* 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;
}
/**
* Perform encoding on supplied input string for certain unsafe ascii characters. These chars may be unsafe
* because ldap reserves some characters as operands. Safe encoding safeguards from malicious scripting input errors
* that are possible if data filtering did not get performed before being passed into dao layer.
*
* @param filter contains the data to filter.
* @return possibly modified input string for matched characters.
*/
protected String escapeLDAPSearchFilter( String filter )
{
StringBuilder sb = new StringBuilder();
int filterLen = filter.length();
for ( int i = 0; i < filterLen; i++ )
{
boolean found = false;
char curChar = filter.charAt( i );
int j = 0;
for ( ; j < GlobalIds.LDAP_FILTER_SIZE; j++ )
{
if ( LdapUtil.getInstance().getLdapMetaChars()[j] > curChar )
{
break;
}
else if ( curChar == LdapUtil.getInstance().getLdapMetaChars()[j] )
{
sb.append( "\\" );
sb.append( LdapUtil.getInstance().getLdapReplVals()[j] );
found = true;
break;
}
}
if ( !found )
{
sb.append( curChar );
}
}
return sb.toString();
}
/**
* Closes all the ldap connection pools.
*/
public static void closeAllConnectionPools(){
LdapConnectionProvider.getInstance().closeAllConnectionPools();
}
}